I just want to confirm that I fully understood the prerequisites for CDI to work. If I have a class A:
public class A {
#Inject private B b;
}
Now when I instantiate this class using:
A a = new A();
In that case, A.b will be null.
But if I define in another class a member:
#Inject A a;
and later use a, a.b will be correctly populated?
Does CDI only work if the class requiring an injection was also created by CDI container? Or what am I missing if injections turn out to be null while creating a POJO using ordinary instantiation with new (yes, I got beans.xml in place)?
Does CDI only work if the class requiring an injection was also
created by CDI container?
Yes, that's pretty much it. The lifecycle of a ManagedBean is controlled by the container and should never be instantiated with the new keyword (BTW: the same is true for EJBs & Spring beans). If you need to create a new ManagedBean you will probably want to use a producer method.
While others have stated correctly that for the most part DI containers will not inject dependencies into bean that they did not instantiate this is not entirely true.
Spring has a wonderful feature that will autowire the bean when you create it using new A().
You just have to use AspectJ and mark your bean with the #Configurable annotation.
#Configurable
public class A {
#Inject private B b;
}
Its actually kind of an awesome feature cause you can do Active Record style POJO's while still honoring your DI (its in fact how Spring Roo does it).
You should also know that with Spring you can autowire a bean programmatically after its been instantiated with the AutowireCapableBeanFactory. This is how it typically autowires JUnit Test Case Classes because JUnit creates the test case classes.
Yes Spring is not CDI but in theory you could write your own #Configurable for CDI or there is probably a CDI way of doing the above.
That being said the above is sort of a sophisticated feature (and kind of a hack) and as #JanGroth mentioned understaning the lifecycle bean management of the container is critical whether its CDI, Spring, Guice etc.
You may use BeanProvider.injectFields(myObject); from Apache DeltaSpike.
Yes, #Inject works only inside a container because it is done using interceptors on method calls. When the container creates a bean it wrappes it in interceptors which perform injection, and in the case of instantiation using new no interceptors will be called during bean method invocation, and there will be no injection.
Here's the conditions needed for a class to be a managed bean (and hence, for the #Inject annotation to work on its fields/methods):
http://docs.oracle.com/javaee/6/tutorial/doc/gjfzi.html
Related
My app is being deployed on to IBM WebSphere. I have a simple service and I'd like to know how dependency injection works in this case.
// stateless EJB
#Stateless
public class UserService {
private UserDAO userDAO;
// btw, UserDAO is stateless EJB as well
#Inject
public UserService(UserDAO userDAO) {
this.userDAO = userDAO;
}
// biz methods ...
}
It fails with the following error:
[ERROR ] CWWKZ0002E: An exception occurred while starting the
application my-app. The exception message was:
com.ibm.ws.container.service.state.StateChangeException:
com.ibm.ws.cdi.CDIException:
com.ibm.wsspi.injectionengine.InjectionException:
com.ibm.ejs.container.EJBConfigurationException: EJB class
com.demo.app.UserService must have a
public constructor that takes no parameters
I remember there was something in EJB spec that says: the class must have a public constructor that takes no parameters and it makes sense for me that the bean instance is first instantiated by the container and afterward dependency injection is done.
On the other hand, I've found this in WELD docs:
First, the container calls the bean constructor (the default
constructor or the one annotated #Inject), to obtain an instance of
the bean.
And I am confused a little bit, why my EJB cannot be instantiated.
How is the EJB instance being created and dependencies injected when we have constructor injection point?
Any ideas? :)
So what happens is that you do not meet the requirements for initializing EJB beans.
CDI spec has some limitations on constructors - either no-args or one with #Inject.
But there is also this chapter, which specifies that in EE, the set of rules is extended by what EJB session beans require.
And now we are getting into EJB spec which requires a no-arg constructor on a bean.
This should be in chapter Enterprise Bean Class where it states
The class must define a public constructor that takes no arguments.
Now, finally moving on to whether this should work - e.g. can you have an EJB bean using CDI constructor injection?
Well, let's have a look at CDI TCK, a set of tests that all implementation and containers have to pass in order to be able to claim they implement CDI.
There, we can see this bean and this test using it - so yea, this can work, but you need to have both constructors.
EJBs are registered as CDI beans. But first they have to meet the requirements of the EJB spec.
I guess it works just by providing the no-args constructor.
the creation of EJB session beans is done by EJB container but it can choose to use CDI to provide EE resource injection but EJB resolution is delegated to the container
https://docs.jboss.org/weld/reference/2.1.0.Final/en-US/html/ri-spi.html says:
Alternatively, the integrator may choose to use CDI to provide EE
resource injection. In this case, the EE_INJECT environment should be
used, and the integrator should implement the Section A.1.4, “EJB
services”, Section A.1.7, “Resource Services” and Section A.1.5, “JPA
services”.
....
Weld registers resource injection points with
EjbInjectionServices, JpaInjectionServices, ResourceInjectionServices
and JaxwsInjectionServices implementations upfront (at bootstrap).
This allows validation of resource injection points to be performed at
boot time rather than runtime
if you interested in how CDI and EJB are integrated. you can have a look at the code of weld-EJB module and weld-integration(glassfish code)
I have some legacy code where the class is a Spring bean defined and initialized through xml. It is a singleton with a field member which is a class dependency. There is a setter method for it, so I am assuming its supposed to be set via Spring, although I didn't find any xml defining it. There is also a get() method for the dependency, it has a null check and if its null it manually creates it outside of Spring like so
Class Test{
Dependency d;
setD(Dependency d){this.d=d;}
getD(){
if(this.d==null){
this.d = new Dependency();
}return this.d
}
}
I am trying to understand why this Spring bean is initializing a dependency outside of Spring and what are the implications if any, is this just bad/old design? or Am I not understanding something with how Spring works.
I would say it's a bad design, probably author wanted to provide fall-back for the case when D was not injected in spring. Another idea is an attempt to make D a lazy dependency. You should explore what's inside D.
Generally you can use #Required to mark the members that always should be injected. Or just use simple and nice constructor injection. If you're concerned about lazy injection, that's how Spring works by default.
I am newbie in cdi and these are my first steps.
I have a bean in ejb module:
#Stateless
public class TestBean {
public String getIt(){
return "test";
}
}
I have a POJO in war module (I tried with #EJB and #Inject - same result)
public class SaveAction extends Action{
#EJB
private TestBean bean;
#Override
public void execute(){
....
String test = bean.getIt(); //HERE I GET java.lang.NullPointerException
...
}
}
Both war and ejb are inside ear. In log I see
EJB5181:Portable JNDI names for EJB TestBean:
[java:global/example.com/my-ejb/TestBean!com.example.TestBean,
java:global/example.com/my-ejb/TestBean]]]
From that I conclude that bean is initialized - but I can't find it. What am I doing wrong?
CDI and other dependency injection containers don't use magic! It's just ordinary java code that cannot do more or less than any other java code written anywhere. So it is impossible for a framework to do injection when an object is instantiated directly via new:
SaveAction action = new SaveAction();
// don't expect any injection has happened - it can't! no magic!
// action.bean is still null here!
The framework does not have any idea that an object like SaveAction has been instantiated. (Therefore it would be necessary to somehow inform the framework about the newly created object - but neither the constructor nor the 'new' statement do this! Just think one minute about how you would write such a framework code! It's not possible!* ).
To make injection work, the object must be created by the container instead! Otherwise it is NOT managed! (See also chapter 3.7 of the Web Beans specification (JSR 299)).
The best way to do this is to let the container inject the object into another already managed bean. It seems this just deferes the problem, but there are some already managed beans in your application, like the servlet!
Suggestion: Make your SaveAction CDI aware (e.g. annotate it with #Default) and let it be injected into your servlet!
Tutorials:
http://middlewaremagic.com/jboss/?p=1063
http://hudson.jboss.org/jenkins/job/JBoss-AS7-Docs/lastSuccessfulBuild/artifact/guides/developer-getting-started-guide/target/docbook/publish/en-US/html/helloworld.html
*) In theory it should be possible using aspect oriented programming or instrumentation to manipulate the constructors of beans to notify the container if they are invoked. But that's a very complex concept with many unsolved issues I think.
I have the following configuration:
1 EAR on one GF containing 2 EJB-JARs with EJB components.
1 WAR on another Glassfish server (=> other JVM) containing web components accessing the EJB components.
I have 2 EJB business services in each EJB-JAR of my EAR, and they are all developped like this:
#Remote
public interface ServiceAItf {
...
}
#Stateless
#Local
public class ServiceAImpl implements ServiceAItf {
...
}
In my WAR, I access the EJB components via an explicit "InitialContext.lookup" on the remote interface.
In my EAR, I am quite confused about the best practice for injection, in terms of performance, architecture, and so on...
I have the following questions:
As you can see, I have declared the annotation "#Local" on the service implementation without defining the local interface. Is it correct? At least I have no error at deployment. But maybe I should use the "#LocalBean" annotation instead? I suppose that the "#LocalBean" annotation simply allows to invoke the implementation directly as a "Local" EJB, but you have to use the implementation in your code like this:
#Stateless
#Local
public class ServiceBImpl implements ServiceBItf {
#EJB
private ServiceAImpl serviceA;
...
}
What is the best way to inject one EJB into another one?
It works like this:
#Stateless
#Local
public class ServiceBImpl implements ServiceBItf {
#EJB
private ServiceAItf serviceA;
...
}
But from what I noticed, the "serviceA" injected is the remote proxy, while it is in the same JVM within the same EAR file. So I suppose that there will be an impact on the performance. That's why I have tried to inject the service like this:
#Stateless
#Local
public class ServiceBImpl implements ServiceBItf {
#Inject
private ServiceAItf serviceA;
...
}
But it doesn't work in GF, I have the following exception:
WELD-001408 Unsatisfied dependencies for type [...] ...
I then tried to create a local interface, and the injection via the annotation "#Inject" works when both services
Even if I create a local interface like this, the service is not injected via the annotation "#Inject" but is null:
#Local
public interface ServiceALocalItf {
...
}
I read a lot of articles where it is highly recommended to use "#Inject" instead of "#EJB" when it is for a local invocation. That leads me to the following question: in which case the "#Local" EJB invocation is recommended (or simply used)?
After all this analysis, I come to the following conclusion:
For each service, I create a "#Local" and a "#Remote" interface.
From WAR to EJB-JAR of EAR, make a JNDI lookup to the remote interface.
From EJB-JAR to EJB-JAR, make an injection via "#EJB" to the local interface.
For two services within the same EJB-JAR, make an injection via "#Inject" to the local interface.
What do you think about it? Is it correct?
As you can see, I have declared the annotation "#Local" on the service
implementation without defining the local interface. Is it correct?
With EJB 3.1, the requirement for local interfaces was dropped. No need to write them unless you explicitly need them.
What is the best way to inject one EJB into another one?
Couple of things to write here:
With Java EE 6, Java Enterprise has changed. A new JSR defines a so-called managed bean (don't confuse with JSF managed beans) as a sort of minimum component that can still benefit from the container in terms of dependency injection and lifecycle management. This means: If you have a component and "just" want to use DI and let the container control its lifecycle, you do not need to use EJBs for it. You'll end up using EJBs if - and only if - you explicitly need EJB functionality like transaction handling, pooling, passivation and clustering.
This makes the answer to your question come in three parts:
Use #Inject over #EJB, the concept of CDI (a) works for all managed
beans (this includes EJBs) and (b) is stateful and therefore far
superior over pure #EJB DI
Are you sure that you need EJBs for your
components?
It's definitely worthwhile to have a look at the CDI
documentation
#Inject annotation is used for java beans(POJOs) while #EJB annotation is used for enterprise java beans. When a container inject an ejb provided by #EJB annotation to another bean it also controls that ejb's lifecycle, performs pooling for stateless beans and so on(when bean which is going to be injected is not deployed the reference will be null). If you use #Inject annotation CDI mechanism simply find and create an instance of injected resource like with new operator(if the implementation of interface which is going to be injected doesn't exist the reference will be null). You can use qualifiers with #Inject annotation to choose different implementation of injected interface.
I'm an end-user of one of my company's products. It is not very suitable for integration into Spring, however I am able to get a handle on the context and retrieve the required bean by name. However, I would still like to know if it was possible to inject a bean into this class, even though the class is not managed by Spring itself.
Clarification: The same application which is managing the lifecycle of some class MyClass, is also managing the lifecycle of the Spring context. Spring does not have any knowledge of the instance of MyClass, and I would like to some how provide the instance to the context, but cannot create the instance in the context itself.
You can do this:
ApplicationContext ctx = ...
YourClass someBeanNotCreatedBySpring = ...
ctx.getAutowireCapableBeanFactory().autowireBeanProperties(
someBeanNotCreatedBySpring,
AutowireCapableBeanFactory.AUTOWIRE_AUTODETECT, true);
You can use #Autowired and so on within YourClass to specify fields to be injected etc.
One way to bring a bean into Spring despite its manufacture being external is to use a helper class marked as a #Configuration bean that has a method (marked with #Bean) that actually makes the instance and hands it back through Spring (which does its property injection and proxy generation at that point).
I'm not quite sure what scope you need; with prototype, you'll get a fresh bean in each place.
#Configuration
public class FooBarMaker {
#Bean(autowire = Autowire.BY_TYPE)
#Scope("prototype")
public FooBar makeAFooBar() {
// You probably need to do some more work in here, I imagine
return new FooBar();
}
}
You can inject properties required for manufacture into the #Configuration bean. (I use this to create instances of an interface where the name of the class to instantiate is defined at runtime.)
suppose that u have the following dependency chain:
A --> B --> C --> x --> y -- > Z
A, B, C are spring managed beans (constructed and manged by spring framework)
x, y are really simple POJOs that constructed by your application, without spring assistance
now if you want that y will get a reference to Z using spring that you need to have a 'handle' to the spring ApplicationContext
one way to do it is to implement ApplicationContextAware interface . In this case I would suggest that either A, B or C will implement this interface and will store the applicationContext reference in a static member.
so lets take Class C for example:
class C implmenets ApplicationContextAware{
public static ApplicationContex ac;
void setApplicationContext(ApplicationContext applicationContext) {
ac = applicationContext;
}
.............
}
now, in class y you should have:
(Z)(C.ac.getBean("classZ")).doSomething()
HTH -- Yonatan
Another way to do this is to us use AspectJ. This is the recommended way of injection Spring beans into non-managed objects that are created with the new operator. See this for details:
http://www.javacodegeeks.com/2011/02/domain-driven-design-spring-aspectj.html
Searching endless combos of autowire inject spring bean into pojo applicationcontextaware beanaware etc circled me back here but this didnt provide a complete enough solution for me.
This is a much better implementation/tutorial of this IMO:
I hope it helps everyone like it finally helped me.
Accessing Spring Beans from outside Spring Context
Be careful that in oldest version of Spring, there is thread-safe problem with bean factory http://jira.springframework.org/browse/SPR-4672
If you want to create an object outside the Spring context, and make that object available for injection into other beans that are in the Spring context, you can follow the steps in this article.
Basically, you create a parent application context and push your external object into this parent context as a singleton. Then you create you main application context (for example, from xml files), with the parent application context as its parent.
Object externalObject = ...
GenericApplicationContext parent = new StaticApplicationContext();
parent.getBeanFactory().registerSingleton( "externalObject", externalObject );
parent.refresh();
ApplicationContext appContext = new ClassPathXmlApplicationContext( ... , parent);
From a Spring configuration class, set a static field on the non-Spring class that needs the beans.
I have an example in my answer to a Liquibase question: https://stackoverflow.com/a/71191546/5499391