I'm writing an Angular2 app. Consider a class Cat which I instantiate many instances of, which has an API method to write itself out:
class Cat {
constructor(private name: string) { }
public write() {
Api.write(this); // <== how do I do this?
}
}
The Cat#write method needs to access an API service wrapped around Http:
#Injectable()
class Api {
constructor(private http: Http) { }
write(data) {
this.http.post(...);
}
}
The (perhaps trivially simple) problem I have is how to access API#write from within my Cat class. Since I need the Cat constructor to pass in the name, I can't use it for injection. So how do I make the API#write available to it? Is there some way to access the singleton instance of Api? In that case, who would be in charge of instantiating the singleton instance?
I played around with a static API class but this obviously doesn't work, since injection is into instances, not static classes.
What basic design pattern am I missing here?
I've been struggling with this for a while as well. I did come up with sort of a solution, but it feels hacky.
I use a singleton AppInjector instance. It is set only once, in the constructor of AppComponent (the bootstrapped component). It is used to resolve dependencies for objects to create.
This AppInjector looks something like this:
export class AppInjector {
private static _INJECTOR: Injector;
public static set INSTANCE(injector: Injector) {
if (!this._INJECTOR) {
this._INJECTOR = injector;
}
}
public static get INSTANCE() : Injector {
return this._INJECTOR;
}
}
And the contructor of the AppComponent looks like this:
constructor(injector: Injector) {
AppInjector.INSTANCE = injector;
}
Now if you have added your Api in the bootstrap function of your application:
bootstrap(AppComponent, [Api]);
You will be able to get this singleton from within the Cat instance like this:
class Cat {
public get _api() : Api {
return AppInjector.INSTANCE.get(Api);
}
constructor(private name: string) { }
public write() {
this._api.write(this);
}
}
Again, this by no means feels like the right solution, and probably against a lot of guidelines in Angular2, but I understand your problem, and would also like a better solution than this, but it works fine now for as it is, and I can happily continue coding
Related
I need to get dependencies in class that implements yii\queue\Job interface. In perfect world I would do something like this:
public function __construct(SomeInterface $service)
{
$this->service = $service;
}
public function execute($queue)
{
$this->service->doSomething();
}
Unfortunately yii2-queue doesn't support resolving dependencies in job handler constructor. For now I deal with it like this:
public function execute($queue)
{
$service = Yii::$container->get(SomeInterface::class);
$service->doSomething();
}
Maybe there is a cleaner way to do this?
I dealt with it using factory method pattern and resolving dependencies from DI container in it. Altought there are some problems with serialization of heavy dependecies. To resolve that I used simple proxy.
$this->queue->push(FooJob::create($someId));
// (...)
class FooJob implements Job
{
public $id;
private $fooService;
private $heavyService;
public function __construct(int $id, FooInterface $fooService)
{
$this->fooService = $fooService;
}
public static function create(int $id): self
{
return Yii::$container->get(static::class, [$id]);
}
public function execute(Queue $queue): void
{
$this->fooService->bar($this->id); // service available since construct
$this->heavyService->bark($this->id); // service available since first call
}
public function getHeavyService(Queue $queue): HeavyInterface
{
if (!$this->heavyService) {
$this->heavyService = Yii::$container->get(HeavyServiceInterface::class);
}
return $this->heavyService;
}
}
This is way more clean approach than one I used before. It's not perfect, but with Yii limitations its enough.
I'm relatively new to Guice, and some things still give me a pretty hard time.
My particular question is, how do you handle nested injections in Guice.
Example:
Class A uses Class B via #Inject, and Class B uses Class C.
Explicitly:
My Module where I bind to Providers.
public class ModuleBinder extends AbstractModule {
#Override
protected void configure() {
bind(DatabaseControllerInterface.class)
.toProvider(DatabaseControllerProvider.class).asEagerSingleton();
bind(AnalyzerInterface.class)
.toProvider(AnalyzerProvider.class).asEagerSingleton();
bind(SystemAdministrationInterface.class)
.toProvider(SystemAdministrationProvider.class).asEagerSingleton();
bind(LogInServiceInterface.class)
.toProvider(LogInServiceProvider.class);
}
}
The DatabaseControllerProvider:
public class DatabaseControllerProvider implements Provider<DatabaseControllerInterface> {
#Override
public DatabaseControllerInterface get() {
return new DatabaseControllerImpl();
}
}
The LogInServiceProvider:
public class LogInServiceProvider implements Provider<LogInServiceInterface> {
#Override
public LogInServiceInterface get() {
return new LogInServiceImpl();
}
}
And finally, the LogInService uses:
public class LogInServiceImpl implements LogInServiceInterface{
#Inject
private DatabaseControllerProvider databaseControllerProvider;
private final DatabaseControllerInterface databaseController;
public LogInServiceImpl() {
this.databaseController = databaseControllerProvider.get();
}
#Override
public User register(final String mail, final String userName, final String password) {
databaseController.registerUser(userName, mail, password, UserRole.ADMIN);
}
}
The call is then:
public class Test() {
public static test() {
final Injector injector = Guice.createInjector(new ModuleBinder());
logInService = injector.getInstance(LogInServiceInterface.class);
logInService.registerUser("test", "test", "test");
}
}
I know most of you guys will get sick with that code, but hey, I'm a beginner with Guice, so please be gentle with me.
I want to use Constructor injection, I already realized that field injection is considered "evil". Do you have any idea how to get that working by keeping the providers (I need them)?
Using the injections in the example does nothing on the "second" level, the DatabaseControllerImpl in LogInServiceImpl is null.
Did I configure something wrong? Did I misunderstand the usage of Provides and/or Modules?
I hope somebody can and wants to help me. If you need more informations, post a comment.
With best regards,
JosefRucksack
Your direct answer: You're calling new T(); in your Providers, which doesn't support field injection.
First, a real timesaver: Don't keep your explicit Providers. If you have bound a T, Guice allows you to inject a Provider or call Injector.getProvider for that T, even if you haven't explicitly created a Provider yourself. See the Built-In Bindings page on the wiki,
or the Injector docs (emphasis mine):
Contains several default bindings:
This Injector instance itself
A Provider<T> for each binding of type T
The Logger for the class being injected
The Stage in which the Injector was created
Instead, do it this way:
public class ModuleBinder extends AbstractModule {
#Override
protected void configure() {
bind(DatabaseControllerInterface.class)
.to(DatabaseControllerImpl.class).asEagerSingleton();
bind(AnalyzerInterface.class)
.to(AnalyzerImpl.class).asEagerSingleton();
bind(SystemAdministrationInterface.class)
.to(SystemAdministrationImpl.class).asEagerSingleton();
bind(LogInServiceInterface.class)
.to(LogInServiceImpl.class);
}
}
You then have the same choice you do now, to inject T or Provider<T> and call getInstance or getProvider as needed.
If your Providers are absolutely necessary, especially if they actually receive an instance from some other system or service locator, one other option is to add your #Inject fields into them as in the Provider bindings wiki page and pass them into your constructor, or to just inject a MembersInjector<T>:
public class LogInServiceProvider implements Provider<LogInServiceInterface> {
#Inject MembersInjector<LogInServiceImpl> logInServiceImplInjector;
#Override
public LogInServiceInterface get() {
LogInServiceImpl logInServiceImpl = YourExternalDep.getLogInService();
logInServiceImplInjector.injectMembers(logInServiceImpl);
return logInServiceImpl;
}
}
However, this explicit-Provider solution is not idiomatic Guice, and should only be used with external or legacy code. Guice's whole reason for existence is to automate away boilerplate and let your systems come together clearly and flexibly. Providers are an implementation detail; let Guice create them for you.
Am trying to learn how guice plays with Play 2.1 framework. I have a service to which I need access outside the service package. I have placed the below in Global file
protected Injector configure() {
injector = Guice.createInjector(new AbstractModule() {
#Override
protected void configure() {
bind(MyService.class).to(MyServiceImpl.class).in(Singleton.class);
}
});
return injector;
}
#Override
public <A> A getControllerInstance(Class<A> clazz) throws Exception {
return injector.getInstance(clazz);
}
Inside the controller class am able to get to my object by doing below and everything seems to be fine
#Inject
MyService serviceObj
But elsewhere outside the controller the same object appears to be null. For example I have a core module which takes care of talking to the service. The controller classes hands out the job to the core module. I need to be able to get hold of this MyService obj in the core module classes.
What am I missing here guys?
Thanks
Karthik
I had figured a way out to do this.
In my configure method I had to use this
protected Injector configure() {
injector = Guice.createInjector(new AbstractModule() {
#Override
protected void configure() {
requestStaticInjection(TheClassThatNeedsMyService.class);
}
});
return injector;
}
And in my TheClassThatNeedsMyService I had to just do
#Inject MyService serviceObj;
Just for reference this is how my Service class looks like
#ImplementedBy(MyServiceImpl.class)
public interface MyService{
...
}
#Singleton
public class MyServiceImpl implements MyService{
...
}
Now am able to get access to my service object whereever I want in my application. Hope it helps someone
Thanks
Karthik
As an alternative to static injection, see the play-guice sample here:
http://typesafe.com/activator/template/play-guice
Guice can be used in a conventional manner with Play.
I would like to have a static instance method with Guice for one of the components (non-managed bean should be able to access this class). I created something like this:
public class LookupService {
#Inject
private static Provider<Injector> injector = null;
private final ILookup<IWS> lookup;
#Inject
public LookupService(ILookup<IWS> lookup) {
this.lookup = lookup;
}
public static LookupService instance() {
return injector.get().getInstance(LookupService.class);
}
public <T extends IWS> T lookup(Class<T> localInterface) {
return lookup.lookup(localInterface);
}
}
What do you think about this design ? Any other ideas on this ? (accessing managed beans from non-managed objects)
Basically, the pattern you're looking for is called "requesting static injection" and there's a Binder method dedicated to it. Once you have that down, your code looks a lot like this example from the Guice docs.
public class MainModule extends AbstractModule {
#Override public void configure() {
requestStaticInjection(LookupService.class);
}
}
public class LookupService {
/** This will be set as soon as the injector is created. */
#Inject
static Provider<LookupService> provider = null;
private final ILookup<IWS> lookup;
#Inject
public LookupService(ILookup<IWS> lookup) {
this.lookup = lookup;
}
public static LookupService instance() {
return provider.get();
}
public <T extends IWS> T lookup(Class<T> localInterface) {
return lookup.lookup(localInterface);
}
}
A few notes:
While you can still set your field to be private, remember that this means you cannot set it in tests (or in future non-Guice usage) without Guice's private-field-access magic. When using injected fields, we often make them package-private and then put the tests in the same package.
Static injection is generally seen as something to endorse only when migrating to Guice, or when you use other code you can't change. When possible, try to avoid global state--even if this means making FooBean data-only and creating an injected FooBeanService.
Even though you can inject an Injector wherever you'd like, you might find it easier to test if you simply inject a Provider<LookupService> instead. Only inject an Injector if you don't know what type you're going to need until runtime--for example, if you implement LookupService.lookup(...) using an Injector by passing the class literal to the injector to get an instance.
In fact, it's hard to say from here, but ILookup seems to act a lot like the Service Locator pattern, which solves the exact type of problem that Guice solves with dependency injection! If that's the case, you might as well rewrite ILookup to use Guice: Just remove calls to LookupService.instance().lookup(Foo.class) and instead create a matching pair of #Inject static Provider<Foo> fooProvider and requestStaticInjection(FooUser.class).
Hope that helps!
Say I have a list of protocol handlers, and the client service knows which protocol to use based on an enum value, it would be nice to selected the protocol from the list of i/fs passed in.
How can I achieve this in StructureMap?:
public EmailTransportService(interfaces...,
IDictionary<EmailAccountType, IEmailTransportHandler> transportHandlers)
At the moment, I'm using ObjectFactory with get named instance like so:
_emailTransportHandlers = new Dictionary<EmailAccountType, string>
{
{EmailAccountType.Pop3, "Pop3Handler"},
{EmailAccountType.IMAP, "IMapHandler"}
};
then resolving like so:
private IEmailTransportHandler GetTransportHandler(EmailAccountType accountType)
{
return ObjectFactory.GetNamedInstance<IEmailTransportHandler>(_emailTransportHandlers[accountType]);
}
but I don't like this as its difficult within my unit tests to verify the calls to the handlers.
My service registry looks like so:
public EmailTransportServiceRegistry()
{
Scan(x =>
{
....
});
For<IEmailTransportHandler>().Use<ActiveUpPop3Handler>().Named("Pop3Handler");
For<IEmailTransportHandler>().Use<ActiveUpIMap4Handler>().Named("IMapHandler");
}
So basically I'm relying on named instances based on the dictionary list of protocol types.
My solution was to have a static register method from the client service like so:
public static IDictionary<EmailAccountType, IEmailTransportHandler> RxHandlerRegistration()
{
return new Dictionary<EmailAccountType, IEmailTransportHandler>
{
// following registrations use ActiveUp library for pop3/imap (http://mailsystem.codeplex.com/)
{EmailAccountType.Pop3, ObjectFactory.GetInstance<ActiveUpPop3Handler>()},
{EmailAccountType.IMAP, ObjectFactory.GetInstance<ActiveUpIMap4Handler>()}
};
}
Then in the ServiceRegistry class:
public class EmailTransportServiceRegistry : ServiceRegistry
{
public EmailTransportServiceRegistry()
{
// other registries...
For<IDictionary<EmailAccountType, IEmailTransportHandler>>().Use(x => EmailTransportService.RxHandlerRegistration());
}
}