How to pass parameter to injected class from another class in CDI? - java

I am new to CDI, tried to find solution for this question, but, couln't found any. Question is Suppose I have one class which is being injected(A) from, where some value(toPass) is getting injected, now I want to pass this same value(toPass) to class B, which is getting injected from class A.
public class A
{
String toPass = "abcd"; // This value is not hardcoded
#Inject
private B b;
}
public class B
{
private String toPass;
public B(String toPass)
{
toPass = toPass;
}
}
Can anybody please help me in this?
Note: we cannot initialize the toPass variable of B in the same way as we have initialized in A, there is some restriction to it. Basically in Spring we could have done it easily, but, I wanted to do it in CDI.

You have options:
1.
Set toPass variable to b from #PostConstruct method of bean A:
#PostConstruct
public void init() {
b.setToPass(toPass);
}
or
2.
Create producer for toPass variable and inject it into bean A and B.
Producer:
#Produces
#ToPass
public String produceToPass() {
...
return toPass;
}
Injection:
#Inject
#ToPass
String toPass;
or
3.
If bean A is not a dependent scoped bean you can use Provider interface to obtain an instance of bean A:
public class B
{
#Inject
Provider<A> a;
public void doSomeActionWithToPass() {
String toPass = a.get().getToPass());
...
}
But you should not use toPass from constructor or from #PostConstruct method.

I need to say before that injection happens just after the object is created and therefore in case toPass is going to change during the life of A object, this change will not have any effect on the already injected B object.
(It would be probably possible to overcome this with some hacky things like creating your own producer method and producing some kind of proxy that would lazily initialize the B instance... But that would be probably not nice )
public class A
{
String toPass = "abcd"; // This value is not hardcoded
private B b;
#Inject
public void setB(B b) {
this.b = b;
b.pass(toPass);
}
}

Related

Is there a way you can inject a class whose constructor has parameters in micronaut?

Suppose I have class A.
class A {
private String s;
public A(String s) {
this.s = s;
}
}
and I want to inject A into class B.
So far, I have been injecting classes without constructor parameters like
class B {
#Inject
private A a;
}
But I don't know how classes with constructor parameters can be injected.
How can I do this?
I think in your example A would be a prototype bean because it has an argument that you must provide. Because you must provide the argument you can't simply inject the bean like you would a singleton. You can inject the BeanContext and call createBean. For example: beanContext.createBean(A.class, "some string value"). That would require A be annotated with #Prototype and any constructor parameters that should be provided by the creator with #Parameter.
Instead of this:
#Singleton
class B {
#Inject
private A a;
}
You could have this:
#Singleton
class B {
private A a;
public B(A a) {
this.a = a;
}
}

Guice field injection

So here's the thing. I'm trying to inject some fields with guice, but it turns out that they are always null (not trying to use them on the constructor).
public class A {
#Inject
private SomeClass bar;
public A() {
foo();
}
public someMethod() {
bar.doSth();
}
}
public class B {
private A a;
public B() {
a = new A();
}
public someOtherMethod() {
a.someMethod();
}
}
Now whenever the app executed someOtherMethod and a tries to run bar.doSth a nice NullPointerException is raised.
So in order to fix this i ended Injecting the field manually inside the constructor:
public A() {
final Injector injector = Guice.createInjector();
bar = injector.getInstance(SomeClass.class);
}
But this is ugly, unclear, has a lot of boilerplate and I'm forced to do it on many places which make it tedious.
Is there any other way to archive this? Or an explanation of why the field is not being injected?
Thanks
Note:
I'm currently unavailable to do the propper injection on the constructor (with bar as a parameter) because of the refactoring it would imply. The project is now under heavy production and thus this kind of refactoring can't be done right now.
The IOC container only controls what you tell it to control. It doesn't know about the A inside of B, because it's being constructed directly, not managed by the container. You need to inject the A as well:
public class B {
#inject private A a;
public B() {}
...
}
And you can't just call new B() either, you need to inject the B as well.

Spring: Is it possible to have a duplicate constructor with different qualifier while autowiring?

I am trying to autowire a member in a class using the constructor.
#Component
public class MyClass {
private ClassA myMember;
#Autowire
public MyClass(ClassA objectA) {
myMember = objectA;
}
}
If I have multiple sources that create beans of ClassA, is it possible to have a duplicate constructor definition that instantiates based on the bean that was autowired into this class?
I want to do something like this:
#Component
public class MyClass {
private ClassA myMember;
#Autowire
public MyClass(#Qualifier ("qualifierA") ClassA objectA) {
myMember = objectA;
}
#Autowire
public MyClass(#Qualifier ("qualifierB") ClassA objectB) {
myMember = objectB;
}
}
I tried using #Qualifier this way, but it didn't work.
Is it possible to do what I'm trying to do, with Spring? How can I disambiguate based on the name (qualifierA) or (qualifierB), if the bean definition is like:
#Bean (name = "qualifierA")
public ClassA getQualifierA() {
...
}
#Bean (name = "qualifierB")
public ClassA getQualifierB() {
...
}
You can't have two constructors with the exact same signature in a single class in Java. Nor any other programming language I've ever encountered. You might use method-injection instead, with two methods (named differently, of course), mark them as #Autowired(required = false) and use the proper #Qualifier(...) to specify the instance you want to inject. You might want to handle the case when both instances are present in the spring context, so no unexpected things happen.
The short answer is: no, that is not possible. In Java you cannot have two constructors with exactly the same signature. And also, you can assign only one value to your "myMember".
However, what are you trying to accomplish here? It seems that in some occasions MyClass needs to use "objectA" and in other occasions, you need "objectB".
For these scenarios, you should not use autowiring (you can't), but simply use explicit wiring:
#Bean
MyClass myObject() {
return new MyClass(qualifierA());
}

Generic CDI producer method not working as expected

I have a CDI producer method which - depending on some conditions not relevant to this example - creates objects of different types:
public class TestProducer {
#Produces #TestQualifier
public Object create(InjectionPoint ip) {
if(something) {
return "a String";
} else {
return Integer.valueOf(42);
}
}
but when using this producer, I always get an error in the followin situation:
#Named("test")
public class TestComponent {
...
#Inject public void setA(#TestQualifier String stringValue) {
...
#Inject public void setB(#TestQualifier Integer integerValue) {
It only works when the create method of the producer has the expected type in the method signature:
public class TestProducer {
#Produces #SpringBean
public String create(InjectionPoint ip) {
Now the String get's injected correctly, but I have no way to also generate an integer from the producer method. But this is exactly what I want to avoid, since the producer itself should be completely generic.
Am I doing something wrong or is there no way to achieve the behaviour I want?
All CDI documentation makes it clear that CDI does typesafe dependency injection - and it is an exalted property of CDI. IMHO, what you are trying to do is just what CDI tries to avoid. You want the container to cast Object to each type and CDI does not work that way.
The injections points stringValue and integerValue can only receive a bean which has java.lang.String and java.lang.Integer in its list of bean types respectively. java.lang.Object does not satisfy this criterion.
I have two suggestions. First, since you have two or more injection points of different types, create two or more producer methods for that types:
public class TestProducer {
#Produces #TestQualifier
public String createString(InjectionPoint ip) {
if(something) {
return "a String";
} else {
// Some other value
}
}
#Produces #TestQualifier
public int createInt(InjectionPoint ip) {
if(something) {
return 42;
} else {
// Some other value
}
}
// ...
It works if the something condition is just to check the type of the injection point (what I am betting is the case).
However, if the something condition does decide the type using other criteria than the type of the injection point, I'd suggestion to do the "dirty job" yourself: inject the returned value in an Object-typed injection point and does the cast manually:
#Named("test")
public class TestComponent {
...
#Inject public void setA(#TestQualifier Object value) {
String stringValue = (String) value;
...
#Inject public void setB(#TestQualifier Object value) {
int intValue = (Integer) value;
The main point is that, unlike some other DI frameworks, CDI does not work against the Java type system - on the contrary, it heavily uses it. Do not try to fight against it but use this aspect of CDI in your favor :)
A producer for Object is strange anyway. I'm not sure if this is forbidden by the spec, or it's a bug, but I think you can make some clever workaround:
public class ValueHolder<T> {
private T value;
public T getValue() {
return value;
}
}
And then inject a ValueHolder<String> and ValueHolder<Integer>
Its possible create generic objects with CDI produces like that:
// the wrapper class
public class Wrapper<T> {
public final T bean;
public Wrapper(T bean){
this.bean = bean;
}
}
// the producer inside some class
#Produces
public <T> Wrapper<T> create(InjectionPoint p){
// with parameter 'p', it is possible retrieve the class type of <T>, at runtime
}
// the bean example 1
public class BeanA {
public void doFoo(){
// ...
}
}
// the bean example 2
public class BeanB {
public void doBar(){
// ...
}
}
// the class that uses the produced beans
public class SomeBean{
//// There on producer method, do you can retrieve the Class object of BeanA and BeanB, from type parameters of Wrapper.
#Inject
private Wrapper<BeanA> containerA;
#Inject
private Wrapper<BeanB> containerB;
public void doSomeThing(){
containerA.doFoo();
containerB.doBar();
}
}
Works on weld 2.2.0.
I think that works on some previous versions as well.
Your initializer methods will look for a managed bean with API types String and Integer, but your producer method bean only has API type (in case of producer method, return type) Object.
You can therefore only use Object in your initializer method injected fields and then discriminate between the types int the body of the receiver, or simply wrap them and the producer method in an actual type that can return Strings or Int (but I'd avoid the generics)

Is Spring able to handle this user case? Neither property nor constructor injection

Basically, I have a class A which depends on class B which in turn depends on a spring managed bean C, but I dont want to keep B as a class variable just use B inside one method for some reason. My solution is to create a static method get() in B which returns a instance of B. Now the problem is, C is not injected to B correctly.
// A cannot have B as a class/field variable.
public class A {
public void method(){
// B.get() returns a instance of B, but this instance is not the
// instance that spring created, it is the static "instance" in B.
B.get().doSomething();// ofcourse it throws out a nullpointer exception
}
}
class B{
#Resource(name = "c")
private C c;
private static B instance;
public static B get() {
return instance==null ? (instance=new B()) : instance;
}
public void doSomething(){
c.toString(); // this line will break if c is not
// injected to the instance of b
}
}
#Service("c")
class C {
}
How do I solve this problem??
The whole point of using Spring is it is a dependency injection framework, and you are hardcoding the dependency of B in A.
Try very hard not to do that. If you don't want to store B in an instance variable, pass it as an argument to the method.
If you are stubborn about doing that then you have to get an ApplicationContext and load it yourself. An alternative might be to have B implement InitializingBean and then have your afterPropertiesSet method register the current instance with a static instance variable.
There isn't a direct bit of spring to support this, but are are a couple of work arounds to solve the problem.
grab hold of the ApplicationContext in B.get() and call getBean in order to get spring to construct B. Getting hold of the ApplicationContext is another mission as it is typically not held anywhere statically, have a look in the spring reference manual for 'dirty singleton'
Let spring construct the bean and then keep a reference to it in the instance variable:
#Service('b')
class B{
#Resource(name = "c")
private C c;
private static B instance;
public B(){
// sets the static here
// not ideal...should use afterProperties set or what ever the Annotation equivalent is
instance = this;
}
public static B get() {
if( instance == null ){
throw new IllegalStateException("errr...say something useful here")
}
return instance;
}
public void doSomething(){
c.toString(); // this line will break if c is not
// injected to the instance of b
}
}
I've seen 2. used a couple of times on various projects, it's not pretty and if you have a choice don't do it, just get spring to wire the whole app. But if you don't have a choice, it might be the least worst option.
You'll have to get the bean programmatically from the Spring ApplicationContext, either within A#method(), or in the constructor of A or its initialization method, and caching the Spring injected B instance.

Categories

Resources