Default @Autowired
Spring implementation throws error when bean that should be autowired is not defined. Is it possible to configure Spring, that will assign null to object instead of throwing exception?
EDIT:
I've add required=false
to Autowired
but it still not working properly.
Thats my code:
@Autowired
private ApplicationContext applicationContext;
@Autowired(required = false)
private HelloService helloService;
public HelloController() {
message = "Hello World";
System.out.println("Controller constructor");
}
@RequestMapping(method = RequestMethod.GET)
public ModelAndView helloWorld() {
ModelAndView modelAndView = new ModelAndView("hello");
if (helloService == null) {
System.out.println(message);
} else {
helloService.hello();
BeanDefinitionRegistry factory = (BeanDefinitionRegistry) applicationContext.getAutowireCapableBeanFactory();
factory.removeBeanDefinition("helloService");
}
return modelAndView;
}
In first request it's autowired, but in next request after removing bean with factory.removeBeanDefinition("helloService")
, controller bean is construct again, and i get NoSuchBeanDefinitionException
EDIT2:
I've created another controller with the following body:
@Autowired(required = false)
private TestService testService;
@RequestMapping(method = RequestMethod.GET)
public ModelAndView hello() {
ModelAndView modelAndView = new ModelAndView("hello");
return modelAndView;
}
and it works properly - Object is null and it doesn't get error. Maybe i should use different method to remove bean from Spring context?
Stacktrace:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'helloService' is defined
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:698) ~[spring-beans-4.2.7.RELEASE.jar:4.2.7.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1175) ~[spring-beans-4.2.7.RELEASE.jar:4.2.7.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:284) ~[spring-beans-4.2.7.RELEASE.jar:4.2.7.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) ~[spring-beans-4.2.7.RELEASE.jar:4.2.7.RELEASE]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.resolvedCachedArgument(AutowiredAnnotationBeanPostProcessor.java:508) ~[spring-beans-4.2.7.RELEASE.jar:4.2.7.RELEASE]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.access$200(AutowiredAnnotationBeanPostProcessor.java:115) ~[spring-beans-4.2.7.RELEASE.jar:4.2.7.RELEASE]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:538) ~[spring-beans-4.2.7.RELEASE.jar:4.2.7.RELEASE]
...
STEPS TO REPRODUCE:
https://github.com/nowszy94/Autowired-null
If the application starts and your field appears to be null it is generally due to one of the following issues: Using @Autowired on a static field. Omitted @Autowired on a field. Instance of bean not visible to Spring.
By default, the @Autowired annotation implies that the dependency is required. This means an exception will be thrown when a dependency is not resolved. You can override that default behavior using the (required=false) option with @Autowired .
When @Autowired doesn't work. There are several reasons @Autowired might not work. When a new instance is created not by Spring but by for example manually calling a constructor, the instance of the class will not be registered in the Spring context and thus not available for dependency injection.
So the answer is: No, @Autowired does not necessarily mean you must also use @Component . It may be registered with applicationContext. xml or @Configuration+@Bean .
you can disable this, by setting the required
attribute to false.
@Autowired(required=false)
If Spring can’t find bean, it will leave field unset as null.
The problem is that AutowiredAnnotationBeanPostProcessor cache injection result. So, when you delete bean from context, this class think that this object is actually exists(see private class AutowiredFieldElement extends InjectionMetadata.InjectedElement in AutowiredAnnotationBeanPostProcessor.class and method inject).So, you should clear that cache.
The most stupid way, that i found is, but looks like that you want to do
@Controller
@RequestMapping("/hello")
public class HelloController {
@Autowired(required = false)
private HelloService helloService;
@Autowired
private ApplicationContext applicationContext;
@RequestMapping(method = RequestMethod.GET)
public ModelAndView modelAndView() {
ModelAndView modelAndView = new ModelAndView("hello");
if (helloService != null) {
helloService.hello();
removeBean("helloService");
}
return modelAndView;
}
private void removeBean(String beanName) {
BeanDefinitionRegistry factory = (BeanDefinitionRegistry) applicationContext
.getAutowireCapableBeanFactory();
factory.removeBeanDefinition(beanName);
clearCache(factory);
}
private void clearCache(BeanDefinitionRegistry beanFactory){
AutowiredAnnotationBeanPostProcessor processor = null;
for (BeanPostProcessor beanPostProcessor : ((DefaultListableBeanFactory) beanFactory).getBeanPostProcessors()){
if (beanPostProcessor.getClass().equals(AutowiredAnnotationBeanPostProcessor.class)){
processor = (AutowiredAnnotationBeanPostProcessor) beanPostProcessor;
}
}
try {
Field injectionMetadataCache = processor.getClass().getDeclaredField("injectionMetadataCache");
injectionMetadataCache.setAccessible(true);
Method clear = Map.class.getMethod("clear");
clear.invoke( injectionMetadataCache.get(processor));
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With