The following code works fine in spring 4, but I am wondering why the getBean(FooService.class) returns an already-loaded bean. I thought the sequence of bean loading is not guaranteed, meaning that it is possible to get a null bean. Is it because the loading target is a class not a String (ie. object) or is it because the FooService bean has a special scope, like prototype? If so, what is the difference between getBean(class) and getBean(object)
public abstract class AbstractService implements ApplicationContextAware {
protected ApplicationContext applicationContext;
protected FooService fooService;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
#PostConstruct
protected void postConstruct() {
fooService = applicationContext.getBean(FooServiceImpl.class);
}
ApplicationContext::getBean method creates the bean of the specified type if it has not been created already.
For the following two bean classes:
#Component
public class Bean1 {
#Autowired
private ApplicationContext applicationContext;
public Bean1() {
System.out.println("Bean 1 constructor");
}
#PostConstruct
public void init() {
System.out.println("Bean 1 #PostConstruct started");
applicationContext.getBean(Bean2.class);
System.out.println("Bean 1 #PostConstruct completed");
}
}
#Component
public class Bean2 {
#Autowired
private ApplicationContext applicationContext;
public Bean2() {
System.out.println("Bean 2 constructor");
}
#PostConstruct
public void init() {
System.out.println("Bean 2 #PostConstruct started");
applicationContext.getBean(Bean1.class);
System.out.println("Bean 2 #PostConstruct completed");
}
}
the printed output during context initialization is:
Bean 1 constructor
Bean 1 #PostConstruct started
Bean 2 constructor
Bean 2 #PostConstruct started
Bean 2 #PostConstruct completed
Bean 1 #PostConstruct completed
As for different getBean methods, if you pass in a class, then exactly one bean of that class has to be present in the application context (otherwise Spring would not not which of multiple bean instances of that class you ask for), whereas searching by name allows you to get a specific named bean instance.
For starters, String is a full rights class, like any you can create yourself.
The reason you're getting something in fooService, is that the ApplicationContext getBean method is able to retrieve a managed bean according to the argument you're passing to it.
If it weren't possible to retrieve the bean, you could get some of these exceptions:
Throws: NoSuchBeanDefinitionException - if no bean of the given type
was found NoUniqueBeanDefinitionException - if more than one bean of
the given type was found BeansException - if the bean could not be
created
Related
Our existing Spring Boot integration setup was using #DirtiesContext to rebuild the entire bean pool in-between different test methods.
This was fairly slow, and so we started working with beans that could be "refreshed" or torn down/rebuild internally without re-creating the instance.
The problem is that only some beans support this. If we control UsersBean, we can implement a UsersBean.refresh() method and call it in our #After method.
But if we have existing beans/classes that don't support refreshing, or we don't control, how can we conditionally indicate that certain beans need to be dirtied/rebuilt after a specific test?
Or more succinctly: Is there a way to mark as dirty a subsection of your bean pool, for rebuilding, at the end of a test method?
It looks like this is possible, at least within a Spring Boot environment. The ApplicationContext implementation there is a GenericApplicationContext which has the ability to removeBeanDefinition(), which can then be re-registered via registerBeanDefinition().
This even cascades through to remove beans that hold a reference to the bean that's being removed (the implementation of this can be seen in DefaultSingletonBeanRegistry.destroyBean()).
For example if Bean1 is referenced by Bean2 :
#Component
public class Bean1 {
}
#Component
public class Bean2 {
#Autowired
public Bean1 bean1;
}
Then a test can remove bean1 from the context, and see bean2 replaced as well:
#RunWith(SpringRunner.class)
public class BeanRemovalTest implements ApplicationContextAware {
#Autowired
private Bean1 bean1;
#Autowired
private Bean2 bean2;
private ApplicationContext applicationContext;
#Test
public void test1() throws Exception {
System.out.println("test1():");
System.out.println(" bean1=" + bean1);
System.out.println(" bean2.bean1=" + bean2.bean1);
resetBean("bean1");
}
#Test
public void test2() throws Exception {
System.out.println("test2():");
System.out.println(" bean1=" + bean1);
System.out.println(" bean2.bean1=" + bean2.bean1);
}
private void resetBean(String beanName) {
GenericApplicationContext genericApplicationContext = (GenericApplicationContext) applicationContext;
BeanDefinition bd = genericApplicationContext
.getBeanDefinition(beanName);
genericApplicationContext.removeBeanDefinition("bean1");
genericApplicationContext.registerBeanDefinition("bean1", bd);
}
#Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.applicationContext = applicationContext;
}
}
This shows both bean instances being replaced:
test1():
bean1=hello.so.Bean1#61d6015a
bean2.bean1=hello.so.Bean1#61d6015a
test2():
bean1=hello.so.Bean1#2e570ded
bean2.bean1=hello.so.Bean1#2e570ded
(If the resetBean("bean1") is commented out, it is the same instance both times round).
There are bound to be edges where this doesn't work out - e.g. if another bean is holding onto a reference obtained from ApplicationContext.getBean().
I present two scenario, one with a bean registered using the bean factory and the other with the bean created from auto-scanning the packages for annotated definitions (e.g #Component). The bean class listens to the ContextRefreshedEvent using a method annotated with #EventListener and also with #Async so that it is called asynchronously.
Scenario 1
A singleton of class BlockingListener is created and registered with the bean factory. This is done at the initialisation of another Bean as described below in the method afterPropertiesSet. The ContextRefreshedEvent is received but doesn't exit, and thus the Application doesn't start. It remained blocked.
#EnableAsync
#EnableScheduling
#EnableAutoConfiguration
#SpringBootApplication
public class SampleApp implements InitializingBean {
private final DefaultListableBeanFactory beanFactory;
#Autowired
public SampleApp(DefaultListableBeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
#Override
public void afterPropertiesSet() {
String beanName = "blocking-listener";
Object bean = new BlockingListener();
beanFactory.registerBeanDefinition(beanName, rootBeanDefinition(bean.getClass()).getBeanDefinition());
beanFactory.registerSingleton(beanName, bean);
}
public static void main(final String... args) {
SpringApplication.run(SampleApp.class, args);
}
public static class BlockingListener {
#Async
#EventListener(ContextRefreshedEvent.class)
void block() throws InterruptedException {
Thread.sleep(Long.MAX_VALUE);
}
}
}
Scenario 2
The class BlockingListener is annotated with #Component and the bean is auto-detected and created. The ContextRefreshedEvent is received but doesn't exit, and yet the Application starts.
#EnableAsync
#EnableScheduling
#EnableAutoConfiguration
#SpringBootApplication
public class SampleApp {
private final DefaultListableBeanFactory beanFactory;
#Autowired
public SampleApp(DefaultListableBeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
public static void main(final String... args) {
SpringApplication.run(SampleApp.class, args);
}
#Component
public static class BlockingListener {
#Async
#EventListener(ContextRefreshedEvent.class)
void block() throws InterruptedException {
Thread.sleep(Long.MAX_VALUE);
}
}
}
The expected behaviour is with the second scenario since the ContextRefreshedEvent is expected to be published after the context is successfully started. However I can't figure out why the bean registered dynamically with the bean factory received the event before the context started and why it blocked the context from starting up.
In scenario 1 the call of block() does not happen asynchronously because the annotation #Async doesn't become effective.
#Async is working through a BeanPostProcessor, namely AsyncAnnotationBeanPostProcessor that is wrapping the instance with a proxy. But when you manually add a bean to the bean factory, the post processors are not applied.
What you can do in the given setting, is applying the post processors manually as:
bean = beanFactory.initializeBean(bean, beanName);
beanFactory.registerSingleton(beanName, bean);
I have a question about auto-wiring order and #PostConstruct logic in Spring. For example following demo code I have a main Spring Boot class:
#SpringBootApplication
public class Demo1Application {
#Autowired
BeanB beanb;
public static void main(String[] args) {
SpringApplication.run(Demo1Application.class, args);
}
}
and 2 #Service Definitions:
#Service
public class BeanB {
#Autowired
private BeanA beana ;
#PostConstruct
public void init(){
System.out.println("beanb is called");
}
public void printMe(){
System.out.println("print me is called in Bean B");
}
}
#Service
public class BeanA {
#Autowired
private BeanB b;
#PostConstruct
public void init(){
System.out.println("bean a is called");
b.printMe();
}
}
and I have the following output:
bean a is called
print me is called in Bean B
beanb is called
My question is how autowiring takes place step by step like a scenario above?
And how printMe() method of beanb is called without calling its #PostConstruct first?
Below should be possible sequence
beanb starts to get autowired
During class initialization of Beanb, beana starts to get autowired
Once beana gets created the #PostConstruct i.e. init() of beana gets called
Inside init(), System.out.println("bean a is called"); gets called
Then b.printMe(); gets called causing System.out.println("print me is called in Bean B"); to execute
Having the beana completed the #PostConstruct i.e. init() of beanb gets called
Then System.out.println("beanb is called"); gets called
Ideally the same can be better observed by a debugger in eclipse.
The Spring reference manual explains how circular dependencies are resolved. The beans are instantiated first, then injected into each other.
Your Answer is Correct as you shown in Your question.
Now Getting the concept of Notation #Autowired. All #Autowired Objects are initialized and loaded in memory just after class Loading is done.
Now here is your SpringBootApplication
#SpringBootApplication
public class Demo1Application {
#Autowired
BeanB beanb; // You are trying to autowire a Bean class Named BeanB.
Here at above Console Application that you have write try to autowire and inject a object of type BeanB.
Now here is your definition of BeanB
#Service
public class BeanB {
#Autowired
private BeanA beana ;
In BeanB class you are trying to inject the Object of Class BeanA which is also defined in your console Project.
So, In Your Demo1Application to inject a Object of Class BeanB there must need to inject a Object of class BeanA.
Now BeanA Class Object is Created First.
Now if you see the definition of Your Class BeanA
#Service
public class BeanA {
#Autowired
private BeanB b;
#PostConstruct // after Creating bean init() will be execute.
public void init(){
System.out.println("bean a is called");
b.printMe();
}
}
So, After injecting the Object BeanA method bind with #PostContruct annotation is going to execute.
So, execution flow will be..
System.out.println("bean a is called");
System.out.println("print me is called in Bean B");
System.out.println("beanb is called");
It will show class not found exception as class A depends on class B. and class B depends on class A.
I have a Spring 3.0.7 application, in which I have to initialize some beans in Java rather than xml. I'm trying to figure out why the beans I initialize can't be retrieved from the application context.
When the below code executes, I get "org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'beanName' is defined" when I call beanFactory.getBean("beanName").
public class BeanToCreateBeans implements ApplicationContextAware {
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
AutowireCapableBeanFactory beanFactory = applicationContext.getAutowireCapableBeanFactory();
BeanObject bean = new BeanObject();
beanFactory.initializeBean(bean, "beanName");
System.out.println("bean in the application: " + beanFactory.getBean("beanName"));
}
}
Why can't the bean be retrieved after creating it? I need to be able to create the bean and add it to the application context.
Update: As the answer states, my code isn't actually registering the bean, which answers the question I posed. So, in order to make my code work the way I wanted, it looks like:
public class BeanToCreateBeans implements BeanDefinitionRegistryPostProcessor, ApplicationContextAware {
private ApplicationContext applicationContext;
#Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry bdr) throws BeansException {
BeanDefinition definition = new RootBeanDefinition(BeanObject.class);
bdr.registerBeanDefinition("beanName", definition);
AutowireCapableBeanFactory beanFactory = applicationContext.getAutowireCapableBeanFactory();
BeanObject bean = new BeanObject();
beanFactory.initializeBean(bean, "beanName");
System.out.println("bean in the application: " + beanFactory.getBean("beanName"));
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
AutowireCapableBeanFactory#initializeBean(Object, String) doesn't add a bean to a context or bean factory. The javadoc states
Initialize the given raw bean, applying factory callbacks such as
setBeanName and setBeanFactory, also applying all bean post processors
(including ones which might wrap the given raw bean).
It basically just processes the object you pass to it as if it was a bean initialized as part of the ApplicationContext. Notice that the method returns an Object, the result of the initialization.
Returns:
the bean instance to use, either the original or a wrapped one
I am developing a SpringBoot project and I want to get the bean by its name using applicationContext. I have tried many solution from web but could not succeed. My Requirement is that I have a controller
ControllerA
and inside the controller I have a method getBean(String className). I want to get instance of registered bean. I have hibernate entities and I want to get an instance of the bean by passing the name of class only in getBean method.
Please help if someone know the solution.
You can Autowire the ApplicationContext, either as a field
#Autowired
private ApplicationContext context;
or a method
#Autowired
public void context(ApplicationContext context) { this.context = context; }
Finally use
context.getBean(SomeClass.class)
You can use ApplicationContextAware.
ApplicationContextAware:
Interface to be implemented by any object that wishes to be notified
of the ApplicationContext that it runs in. Implementing this interface
makes sense for example when an object requires access to a set of
collaborating beans.
There are a few methods for obtaining a reference to the application context. You can implement ApplicationContextAware as in the following example:
package hello;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
#Component
public class ApplicationContextProvider implements ApplicationContextAware {
private ApplicationContext applicationContext;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public ApplicationContext getContext() {
return applicationContext;
}
}
Update:
When Spring instantiates beans, it looks for ApplicationContextAware implementations, If they are found, the setApplicationContext() methods will be invoked.
In this way, Spring is setting current applicationcontext.
Code snippet from Spring's source code:
private void invokeAwareInterfaces(Object bean) {
.....
.....
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware)bean).setApplicationContext(this.applicationContext);
}
}
Once you get the reference to Application context, you get fetch the bean whichever you want by using getBean().
actually you want to get the object from the Spring engine, where the engine already maintaining the object of your required class at that starting of the spring application(Initialization of the Spring engine).Now the thing is you just have to get that object to a reference.
in a service class
#Autowired
private ApplicationContext context;
SomeClass sc = (SomeClass)context.getBean(SomeClass.class);
now in the reference of the sc you are having the object.
Hope explained well. If any doubt please let me know.
Even after adding #Autowire if your class is not a RestController or Configuration Class, the applicationContext object was coming as null. Tried Creating new class with below and it is working fine:
#Component
public class SpringContext implements ApplicationContextAware{
private static ApplicationContext applicationContext;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws
BeansException {
this.applicationContext=applicationContext;
}
}
you can then implement a getter method in the same class as per your need to get the bean. Like:
applicationContext.getBean(String serviceName,Interface.Class)
Using SpringApplication.run(Class<?> primarySource, String... arg) worked for me. E.g.:
#SpringBootApplication
public class YourApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(YourApplication.class, args);
}
}
As an alternative approach you can use ConfigurableApplicationContext to get bean of any class which is annotated with #Component, #Repository or #Service.
Let's say you want to get a bean of the class BaseComponent :
#Service
public class BaseComponent {
public String getMessage() {
return "hello world";
}
}
Now you can use ConfigurableApplicationContext to get the bean:
#Component
public class DemoComponent {
#Autowired
ConfigurableApplicationContext applicationContext;
public BaseComponent getBeanOfBaseComponent() {
return applicationContext.getBean(BaseComponent.class);
}
}
You can use the ApplicationContextAware class that can provide the application context.
public class ApplicationContextProvider implements ApplicationContextAware {
private static ApplicationContext ctx = null;
public static ApplicationContext getApplicationContext() {
return ctx;
}
#Override
public void setApplicationContext(final ApplicationContext ctx) throws BeansException {
ApplicationContextProvider.ctx = ctx;
}
/**
* Tries to autowire the specified instance of the class if one of the specified
* beans which need to be autowired are null.
*
* #param classToAutowire the instance of the class which holds #Autowire
* annotations
* #param beansToAutowireInClass the beans which have the #Autowire annotation
* in the specified {#classToAutowire}
*/
public static void autowire(Object classToAutowire, Object... beansToAutowireInClass) {
for (Object bean : beansToAutowireInClass) {
if (bean == null) {
ctx.getAutowireCapableBeanFactory().autowireBean(classToAutowire);
}
}
}
}
If you are inside of Spring bean (in this case #Controller bean) you shouldn't use Spring context instance at all. Just autowire className bean directly.
BTW, avoid using field injection as it's considered as bad practice.
One API method I use when I'm not sure what the bean name is org.springframework.beans.factory.ListableBeanFactory#getBeanNamesForType(java.lang.Class<?>). I simple pass it the class type and it retrieves a list of beans for me. You can be as specific or general as you'd like to retrieve all the beans associated with that type and its subtypes, example
#Autowired
ApplicationContext ctx
...
SomeController controller = ctx.getBeanNamesForType(SomeController)
Easy way in configration class call the BEAN annoted method . Yes u heard it right---- :P calling SpringBoot #Bean annoted method return the same bean from config .I was trying to call a logout in #predestroy method in config class from a bean and direcltly called the method to get the same bean .
P.S. : I added debug in the #bean annotated method but it didn't entered the method even when i called it.Sure to blame -----> Spring Magic <----
You can use ServiceLocatorFactoryBean. First you need to create an interface for your class
public interface YourClassFactory {
YourClass getClassByName(String name);
}
Then you have to create a config file for ServiceLocatorBean
#Configuration
#Component
public class ServiceLocatorFactoryBeanConfig {
#Bean
public ServiceLocatorFactoryBean serviceLocatorBean(){
ServiceLocatorFactoryBean bean = new ServiceLocatorFactoryBean();
bean.setServiceLocatorInterface(YourClassFactory.class);
return bean;
}
}
Now you can find your class by name like that
#Autowired
private YourClassfactory factory;
YourClass getYourClass(String name){
return factory.getClassByName(name);
}
Just use:
org.springframework.beans.factory.BeanFactory#getBean(java.lang.Class)
Example:
#Component
public class Example {
#Autowired
private ApplicationContext context;
public MyService getMyServiceBean() {
return context.getBean(MyService.class);
}
// your code uses getMyServiceBean()
}