How to inject a dependency into a repository base class - dependency-injection

The various #EnableXXXRepository annotations of Spring Data allow you to specify a custom base class for your repositories, which will be used as an implementation of methods in your repository.
If such a base class needs access to other beans in the ApplicationContext how does one get those injected? It doesn't work out of the box, because Spring Data instantiates those base classes itself, supporting only special store dependent constructor parameters.
Note: I created this answer in the chat to this now deleted question and thought it might be valuable for others, although the original question is gone.

In the #Enable...Repository annotation specify a repositoryBaseClass and repositoryFactoryBeanClass. Like this:
#EnableMongoRepositories(
repositoryBaseClass = MyBaseClass.class,
repositoryFactoryBeanClass = MyRepositoryFactoryBean.class)
In that RepositoryFactoryBean class, you can use normal dependency injection, because it is a Spring Bean, so, for example, you can get an instance of SomeBean injected via the constructor, as shown below:
public class MyRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extends Serializable> extends MongoRepositoryFactoryBean<T,S,ID>{
private final SomeBean bean;
public MyRepositoryFactoryBean(Class repositoryInterface, SomeBean bean) {
super(repositoryInterface);
this.bean = bean;
}
}
Your RepositoryFactoryBean now create an instance of a custom RepositoryFactory by overwriting 'getFactoryInstance'.
#Override
protected RepositoryFactorySupport getFactoryInstance(MongoOperations operations) {
return new MyMongoRepositoryFactory(operations, bean);
}
While doing so, it can pass on the bean to be injected. bean in the example above.
And this factory finally instantiates your repository base class. Probably the best way to do it is to delegate everything to the existing factory class and just add injecting of the dependency to the mix:
public class MyMongoRepositoryFactory extends MongoRepositoryFactory {
private final SomeBean bean;
MyMongoRepositoryFactory(MongoOperations mongoOperations, SomeBean bean) {
super(mongoOperations);
this.bean = bean;
}
#Override
protected Object getTargetRepository(RepositoryInformation information) {
Object targetRepository = super.getTargetRepository(information);
if (targetRepository instanceof MyBaseClass) {
((MyBaseClass) targetRepository).setSomeBean(bean);
}
return targetRepository;
}
}
There is a complete working example on Github.

Related

Getting an injected object using CDI Produces

I have a class (OmeletteMaker) that contains an injected field (Vegetable). I would like to write a producer that instantiates an injected object of this class. If I use 'new', the result will not use injection. If I try to use a WeldContainer, I get an exception, since OmeletteMaker is #Alternative. Is there a third way to achieve this?
Here is my code:
#Alternative
public class OmeletteMaker implements EggMaker {
#Inject
Vegetable vegetable;
#Override
public String toString() {
return "Omelette: " + vegetable;
}
}
a vegetable for injection:
public class Tomato implements Vegetable {
#Override
public String toString() {
return "Tomato";
}
}
main file
public class CafeteriaMainApp {
public static WeldContainer container = new Weld().initialize();
public static void main(String[] args) {
Restaurant restaurant = (Restaurant) container.instance().select(Restaurant.class).get();
System.out.println(restaurant);
}
#Produces
public EggMaker eggMakerGenerator() {
return new OmeletteMaker();
}
}
The result I get is "Restaurant: Omelette: null", While I'd like to get "Restaurant: Omelette: Tomato"
If you provide OmeletteMaker yourself, its fields will not be injected by the CDI container. To use #Alternative, don't forget specifying it in the beans.xml and let the container instantiate the EggMaker instance:
<alternatives>
<class>your.package.path.OmeletteMaker</class>
</alternatives>
If you only want to implement this with Producer method then my answer may be inappropriate. I don't think it is possible (with standard CDI). The docs says: Producer methods provide a way to inject objects that are not beans, objects whose values may vary at runtime, and objects that require custom initialization.
Thanks Kukeltje for pointing to the other CDI question in comment:
With CDI extensions like Deltaspike, it is possible to inject the fields into an object created with new, simply with BeanProvider#injectFileds. I tested this myself:
#Produces
public EggMaker eggMakerProducer() {
EggMaker eggMaker = new OmeletteMaker();
BeanProvider.injectFields(eggMaker);
return eggMaker;
}

How to inject a list with different implementations of the same interface in a nested module scenario via Guice?

There is an interface DCE, which is implemented by a class DCEImpl which has a dependency, say, string S, which it gets via its constructor.
The universe of S is limited, say S can only take values {'A','B','C'}.
There is an already existing Guice module that accepts the value of S in its constructor, and then binds the interface DCE to the correctly initialized version of DCEImpl.
public class DCEModule extends AbstractModule {
private final String s;
public DCEModule(String s){
this.s = s;
}
protected void configure() {
bind(DCE.class).toInstance(new DCEImpl(s));
}
}
Now I have a class C which needs a List<DCE> with all the 3 implementations (actually a lot more than 3, using 3 for example purpose).
I want to inject this list via Guice in C. To do that, I created a new module DCEPModule, which will provide a List<DCE> in this way:
#Provides
List<DCE> getDCE() {
for(String s: S){
Module m = new DCEModule(s);
install(m);
Injector injector = Guice.createInjector(m);
listDomains.add(injector.getInstance(DCE.class));
}
}
My problem is that I don't want to call a new injector in this module, because DCEPModule will be installed by a different module.
public class NewModule extends AbstractModule {
protected void configure() {
install(DCEPModule);
}
}
I want a way to get the List<DCE> without explicitly creating a new injector in DCEPModule.
You can achieve this by using a Multibinder (javadoc, wiki).
Here’s an example:
public class SnacksModule extends AbstractModule {
protected void configure(){
Multibinder<Snack> multibinder = Multibinder.newSetBinder(binder(), Snack.class);
multibinder.addBinding().toInstance(new Twix());
  multibinder.addBinding().toProvider(SnickersProvider.class);
  multibinder.addBinding().to(Skittles.class);
}
}
Now, the multibinder will provide a Set<Snack>. If you absolutely need a List instead of a Set, then you can add a method to your module like this:
#Provides
public List<Snack> getSnackList(Set<Snack> snackSet) {
return new ArrayList(snackSet);
}
You can add implementations to the same Multibinding in more than one module. When you call Multibinder.newSetBinder(binder, type) it doesn’t necessarily create a new Multibinding. If a Multibinding already exists for for that type, then you will get the existing Multibinding.

Use Guice nested injects

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.

CDI, polymorphism: Is it possible to inject an implementation of bean B into a bean A based on a field initialized during #PostConstruct of A?

In my JSF application I'm using a #ViewScoped bean Publication to show/edit data coming from my database. In that bean there is a field for a subtype-specific data object, i.e. containing a different object depending on whether the publication is, say, a book or an article.
#ViewScoped
#Named
public class Publication implements Serializable {
#Inject
DatabaseStorage storage;
...
String id;
String type;
PublicationType typedStuff;
#PostConstruct
public void init() {
// Get an URL parameter from the request,
// look up row in database accordingly, initialize String "type".
switch (type) {
case "ARTICLE":
typedStuff = new Article(id);
break;
case "BOOK":
typedStuff = new Book(id);
break;
default:
break;
}
}
}
...with classes Article and Book that implement / extend PublicationType.
So far, so good, but I would like for typedStuff to be a CDI bean, so that I can inject useful resources there.
I've read this and this page on producer methods, as well as this tutorial and this very related SO question, but none of them answer precisely my question: Can I inject based on a field that the injecting bean itself only knows at runtime?
I've gotten the producer method to work as such, but I can't parametrize it, so I can't get that switch to work.
If I put the producer method in a separate class (or bean) then I don't have access to the type field.
If I inject the injecting bean into the producer class, or move the producer method into the injecting class, I get a circular injection.
If I put the producer method statically into the injecting class, I also don't have access, because type cannot be static. (Although, since it's only used momentarily...?)
Also (and that is probably the answer right there), the producer method is executed before my injecting bean's init method, so type wouldn't even have been set yet.
Does anybody have a better idea?
No you cannot, but you can select a bean based on the field value. Say:
public interface PublicationType {}
#PType("ARTICLE")
public class Article implements PublicationType{}
#PType("BOOK")
public class Book implements PublicationType {}
And define a qualifier:
public #interface PType {
String value();
}
And define an AnnotationLiteral:
public class PTypeLiteral extends AnnotationLiteral<PType> implements PType {}
Then you can use:
public class Publication {
#Any
#Inject
private Instance<PublicationType> publicationTypes;
public void doSomething() {
PType ptype = new PTypeLiteral(type);
// Of course you will have to handle all the kind of exceptions here.
PublicationType publicationType = publicationTypes.select(ptype).get();
}
}
There is the javax.inject.Provider interface (I think You are using #Named and #Inject annotations from the same package).
You could use it to achieve what You want. It will create instances for You with injected fields.
One drawback is that You will have to set the id yourself.
#ViewScoped
#Named
public class Publication implements Serializable {
#Inject
DatabaseStorage storage;
#Inject
Provider<Article> articleProvider;
#Inject
Provider<Book> bookProvider;
String id;
String type;
PublicationType typedStuff;
#PostConstruct
public void init() {
// Get an URL parameter from the request,
// look up row in database accordingly, initialize String "type".
switch (type) {
case "ARTICLE":
typedStuff = articleProvider.get();
typedStuff.setId(id);
break;
case "BOOK":
typedStuff = bookProvider.get();
typedStuff.setId(id);
break;
default:
break;
}
}
}

Instance method with Guice

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!

Resources