Do i need to make all object instantiation via producer in CDI - java

I just start a CDI project. In this project a Beans2 is injected inside a Beans1.
But the Beans2 has a method that create a file. This method instantiates the File Object like this :
new File('myPathFile');
Because this instantiation is not managed by the CDI container, Bean2 is not injected into Beans1. I try to make a producer to inject a File into the Beans2 but do i need to do the same thing for all java base class that i will use?
Is there another solution to simply use class that do not need to be inject?
Bean1 :
#Dependant
public class Bean1 implements Serializable {
private #Inject #Bean2Producer Bean2 bean2;
public void someMethod() {
bean2.foo();
}
}
Bean2 :
#Dependant
public class Bean2 extends AbstractClass implements Serializable {
private #Inject #PathDir String pathDir;
public Bean2(String param1, boolean param2) {
super(param1, param2);
}
public void foo() {
File file = new File(pathDir);
}
}
pathDir Producer :
#ApplicationScoped
public class ProjectProducer implements Serializable {
#Produces
#PathDir
public String getPathDir() {
try {
return PropsUtils.geetProperties().getProperty(PATH_DIR);
} catch (Exception e) {
e.printStackTrace();
}
}
}
PathDir annotation :
#Qualifier
#Target({FIELD, METHOD, PARAMETER, CONSTRUCTOR, TYPE})
#Retention(RUNTIME)
#Documented
public #interface PathDir {}
In this example, PathDir is not Inject if new File is invoke in foo() method.

The reason why Bean2 is not injected into Bean1 is that there is no suitable constructor in Bean2 so that CDI can automatically create an instance of it. Beans need to have a constructor with no parameters or a constructor with all parameters injected, see more in the Java EE tutorial
This is simple the limitation of CDI. If you want to pass arguments to your bean, you need to supply a setter method(s) to pass arguments after bean2 is injected. Or, in case you need to have some values during object creation (because the abstract parent requires them), you need to inject all constructor arguments, like this (#Param1 and #param2 are qualifiers):
public Bean2(#Inject #Param1 String param1, #Inject #Param2 boolean param2) {
super(param1, param2);
}
Alternatively, you do not need to turn every object into a CDI bean, especially if you have requirements on constructor parameters. You may inject all dependencies manually after the bean is created. It means you do use #Inject in Bean2, but provide a setter method, inject into bean1 and set the value in postconstruct method of bean1, example follows:
Bean1 :
#Dependant
public class Bean1 implements Serializable {
private #Inject #PathDir String pathDir; // inject pathDir here instead of in Bean2
private Bean2 bean2; // without inject, created in init()
#PostConstruct
public void init() {
bean2 = new Bean2("param1", "param2");
bean2.setPathDir(pathDir); // set injected value manually
}
public void someMethod() {
bean2.foo(); // here bean2.pathDir should be already initialized via setPathDir in init() method above
}
}
Bean2 :
#Dependant
public class Bean2 extends AbstractClass implements Serializable {
private String pathDir;
public Bean2(String param1, boolean param2) {
super(param1, param2);
}
public void setPathDir(String pathDir) {
this.pathDir = pathDir;
}
public void foo() {
File file = new File(pathDir);
}
}
Or even better, merge setPathDir and Bean2 constructor - it will be clear that pathDir is a required dependency for Bean2.

Related

How to specify which version of a concrete implementation for an abstract class to autowire in Spring?

Having the following class structure:
public abstract class A {
String someProperty = "property"
public abstract void doSomething();
}
#Service
public class Aa extends A {
#Override
public abstract void doSomething() {
System.out.println("I did");
}
}
#Service
public class Ab extends A {
#Override
public abstract void doSomething() {
System.out.println("I did something else");
}
}
I need a way to tell Spring which A concrete class to Autowire in my Foo service, based on a property in a properties file.
#Service
public class Foo {
#Autowire
private A assignMeAConcreteClass;
}
And in my properties file I have this:
should-Aa-be-used: {true, false}
Remove the #Service annotation, instead write a #Bean-annotated method in a configuration class that reads the properties, and returns the appropriate A instance.
Not a new way but in your case I think that a possible suitable way would be to use
FactoryBean in the class that wants to inject the bean conditionally.
The idea is simple : you implement FactoryBean by parameterizing it with the interface of the bean that you want to inject and override getObject() to inject the wished implementation :
public class FactoryBeanA implements FactoryBean<A> {
#Autowired
private ApplicationContext applicationContext;
#Value("${should-Aa-be-used}")
private boolean shouldBeUsed;
#Override
public A getObject() {
if (shouldBeUsed) {
return applicationContext.getBean(Aa.class));
return applicationContext.getBean(Ab.class));
}
}
But FactoryBean instances are not classic beans. You have to configure it specifically.
You could configure it in a Spring Java configuration in this way :
#Configuration
public class FactoryBeanAConfiguration{
#Bean(name = "factoryBeanA")
public FactoryBeanA factoryBeanA() {
return new FactoryBeanA();
}
#Bean
public beanA() throws Exception {
return factoryBeanA().getObject();
}
}

Using an autowired parameter in another instance variable

I have a service that implements an interface. I now want to write a mapper, that basically says, when I pass in this enum type, use that service. This is what I have
#Service
MyService implements Service {}
#Component
#RequiredArgsConstructor
MyMapper implements Mapper<Enum, Service> {
private final MyService myService;
private ImmutableMap<Enum, Service> MAPPER = ImmutableMap.<MyEnum, MyService>builder()
.put(Enum.A, myService)
.build();;
#Override
public Service map(Enum input) {
return MAPPER.get(input);
}
}
However, it seems that this doesn't work. I think I am not allowed to use an (autowired) instance variable for the instantiation of another instance variable.
To solve this I now used a singleton pattern.
#Service
MyService implements Service {}
#Component
#RequiredArgsConstructor
MyMapper implements Mapper<Enum, Service> {
private final MyService myService;
private ImmutableMap<Enum, Service> MAPPER = null;
#Override
public Service map(Enum input) {
if(MAPPER == null){
MAPPER = createMapper();
}
return MAPPER.get(input);
}
private ImmutableMap<Enum, Service> createMapper(){
return ImmutableMap.<MyEnum, MyService>builder()
.put(Enum.A, myService)
.build();;
}
}
This seems to work, but I was wondering if there were other options to solve this.
For this problem service locator is best fit.
My Enum:-
public enum MyEnum {
A,
B
}
Create service and with the name "A" and "B" (Name of your enum as string):-
#Service("A")
MyService1 implements Service {}
#Service("B")
MyService2 implements Service {}
Create MyMapper interface:-
public interface MyMapper {
Service map(MyEnum myEnum);
}
Configure ServiceLocatorFactoryBean :-
#Bean
public ServiceLocatorFactoryBean serviceLocatorFactoryBean(){
ServiceLocatorFactoryBean bean = new ServiceLocatorFactoryBean();
bean.setServiceLocatorInterface(MyMapper.class);
return bean;
}
Start using:-
#Autowired
MyMapper mapper;
You are running into an edge case regarding final variables; even though it's marked final, the map's initializer statement runs before instance initializer blocks (which would otherwise be useful), which run before the constructor body that makes the assignment to the variable.
I'm not certain why you're wanting to create a map just to hold a singleton value, but you'll need to assign the map inside your constructor body. If you really want this setup for some reason, my suggestion would be to do this:
private final Map<Enum, Service> MAPPER;
public MyMapper(MyService myService) {
MAPPER = Map.of(Enum.A, myService);
}

Spring #Autowired on all setter in a class

Is possible to specify that all setter should be autowired with one annotation?
This is my class:
#Component
public class MyClass {
private static Bean1 bean1;
//...
private static BeanN beanN;
public static Bean1 getBean1() {
return bean1;
}
#Autowired
public void setBean1(Bean1 bean1) {
MyClass.bean1 = bean1;
}
//...
public static BeanN getBeanN() {
return beanN;
}
#Autowired
public void setBeanN(BeanN beanN) {
MyClass.beanN = beanN;
}
}
No. There is no such built-in annotation. Also, Spring doesn't care that your method is to be interpreted as a bean mutator (a setter). Any method can be annotated with #Autowired and Spring will try to invoke it with the appropriate arguments.
Since the whole point of Spring is dependency injection, there's no reason for you to have static fields. Just inject the bean where you need it.

How to use spring #Lookup annotation?

I need to get prototype class from singleton. I found that method injection is the way to go, but I don't really know how to use spring #Lookup annotation.
I'm new to dependency-injection, and I chose to go with annotation configuration, so I would like to continue in that direction.
I found out that #Lookup annotation was added only recently (https://spring.io/blog/2014/09/04/spring-framework-4-1-ga-is-here), but I cannot find anywhere how to use it.
So, here is simplified example
Configuration class:
#Configuration
#Lazy
public class ApplicationConfiguration implements ApplicationConfigurationInterface {
#Bean
public MyClass1 myClass1() {
return new ContentHolderTabPaneController();
}
#Bean
#Scope("prototype")
public MyClass2 myClass2() {
return new SidebarQuickMenuController();
}
}
And here is class example:
public class MyClass1 {
doSomething() {
myClass2();
}
//I want this method to return MyClass2 prototype
public MyClass2 myClass2(){
}
}
How do I do that with #Lookup annotation?
Before applying #Lookup annotation to your public MyClass2 myClass2() method, read this in #Lookup's Javadoc:
the container will generate runtime subclasses of the method's containing class via CGLIB, which is why such lookup methods can only work on beans that the container instantiates through regular constructors (i.e. lookup methods cannot get replaced on beans returned from factory methods where we can't dynamically provide a subclass for them).
So remove the following factory method style bean declaration from ApplicationConfiguration:
#Bean
public MyClass1 myClass1() {
return new ContentHolderTabPaneController();
}
and add #Component annotation to let Spring instantiate the bean (also add the #Lookup annotation to the method):
#Component
public class MyClass1 {
doSomething() {
myClass2();
}
//I want this method to return MyClass2 prototype
#Lookup
public MyClass2 myClass2(){
return null; // This implementation will be overridden by dynamically generated subclass
}
}
Now get myClass1 bean out of context, and its myClass2 method should have been replaced/overridden to get a new prototype bean each time.
Update:
Using factory method declaration
It's not hard to implement the #Lookup annotated method (the "lookup method"). Without #Lookup and keeping your configuration class unchanged, now MyClass1 looks like (in fact Spring generates a similar implementation in a subclass if #Lookup were used):
public class MyClass1 {
doSomething() {
myClass2();
}
//I want this method to return MyClass2 prototype
#Autowired
private ApplicationContext applicationContext;
public MyClass2 myClass2() {
return applicationContext.getBean(MyClass2.class);
}
}
Spring injects the ApplicationContext for you.
If you are not on Spring 4.1 you can use the provider injection instead:
public class MyClass1 {
#Autowired
private Provider<MyClass2> myClass2Provider;
doSomething() {
MyClass2 myClass2 = myClass2();
myClass2.fooBar()
}
public MyClass2 myClass2(){
return myClass2Provider.get();
}
}
This is DI, IoC, avoids abstract classes and xml definitions for lookup methods.
Also, you can declare myClass2 bean with TARGET_CLASS proxyMode.
#Bean
#Scope("prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
public MyClass2 myClass2() {
return new SidebarQuickMenuController();
}

Manually Register Class in CDI Container

I have a group of classes which are instantiated by reflection, hence these are not managed by the CDI container, and no injections are made by the context. My question is, is there any way to register these classes in the CDI context, so the classes get managed by the context?
Bellow, is how I create the classes:
String clazz = "org.myorg.thisIsMyClass";
MyClass myClass = Class.forName(clazz).newInstance(); // myClass instance not managed by CDI
How do I make the instance of myClass managed by the CDI container?
If your classes were registered as bean by the container you can use programmatic lookup to get them easily.
#Inject
#Any
Instance<Object> myBeans;
public Object getMyBeanFromClassName(String className) throws Exception{
Class clazz = Class.forName(className);
return myBeans.select(clazz).get();
}
Et voilĂ .
Following the comments of #AdrianMitev, I finally ended up writing this class which returns an instance of a Managed CDI Bean given its class name (elName) or class type:
public class GetInstance {
public static Object of(String elName) {
BeanManager bm = getBeanManager();
Bean<?> bean = bm.resolve(bm.getBeans(elName));
return bm.getReference(bean, bean.getBeanClass(), bm.createCreationalContext(bean));
}
#SuppressWarnings("unchecked")
public static <T> T of(Class<T> clazz) {
BeanManager bm = getBeanManager();
Bean<?> bean = bm.resolve(bm.getBeans(clazz));
return (T) bm.getReference(bean, bean.getBeanClass(), bm.createCreationalContext(bean));
}
private static BeanManager getBeanManager() {
try {
return (BeanManager) new InitialContext().lookup("java:comp/BeanManager");
} catch (NamingException e) {
e.printStackTrace();
}
return null;
}
}
So, if you have a class like this one:
#Named
public class FooClass {
...
}
You can get an a managed CDI instance using:
FooClass fC = GetInstance.of(FooClass.class);
or using its elName
FooClass fC = (FooClass) GetInstance.of("fooClass");
or you can select the name to use:
#Named(value="CustomFooClassName")
public class FooClass {
...
}
And using:
FooClass fC = (FooClass) GetInstance.of("CustomFooClassName");
The easiest way to do let CDI to manage your class is to use a producer.
public class MyProducers {
#Produces
#RequestScoped //Could be any scope here
#FromReflection //a qualifier to distinguish this Bean of type Object from others. Would be better to have a more specific bean type if all your class come from the same ancestor.
public Object produceMyClass()
{
String clazz = "org.myorg.thisIsMyClass";
Object myObject = Class.forName(clazz).newInstance();
return myObject;
}
}
Somewhere else in your code you can use this producer like this :
#Inject
#FromReflection
Object myBean;
** Edit : adding InjectionPoint usage. **
Now you can enhance your producer by injecting it's InjectionPointin its parameter list. You can then use the metadata of the injection point (i.e qualifier) to dynamically find your class.
First you have to add a field to store the class name in your #FromReflection qualifier :
#Qualifier
#Target({TYPE, METHOD, PARAMETER, FIELD})
#Retention(RUNTIME)
#Documented
public #interface FromReflection {
#Nonbinding String value(); // classname will be store here
}
then you use this info in your producer :
public class MyProducers {
private String extractClassName(InjectionPoint ip) {
for (Annotation annotation : ip.getQualifiers()) {
if (annotation.annotationType().equals(FromReflection.class))
return ((FromReflection) annotation).value();
}
throw new IllegalStateException("No #FromReflection on InjectionPoint");
}
#Produces
#FromReflection
public Object produceMyClass(InjectionPoint ip)
{
String clazzNanme = extractClassName(ip);
Object myObject = Class.forName(clazz).newInstance();
return myObject;
}
}
Note that the produced bean has to be in #Dependent scope, it's a constraint when injecting InjectionPoint in the producer parameters.
You can now inject your bean like that :
#Inject
#FromReflection("org.myorg.thisIsMyClass")
Object myBean;
Now if you want to decide at runtime which class you want to build, you'll have to use the CDI programmatic lookup feature which allow you to create synthetic qualifier.
First create an AnnotationLiteral for your qualifier to be able to instantiate a a new qualifier.
public class FromReflectionLiteral extends AnnotationLiteral<FromReflection> implements FromReflection {
private String value;
public FromReflectionLiteral(String value) {
this.value = value;
}
#Override
public String value() {
return value;
}
}
Then you'll use the Instance<> bean to request for your final bean.
public class ConsumingBean {
#Inject
#Any
Instance<Object> myBeanInstance;
public Object getBeanFor(String className) {
return myBeanInstance.select(new FromReflectionLiteral(className)).get();
}
...
}
Next step would be to use a portable extension...
You can make CDI aware of your instance by not instantiating the bean yourself (as i pointed in your previous post) but to let CDI instantiate the bean. Here is a sample code for that:
InitialContext initialContext = new InitialContext();
BeanManager bm = (BeanManager) initialContext.lookup("java:comp/BeanManager");
//List all CDI Managed Beans and their EL-accessible name
Set<Bean<?>> beans = bm.getBeans(AbstractBean.class, new AnnotationLiteral<Any>() {});
List<Object> beanInstances = new ArrayList<Object>();
for (Bean bean : beans) {
CreationalContext cc = bm.createCreationalContext(bean);
//Instantiates bean if not already in-service (undesirable)
Object beanInstance = bm.getReference(bean, bean.getBeanClass(), cc);
beanInstances.add(beanInstance);
}
return beanInstances;
If you are sure that there is only one bean of the specific type you can use beans.iterator.next().

Categories