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
Related
On application close, each singleton bean is destroyed by DefaultListableBeanFactory.destroySingletons().
If the bean has a public void no-arg method called shutdown, then it will be invoked.
I have some 3rd party beans, all of which implement a certain interface, let's call it DoNotDestroy, that I do not want Spring to destroy.
I can specify a blank string destroy method on each bean, like so:
#Bean(destroyMethod = "")
public MyBean myBean() {
return new MyBean();
}
However, I would prefer to somehow configure Spring to not destroy any beans that implement DoNotDestroy. Is there a good way to do this?
Rather than blanking out the destroy method in each #Bean method, I can implement a BeanFactoryPostProcessor to do the same thing:
#Component
public class DoNotDestroyPostProcessor implements BeanFactoryPostProcessor {
private final Logger log = LoggerFactory.getLogger(DoNotDestroyPostProcessor.class);
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
String[] doNotDestroys = beanFactory.getBeanNamesForType(DoNotDestroy.class);
for (String doNotDestroy : doNotDestroys) {
BeanDefinition bean = beanFactory.getBeanDefinition(doNotDestroy);
log.info("Don't destroy bean {} {}", bean.getFactoryMethodName(), bean.getDestroyMethodName());
bean.setDestroyMethodName("");
}
}
}
This avoids the problem of someone adding a bean and forgetting to blank out the destroy method.
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
In spring, if a bean implements ApplicationContextAware, then it is able to access the applicationContext. Therefore it is able to get other beans.
e.g.
public class SpringContextUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
public void setApplicationContext(ApplicationContext context) throws BeansException {
applicationContext = context;
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
}
Then SpringContextUtil.getApplicationContext.getBean("name") can get the bean "name".
To do this, we should put this SpringContextUtil inside the applications.xml, e.g.
<bean class="com.util.SpringContextUtil" />
Here the bean SpringContextUtil doesn't include the property applicationContext. I guess when spring bean initialize, this property is set. But how is this done? How does the method setApplicationContext get called?
When spring instantiates beans, it looks for a couple of interfaces like ApplicationContextAware and InitializingBean. If they are found, the methods are invoked. E.g. (very simplified)
Class<?> beanClass = beanDefinition.getClass();
Object bean = beanClass.newInstance();
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(ctx);
}
Note that in newer version it may be better to use annotations, rather than implementing spring-specific interfaces. Now you can simply use:
#Inject // or #Autowired
private ApplicationContext ctx;
Spring source code to explain how ApplicationContextAware work
when you use ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
In AbstractApplicationContext class,the refresh() method have the following code:
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
enter this method,beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this)); will add ApplicationContextAwareProcessor to AbstractrBeanFactory.
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// Tell the internal bean factory to use the context's class loader etc.
beanFactory.setBeanClassLoader(getClassLoader());
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
// Configure the bean factory with context callbacks.
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
...........
When spring initialize bean in AbstractAutowireCapableBeanFactory,
in method initializeBean,call applyBeanPostProcessorsBeforeInitialization to implement the bean post process. the process include inject the applicationContext.
#Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
result = beanProcessor.postProcessBeforeInitialization(result, beanName);
if (result == null) {
return result;
}
}
return result;
}
when BeanPostProcessor implement Objectto execute the postProcessBeforeInitialization method,for example ApplicationContextAwareProcessor that added before.
private void invokeAwareInterfaces(Object bean) {
if (bean instanceof Aware) {
if (bean instanceof EnvironmentAware) {
((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
}
if (bean instanceof EmbeddedValueResolverAware) {
((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(
new EmbeddedValueResolver(this.applicationContext.getBeanFactory()));
}
if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
}
if (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
}
if (bean instanceof MessageSourceAware) {
((MessageSourceAware) bean).setMessageSource(this.applicationContext);
}
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}
}
}
Interface to be implemented by any object that wishes to be notified of the ApplicationContext that it runs in.
above is excerpted from the Spring doc website https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/ApplicationContextAware.html.
So, it seemed to be invoked when Spring container has started, if you want to do something at that time.
It just has one method to set the context, so you will get the context and do something to sth now already in context I think.
ApplicationContextAware Interface ,the current application context, through which you can invoke the spring container services. We can get current applicationContext instance injected by below method in the class
public void setApplicationContext(ApplicationContext context) throws BeansException.
I have all of my DAO's as beans in my applicationContext. Now this works great for autowiring these objects into my controllers to make database calls. However, given that I have embedded objects within some of my POJO's (Example: User class has list of pendingsurvey objects, list of appointment objects, etc) and don't want to grab these embedded objects when I grab the main object (Example: In a page with a list of users I don't need to know their pendingsurveys or appointments), I had set it up so that if the embedded object is null, go to the database to get the data. However, apparently #Autowire doesn't work in this case because the POJO's are not a Spring controlled object or something.
So question now is, how can I get a bean from applicationContext to use in my POJO's? I'm not that good with Spring so specific instructions would be greatly appreciated..
I've tried this but it gives me a bunch of injection dependency errors:
/* applicationContext.xml */
<bean id="userDao" class="UserDao" scope="singleton">
<property name="connectionWrapper" ref="connectionWrapper" />
</bean>
/* User.java - I tried putting in constructor and in the getter for userDao */
ApplicationContext ctx = AppContext.getApplicationContext('applicationContext.xml');
UserDao userDao = (UserDao) ctx.getBean("userDao");
Got it to work, same concept as Ric Jafe's answer but different implementation of it (Source: http://blog.jdevelop.eu/?p=154):
Made this class:
public class ApplicationContextProvider implements ApplicationContextAware {
private static ApplicationContext ctx = null;
public static ApplicationContext getApplicationContext() {
return ctx;
}
public void setApplicationContext(ApplicationContext ctx) throws BeansException {
// Assign the ApplicationContext into a static method
this.ctx = ctx;
}
}
Added this line into applicationContext.xml:
<bean id="applicationContextProvider" class="org.ApplicationContextProvider"></bean>
Called it in my POJO with:
ApplicationContext applicationContext = ApplicationContextProvider.getApplicationContext();
if (applicationContext != null && applicationContext.containsBean("keyName")) {
object = (Object) applicationContext.getBean("keyName");
}
Thanks for all the help guys.
When you load a context like that you are creating a new context, a copy of what was created when the application loaded. To access the same context that your application is using, you have several options, documented here: http://mythinkpond.wordpress.com/2010/03/22/spring-application-context/
For ease of access I'll copy/paste the one that most likely you want to use:
public class MyClass implements ApplicationContextAware {
static final long serialVersionUID = 02L;
ApplicationContext applicationContext = null;
public void doSomething(){
if (applicationContext != null && applicationContext.containsBean("accessKeys")){
MyBean beanA = (MyBean) applicationContext.getBean("mybean");
//Do something with this AccessBean
}
return null;
}
#Override
public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException {
System.out.println("setting context");
this.applicationContext = applicationContext;
}
}
Notice the implements ApplicationContext.
This tells the Spring framework you need the Application Context in this class.. So it autowires it to the applicationContext variable. For this to happen you also need the setter method. Then you can just use it and get your beans. Good coffee :)
P.S. - If you need the context in other classes, you can pass the applicationContext variable to them or use the same method. This way you only have 1 context at all times, containing your beans.
The slow autowire by type problem has finally been solved by creating a caching bean factory.
I would really like to be able to use such a CachingByTypeBeanFactory together with SpringJUnit4ClassRunner for running JUnit tests with #Autowired. But it does not seem to be possible to change the Bean Factory on the application context via the ContextLoader.
Is there any other way to do this ?
Create your own ContextLoader and attach this annotation to your JUnit class:
#ContextConfiguration(loader=YourLoader.class)
This is my example Loader which instantiates another or custom ApplicationContext which in turn may be initialized with custom BeanFactory (depending of capabilities):
public class XmlWebApplicationContextLoader extends AbstractContextLoader {
public final ConfigurableApplicationContext loadContext(final String... locations) throws Exception {
ServletContext servletContext = new MockServletContext("war", new FileSystemResourceLoader());
GenericWebApplicationContext webContext = new GenericWebApplicationContext();
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, webContext);
webContext.setServletContext(servletContext);
new XmlBeanDefinitionReader(webContext).loadBeanDefinitions(locations);
AnnotationConfigUtils.registerAnnotationConfigProcessors(webContext);
webContext.refresh();
webContext.registerShutdownHook();
return webContext;
}
protected String getResourceSuffix() {
return "";
}
}
In above case application context (provided by Spring Framework) has constructor:
public GenericWebApplicationContext(DefaultListableBeanFactory beanFactory) {
super(beanFactory);
}