Injecting beans into a class outside the Spring managed context - java

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

Related

How can I autowired attributes of object which I created manually?

I have class which I created manually using new, because I needed to pass it some objects (not beans). It has 2 objects tough which I want to be autowired by spring. This is my class:
#Component
#Scope("prototype")
public class DayLayout extends VerticalLayout {
#Autowired
private SchedulingService schedulingService;
#Autowired
private GeneralService generalService;
.
.
.
}
But after creation of the class those objects are still null. I think it is because I have not obtained that bean via spring container. But is there any way how can I create object manually and all it's objects will be still autowired ?
So if you need to inject Autowired properties to an object created via new you could do the following:
DayLayout dl = new DayLayout(<whatever parameters go here>);
ctx.getAutowireCapableBeanFactory().autowireBean(dl); // Where ctx is Spring's application context
But if you need to do such things I think you might rethink what you are actually doing in your application.
Assuming that GeneralService is not a class annotated with #Component or other Spring stereotypes annotations: yes, there is.
#Configuration
public class ConfigClasses{
#Bean
public GeneralService generalService(){
return new GeneralService();
}
}
Obviously the same for SchedulingService, just add another method which produces that class.
I think you are misunderstanding some part of managed objects, but I am not sure what part that is.
Since you annotated the bean as #Prototype I assume you realize Spring will instantiate a new instance for you every time you request one. It would then be a trivial matter to call setters for your non-managed objects. You could even add a belt and suspenders approach and have your bean throw a IllegalStateException if the setters have not been called.
#Ivan is exactly correct on how you can manually request a bean. He is also exactly correct that if you are resorting to that your design is probably not the best.
If you do : Object obj = new Object(); , it won't autowire. If you want to create manually your object, you can use a config file with #Configuration and return a #Bean. https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/Configuration.html . If that is not what you want, can you provide how you create those two objects ? What do you pass to them ?

Is Spring bean just a shared object

I am trying to understand purpose of Spring-created beans. Are they just global shared object (such that they are declared like
#Component
public class MySpringBean{},
and later this object is used anywhere like inside some class
public class MyClass {
#Autowired
MySpringBean mySpringBean;
}
)?
Can their internal creation/implementation assumed like this? -
public class MyApp {
MySpringBean mySpringBean;
}
and used in MyClass like -
public class MyClass {
MySpringBean mySpringBean = MyApp.mySpringBean;
}
Its the object valid for only that class hierarchy. In your case Spring just create an object for mySpringBean and will keep it available for MyClass. Internally its more like
MySpringBean mySpringBean = new MySpringBean()
But actually
all Spring beans are managed - they "live" inside a container, called "application context".
Autowiring happens by placing an instance of one bean into the desired field in an instance of another bean. Both classes should be beans, i.e. they should be defined to live in the application context.
so in your case both mySpringBean and an instance of MyClass will be in application context.
Based on your question, I believe you should know about how beans are managed by Spring (or how Spring manages the life cycle of beans from initialization to destroy). But also note that you don't have to go into too much of details (wells, it's a framework that's is providing you). Yes, it's definitely true to init involves using new operator. These objects live inside the Container and Spring wires them whenever it's called for. Since beans are managed by Spring, you can implement callback methods too.

How to create a bean by type in Spring?

In my ApplicationContext I have several Beans being created the same style. So I have a lot of dublicated code writing a FactoryBean for each of this beans. Those beans have a common ground, implementing all one special interface.
I would like to move all that bean creation to one factory. That one would have to provide a methode like this
<T extends CommonInterface> T createInstance(Class<T> clazz);
There I could implement all the instantiation necessary to create one of my special beans.
My implementation would be called by spring for
#Autowired
private MyCommonInterfaceImplementation impl;
in that way
createInstance(MyCommonInterfaceImplementation.class)
So far I looked at BeanFactory and FactoryBean, both seem not to be I'm searching for.
Any suggestions?
why not use #bean
#Bean
public MyCommonInterfaceImplementation getMyCommonInterfaceImplementation(){
return MyBeanFactory.createInstance(MyCommonInterfaceImplementation.class);
}
//should autowire here
#Autowired
private MyCommonInterfaceImplementation impl;
Basically you need the #Bean annotation on a "factory" only if you need some special handling during the creation of a bean.
If everything can be #Autowired, either by setters, fields, or one constructor, and nothing else needs to be done on a bean during initialization, you can simply declare the annotation #Component on each implementation of your interface. This works as long as you have component scanning active inside your application. The result will be that for each component spring will create a bean which you can use.
I'm writing this on a mobile so showing code is not the best. Just follow some tutorial on #ComponentScan, or if you need, let me know and I can augment this answer with an example.
As of Spring 4.3 you no longer have to annotate your bean classes and you can let them be instantiated via a componentscan.
#Configuration
#ComponentScan(
value = "some.package.path",
includeFilters = {
#Filter(type = ASSIGNABLE_TYPE, value = {
MyClass1.class,
MyClass2.class,
MyClass3.class
})
})
This actually creates beans for the three classes listed there. The example should work without filters as well (everything in the package becomes a bean). This works as long as the classes have a single constructor that can be used for autowiring. I don't think it is possible to filter for all implementations of a particular interface and then register a bean.
To do that, you might do something with a ContextListener and e.g. use reflection to find out what classes to instantiate and then use context.autowire(..) to inject any dependencies from your context. A bit hacky but it might work.
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
ApplicationContext context = event.getApplicationContext();
MyClass bean
= context
.getAutowireCapableBeanFactory()
.autowire(MyClass.class, Autowire.BY_NAME.value(), true);
...
}
That still leaves the problem of how to get the bean registered in the context of course.
You might also be able to adapt the answer to this SO question on how to add beans programmatically.
Finally the best approach I've found is using a ConfigurationClassPostProcessor. As example I've used https://github.com/rinoto/spring-auto-mock
But, since it is quite complicated and "too much magic" to create beans from nothing, we decided to explicitly create those beans via #Bean.
Thanks for your answers.

AnnotationConfigApplicationContext has not been refreshed yet

I am developing a spring MVC application. When I try to use AnnotationConfigApplicationContext in my controller class I am getting the following error. I have no idea what this statement exactly means.
#RequestMapping(value = "/generate", method = RequestMethod.POST)
public ModelAndView generateMappingFile(#ModelAttribute Mapping mapping)
{
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
MappingFileGenerator mfg = ctx.getBean(MappingFileGenerator.class);
}
Error Message -->
java.lang.IllegalStateException:org.springframework.context.annotation.AnnotationConfigApplicationContext#116b3c0 has not been refreshed yet
Can someone explain me what went wrong here ? I am using Spring 4.0.1.. I am new to spring mvc.
When you are creating a new instance of an ApplicationContext (regardless which type) you are basically creating new instances of each and every bean configured in that ApplicationContext. That is nice the first time, it might work the second and depending on the amount of beans, the type of beans will crash after that. As the context will never be destroy (until the app crashed and is restarted) you will run into possible memory issues, performance issues, strange transactional problems etc.
A general rule of thumb is to never construct a new instance of an ApplicationContext but to use dependency injection instead.
If you really want access to the ApplicationContext put a field of that type in your controller and put #Autowired on it.
#Controller
public class MyController {
#Autowired
private ApplicationContext ctx;
….
}
Then you can do a lookup for the bean you need in the method. This can be handy if you use the ApplicationContext as a factory for your beans. If all the beans you need are singletons it is better to simply inject the bean you need.
#Controller
public class MyController {
#Autowired
private MappingFileGenerator mfg ;
….
}
Now Spring will inject the MappingFileGenerator and it is available for use in your methods. No need to create a new instance of an ApplicationContext.
More information is in the Spring Reference Guide.
#M.Deinum's comment will get quite a few more upvotes.
Think of creating a new ApplicationContext as instantiating a new (instance of an) application. Do you want to do that every time this (or any other method in said application) is called? No, you don't.
I'm guessing you think you do because you need access to your ApplicationContext in this method. To do that - i.e. to get access to the running application context (rather than creating a new one), you want to do
#Controller // or #Service / #Component / ... : tells Spring that this is a bean, and to inject the specified dependencies
class YourClass {
#Autowired // tells Spring that this object is a dependency should should be injected
ApplicationContext ctx;
#RequestMapping(value = "/generate", method = RequestMethod.POST)
public ModelAndView generateMappingFile(#ModelAttribute Mapping mapping) {
MappingFileGenerator mfg = ctx.getBean(MappingFileGenerator.class);
}
The key here is the Autowired annotation, which tells Spring to inject the annotated object as a dependency.
I highly suggest following the links I've included (for starters), as what you're doing here suggests pretty strongly that you haven't wrapped your head around what DI is and does for you, and until you do, using it is likely to be counterproductive toward it's own ends for you.
In case it helps someone, i was having this issue on a new URL Mapping added to a gradle project, i was missing the first slash of the url and that causing this "illegalstate not refreshed yet" on my tests

#Inject only working for POJOs created by CDI container?

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

Categories

Resources