@Value not working inside a class which extends ot

2019-08-02 20:37发布

问题:

Is it possible to use @Value inside a class that extends another class?

Below are the relevant code snippets. In the Lo_Controller class it works perfectly, but in the Lo_DisplayHandler always returns null. The only reason I can think of is because it depends on another class, which is not annotated with @Component. If that is the cause, what is the recommended option to read a value from properties file similar to @Value?

Just to test it out, I changed from @Component to @Controller in Lo_DisplayHandler to see, if they are somehow related to each other, however it returns null as well.

This works:

package com.ma.common.controller;
imports ...

@Controller
@RequestMapping("/log")
public class Lo_Controller {
       @Value("${log.display.lastpage}")
       private String lastPageUrl;
...

This always returns null:

package com.ma.log.handler;
imports ...
@Component
public class Lo_DisplayHandler extends Lo_Handler {
       public Lo_DisplayHandler() {
              super();
       }
       @Value("${log.display.lastpage}")
       private String lastPageUrl;
... 

mvc-dispatcher-servlet.xml

<context:component-scan base-package="com.ma.common.controller, com.ma.log.handler" />
<context:property-placeholder location="classpath:restServices.properties"/>

<mvc:annotation-driven />
<mvc:resources mapping="/**" location="/" />


@Component
public class Lo_DisplayHandler extends Lo_Handler {

       @Value("${log.display.lastpage}")
       private String lastPageUrl;

       public void anyOtherMethod(){
              String _a = lastPageUrl; //FAIL - always null
       }

       @PostConstruct
       public void initIt() throws Exception {
              String _a = lastPageUrl; //OK - when the application is deployed and started for the first time
       }

       @PreDestroy
       public void cleanUp() throws Exception {
              String _a = lastPageUrl; //OK - when the application is stopped
       }

web.xml

       <servlet>
              <servlet-name>mvc-dispatcher</servlet-name>
              <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
              <load-on-startup>1</load-on-startup>
       </servlet>

       <servlet-mapping>
              <servlet-name>mvc-dispatcher</servlet-name>
              <url-pattern>/</url-pattern>
       </servlet-mapping>

       <context-param>
              <param-name>contextConfigLocation</param-name>
              <param-value>/WEB-INF/mvc-dispatcher-servlet.xml</param-value>
       </context-param>

       <listener>
              <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
       </listener>

回答1:

Three possible problems here:

First, I disencourage to extends classes as Spring works better autowiring dependencies. But it should work Second, you have to focus on the lifecycle of a bean. The property will be set after the instanciation. @PostConstruct to validate the content. Third, The visibility in hierarchical context of the property holder is not straight forward. So if you define @value in the root applicationContext, it will not set by your dispatcherServlet context. To test it, please inject the dependency of the bean defined at root level, you will see that your @Value will be take in account.

The lastPageUrl property will be accessible from a bean within the same context (by push creation or pull creation). In case of push creation, if another bean autowire the Lo_DisplayHandler bean and call your method anyOtherMethod(), it will get the value.

@Component
public class ScannableIntoYourContext{

    @Autowired
    private Lo_DisplayHandler myHandler;

}

Other way is to pull the bean from ObjectFactory.

@Autowired
private ObjectFactory<Lo_DisplayHandler> bean;

Lo_DisplayHandler instanceFromContext = bean.getObject();