Spring controller: How to use property ${..} in @R

2019-08-07 11:52发布

问题:

I already found questions with answers but they don't help me.

I have a web servlet project where I use a Spring Controller (4.2.5) and Spring Security (4.0.2). I don't use Spring Boot.

My project works fine.

But now my task is this:
Make @RequestMapping(value={"auth/**"} configurable (replace "auth/**" with ${dm.filterPattern})

Problem: in @RequestMapping ${dm.filterPattern} isn't resolved, although @PropertySource is processed.

This is the entry dm.filterPattern in dmConfig.properties:

dm.filterPattern=/auth/*

Here is some essential code, with all Spring annotations.

Controller:

The output of the method init() shows me that @PropertySource is processed correctly. env.getProperty("...") returns correct values.

@Controller
@PropertySource("classpath:/dmConfig.properties")
@RequestMapping(value ={ "${dm.filterPattern}"})
public class DmProxyController implements ApplicationContextAware
{
    private Environment env;

    @Autowired
    public DmProxyController(Environment env)
    {
        this.env = env;
    }

    @RequestMapping(path={"${dm.filterPattern}"} ,method = RequestMethod.POST)
    protected void doPost(HttpServletRequest customerRequest, HttpServletResponse response)
            throws ServletException, IOException, DmException
    {
           // code for POST request
    }

    @RequestMapping(path={"${dm.filterPattern}"} ,method = RequestMethod.GET)
    protected void doGet(HttpServletRequest customerRequest, HttpServletResponse response)
            throws ServletException, IOException, DmException
    {
           // code for GET request
    }

    @PostConstruct
    public void init() throws ServletException
    {
        RequestMappingHandlerMapping requestMapping=
                (RequestMappingHandlerMapping) appContext.getBean("requestMappingHandlerMapping");

        Map<RequestMappingInfo, HandlerMethod> handlerMethods = requestMapping.getHandlerMethods();
        logger.debug("RequestMapping via dm.filterPattern: {}",
                env.getProperty("dm.filterPattern"));
                logger.debug("Handler Methods: {}", handlerMethods.size());

        for (RequestMappingInfo mapInfo : handlerMethods.keySet())
        {
            logger.debug(" Mappinginfo: {} --> {}", mapInfo, handlerMethods.get(mapInfo));
        }
    }
}

Class with bean definitions

@Configuration
@PropertySource("classpath:/dmConfig.properties")
@ComponentScan(basePackages = "com.dm.filter, com.dm.controller")
@EnableTransactionManagement(mode = AdviceMode.PROXY, proxyTargetClass = false)
@Import({DmSecurityConfigurer.class, DmWebConfigurer.class})
public class DmRoot
{

}

DispatcherServletInitializer

public class DmDispatcherServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer
{
    @Override
    protected Class<?>[] getRootConfigClasses()
    { return new Class[]{DmRoot.class};  }

    @Override
    protected Class<?>[] getServletConfigClasses()
    { return null; }

    @Override
    protected String[] getServletMappings()
    { return new String[]{"/"}; }

    @Override
    protected String getServletName()
    {  return "dmDispatcherServlet";  }

    @Override
    protected void customizeRegistration(ServletRegistration.Dynamic registration)
    {
        super.customizeRegistration(registration);
        registration.setLoadOnStartup(1);
    }
}

WebConfigurer

public class DmWebConfigurer extends WebMvcConfigurerAdapter
{
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry)
    {
        super.addResourceHandlers(registry);
        registry.addResourceHandler("/index.html").addResourceLocations("/");
        registry.setOrder(Integer.MAX_VALUE-5);
    }
}

SecurityWebApplicationInitializer

public class DmSecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer
{
    public DmSecurityWebApplicationInitializer()
    {
        // some logging
    }

    @Override
    protected void beforeSpringSecurityFilterChain(ServletContext servletContext)
    {     // adding own filters   }

    @Override
    protected void afterSpringSecurityFilterChain(ServletContext servletContext)
    {     // adding own filters   }
}

SecurityConfigurer

@EnableWebMvc
@EnableWebSecurity
@PropertySource("classpath:dmConfig.properties")
public class DmSecurityConfigurer extends WebSecurityConfigurerAdapter
{

    private static Logger logger = LogManager.getLogger(DmSecurityConfigurer.class.getName());

    @Autowired
    private Environment env;

    @Autowired
    private UserDetailsService dmUserDetailsService;

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception
    {
        String urlPattern = env.getProperty("dm.springSecurityPattern");
        String realmName = env.getProperty("dm.springSecurityRealm");

        httpSecurity.httpBasic().realmName(realmName)
                .and().userDetailsService(dmUserDetailsService)
                .authorizeRequests()
                .antMatchers(urlPattern).authenticated()
                .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .csrf().disable();
    }

}

回答1:

There is a possibility that PropertySourcesPlaceholderConfigurer is being initialised later in the spring context than your controller and hence the values are not resolved. try adding explicit bean definition for PropertySourcesPlaceholderConfigurer in one of the root configuration file as below;

@PropertySource("classpath:/dmConfig.properties")
public class DmWebConfigurer extends WebMvcConfigurerAdapter
{
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry)
    {
        super.addResourceHandlers(registry);
        registry.addResourceHandler("/index.html").addResourceLocations("/");
        registry.setOrder(Integer.MAX_VALUE-5);
    }

    @Bean
    public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
        return new PropertySourcesPlaceholderConfigurer();
    }
}

The reason you can see the values properly in your init() method is because it is called after all the beans are initialised including PropertySourcesPlaceholderConfigurer.