Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Autowired null if not found

Tags:

java

spring

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

like image 430
nowszy94 Avatar asked Aug 19 '16 12:08

nowszy94


People also ask

Why Autowired is getting 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.

What is @autowired required false?

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 .

Why is @autowired not working?

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.

Can we use @autowired without @component?

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 .


2 Answers

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.

like image 200
ByeBye Avatar answered Nov 04 '22 15:11

ByeBye


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();
        }


    }

}
like image 24
Nikita Bakaev Avatar answered Nov 04 '22 17:11

Nikita Bakaev