Spring中使用Java Config部分替代Xml实现fine-grained细粒度配置时的特殊注意事项

xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="bean1" class="test.Bean1" />
    <bean id="bean2" class="test.Bean2" />

</beans>

通常,我们可以采用下面的Java Config方式代替上面的Xml实现fine-grained细粒度配置。

java config

package test;

@Configuration
public class WebConfig {
  @Bean
  public Bean1 bean1(){
       //……
  }
  @Bean
  public Bean2 bean2(){
       //……
  }
}

但是,有时候我们希望:一组功能相关的Bean之间能够建立更直接明确的关系。那么我们可以选择实现 XXXConfigurer 这类回调接口,然后使用 @Configuration注解该实现类,并复写Override它们的抽象方法,例如:

功能 回调接口
缓存 org.springframework.cache.annotation.CachingConfigurer
定时任务 org.springframework.scheduling.annotation.SchedulingConfigurer
异步(并发)任务 org.springframework.scheduling.annotation.AsyncConfigurer
Spring MVC高级配置 org.springframework.web.servlet.config.annotation.WebMvcConfigurer

这里,我们以 WebMvcConfigurer 为例:自定义Jackson的ObjectMapper,实现@ResponseBody的自定义渲染?

  • 解决方法是配置Spring MVC的 HttpMessageConverter 消息转换器

先来看xml方式:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"          
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans-4.3.xsd 
        http://www.springframework.org/schema/mvc 
        http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd">

        <mvc:annotation-driven >
            <mvc:message-converters>
                <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                    <property name="objectMapper" ref="objectMapper"/>
                </bean>
            </mvc:message-converters>
        </mvc:annotation-driven>

        <bean id="objectMapper" class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean"
           p:serializationInclusion="NON_NULL"/>

注意:

XXXConfigurer 的实现类无疑需要复写其全部抽象方法,但是如果不希望覆盖默认增加额外配置,方法若有返回值则 return null,否则不写任何实现代码

所以,我们还可以通过继承 WebMvcConfigurer抽象实现类
—— org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter,
来实现Spring MVC在Java Config 形式下的高级配置:

@Configuration
@EnableWebMvc
public class WebConfiguration extends WebMvcConfigurerAdapter {

        @Override
        public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
            ObjectMapper mapper = new ObjectMapper();
            mapper.setDefaultPropertyInclusion(Include.NON_NULL);
            converters.add(new MappingJackson2HttpMessageConverter(mapper));
        }
}

到这里,我们已经实现了Spring MVC在Java Config 形式下的高级配置,但是需要注意这里我们使用了@EnableWebMvc。看下源码:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}

这里导入了配置类org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration
再看下它的源码:

@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {

    @Autowired(required = false)
    public void setConfigurers(List<WebMvcConfigurer> configurers) {
        if (!CollectionUtils.isEmpty(configurers)) {
            this.configurers.addWebMvcConfigurers(configurers);
        }
    }
}

所以,DelegatingWebMvcConfiguration的作用就是注入WebMvcConfigurer的实现类,而传统Spring项目中需要@EnableWebMvc来导入此类。

  • 这里强调传统Spring项目,是因为Spring Boot项目中Spring MVC的自动配置类WebMvcAutoConfiguration已经注入了该类,从而导入WebMvcConfigurer接口的某个默认实现以及MVC架构所需的其他基础设施的默认Bean

或者,我们干脆直接移除@EnableWebMvc,作为替代方式去继承DelegatingWebMvcConfiguration,一样能达到效果。


对于传统Spring项目,Java Config的使用势必导致Xml 与Java Config 同时存在于项目中。这时候就需要我们去考虑Xml与annotation-based之间是否会产生冲突。比如下面这些基本功能相同标签注解

  • 这里强调基本功能,因为Java Config的配置更细粒度,自然容易获得其他扩展导致某些功能不一致。例如:@EnableWebMvc导入了DelegatingWebMvcConfiguration,从而实现了WebMvcConfigurer接口的注入。
<cache:annotation-driven /> <!--@EnableCaching--> 
<task:annotation-driven scheduler=""/> <!--@EnableScheduling--> 
<task:annotation-driven executor=""/> <!--@EnableAsync--> 
<mvc:annotation-driven /> <!--@EnableWebMvc--> 
<!-- …… 等等,诸如此类 -->

这里还是以<mvc:annotation-driven /> <!--@EnableWebMvc-->为例,如果二者同时存在:

  • 传统Spring项目以web.xml加载dispatcher-servlet.xml,毫无疑问配置在xml中的<mvc:annotation-driven />将会优先生效。
  • 所以,此时,WebMvcConfigurer的实现类将不会被注入,这种Java Config的配置方式不会生效。
  • 所以,这种情况需要二选一。

至于,其他XXXConfigurer 在传统Spring项目中的配置方式,与示例同理,半斤八两。

猜你喜欢

转载自blog.csdn.net/qq_32331073/article/details/80394949