SpringMVC配置原理

谈及Spring的Java配置,核心类就是WebMvcConfigurationSupport
我们从WebMvcConfigurationSupport这个类开始逐步深入了解Spring的配置原理。

WebMvcConfigurationSupport

这是提供SpringMVC Java config配置的主要类。通常通过将@EnableWebMvc添加到@Configuration注解的类来导入它。另一种更高级的方式是直接扩展这个类,并根据需要重写其方法,记住要将@Configuration添加到扩展的子类中,并添加@Bean到重写的@Bean方法。有关更多详细信息,请参阅@EnableWebMvc的Javadoc。

这个类会注册下面的HandlerMappings:
  • RequestMappingHandlerMapping排序索引为0,将请求映射到控制器方法。
  • HandlerMapping排序索引为1,直接映射URL路径到视图名称。
  • BeanNameUrlHandlerMapping排序索引为2,以将URL路径映射到控制器bean名称。
  • HandlerMapping排序索引为Integer.MAX_VALUE-1,以提供静态资源请求。
  • HandlerMapping排序索引为Integer.MAX_VALUE,将请求转发到默认的servlet。
注册这些HandlerAdapter:
  • RequestMappingHandlerAdapter用于使用控制器方法处理请求。
  • HttpRequestHandlerAdapter用于使用HttpRequestHandlers处理请求。
  • SimpleControllerHandlerAdapter用于使用interface-based控制器处理请求。
用这个异常解析器链注册一个HandlerExceptionResolverComposite:
  • ExceptionHandlerExceptionResolver用于通过@ExceptionHandler方法处理异常。
  • ResponseStatusExceptionResolver用于使用@ResponseStatus注解的异常。
  • DefaultHandlerExceptionResolver用于解析已知的Spring异常类型
注册AntPathMatcher和UrlPathHelper以供以下用户使用:
  • RequestMappingHandlerMapping
  • ViewControllers的HandlerMapping
  • 用于服务资源的HandlerMapping

请注意,这些bean可以使用PathMatchConfigurer进行配置。

默认情况下,RequestMappingHandlerAdapter和ExceptionHandlerExceptionResolver都使用以下默认实例进行配置:

  • 一个ContentNegotiationManager
  • 一个DefaultFormattingConversionService
  • 一个org.springframework.validation.beanvalidation.OptionalValidatorFactoryBean(如果JSR-303的实现存在于类路径中)
  • 一系列HttpMessageConverters,这取决于类路径上可用的第三方库。

@Configuration

首先看下声明:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
    String value() default "";
}

注意,@Component@Configuration元注解,也即它具备@Component的特性。

指示一个类声明一个或多个@Bean方法,并且可以由Spring容器处理,以便在运行时为这些bean生成bean定义和处理请求,例如:

@Configuration
 public class AppConfig {

     @Bean
     public MyBean myBean() {
         // instantiate, configure and return bean ...
     }
 }

引导@Configuration类

通过AnnotationConfigApplicationContext

@Configuration类通常使用AnnotationConfigApplicationContext或web版本AnnotationConfigWebApplicationContext进行引导。 前者的一个简单例子如下:

AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
 ctx.register(AppConfig.class);
 ctx.refresh();
 MyBean myBean = ctx.getBean(MyBean.class);
 // use myBean ...

有关更多详细信息,请参阅AnnotationConfigApplicationContext Javadoc,有关web.xml配置说明,请参阅AnnotationConfigWebApplicationContext

通过Spring <beans> XML

作为直接针对AnnotationConfigApplicationContext注册@Configuration类的替代方法,可以在Spring XML文件中将@Configuration类声明为<bean>定义:

 <beans>
    <context:annotation-config/>
    <bean class="com.acme.AppConfig"/>
 </beans>

在上面的示例中,为了启用ConfigurationClassPostProcessor和其他与注解有关的后置处理器来处理@Configuration类,需要<context:annotation-config/>

通过组件扫描

@Component@Configuration元注解,因此@Configuration类是组件扫描的候选对象(通常使用Spring XML的<context:component-scan/>元素)。

@Configuration类不仅可以通过组件扫描进行引导,还可以自己使用@ComponentScan注解来配置组件扫描:

@Configuration
 @ComponentScan("com.acme.app.services")
 public class AppConfig {
     // various @Bean definitions ...
 }

有关详细信息,请参阅@ComponentScan javadoc。

@ComponentScan

配置用于@Configuration类的组件扫描指令。提供与Spring XML <context:component-scan>元素并行的支持。

可以指定basePackageClasses()或basePackages()(或其别名value())来定义要扫描的特定类包。如果未定义特定的包,则将从声明此注解的类的包中进行扫描。

请注意,<context:component-scan>元素具有annotation-config属性; 但是,这个注解没有。这是因为在几乎所有使用@ComponentScan的情况下,默认的annotation config processing(例如处理@Autowired之类)is assumed。此外,当使用AnnotationConfigApplicationContext和web版本AnnotationConfigWebApplicationContext时,annotation config processors总是被注册,这意味着任何试图在@ComponentScan级别禁用它们的尝试都将被忽略。有关使用示例,请参阅@Configuration的Javadoc。

💡

  • <context:annotation-config/>启用ConfigurationClassPostProcessor和其他与注解有关的后置处理器来处理@Configuration类。
  • <context:component-scan>annotation-config属性作用同<context:annotation-config/>
  • @ComponentScan默认的annotation config processing(例如处理@Autowired之类)is assumed。此外,当使用AnnotationConfigApplicationContext和web版本AnnotationConfigWebApplicationContext时,annotation config processors总是被注册。

使用外部的值

使用Environment API

通过使用@Autowired@Inject注解将Spring Environment注入@Configuration类,来查找外部的值:

@Configuration
 public class AppConfig {

     @Inject Environment env;

     @Bean
     public MyBean myBean() {
         MyBean myBean = new MyBean();
         myBean.setName(env.getProperty("bean.name"));
         return myBean;
     }
 }

通过Environment解析的属性属于一个或多个"属性源"对象,而@Configuration类可以使用@PropertySources注解向Environment对象提供属性源:

@Configuration
 @PropertySource("classpath:/com/acme/app.properties")
 public class AppConfig {

     @Inject Environment env;

     @Bean
     public MyBean myBean() {
         return new MyBean(env.getProperty("bean.name"));
     }
 }

有关更多详细信息,请参阅Environment和@PropertySource Javadoc。

使用@Value注解

外部的值可以通过@Value注解注入到@Configuration类中:

@Configuration
 @PropertySource("classpath:/com/acme/app.properties")
 public class AppConfig {

     @Value("${bean.name}") String beanName;

     @Bean
     public MyBean myBean() {
         return new MyBean(beanName);
     }
 }

这种方法在使用Spring的PropertySourcesPlaceholderConfigurer时非常有用,通常通过XML <context:property-placeholder/>来启用。

有关使用BeanFactoryPostProcessor类型(PropertySourcesPlaceholderConfigurer)的详细信息,请参阅下面有关使用@ImportResource导入Spring XML来构造@Configuration类的部分,@Value Javadoc,@Bean Javadoc。

构造@Configuration类

用@Import注解

@Configuration类可以使用@Import注解构造,与<import>在Spring XML中的工作方式相似。 由于@Configuration类对象是作为容器内的Spring bean进行管理的,因此可以使用@Autowired@Inject注入导入的配置:

@Configuration
 public class DatabaseConfig {

     @Bean
     public DataSource dataSource() {
         // instantiate, configure and return DataSource
     }
 }

 @Configuration
 @Import(DatabaseConfig.class)
 public class AppConfig {

     @Inject DatabaseConfig dataConfig;

     @Bean
     public MyBean myBean() {
         // reference the dataSource() bean method
         return new MyBean(dataConfig.dataSource());
     }
 }

现在,AppConfig和导入的DatabaseConfig都可以通过在Spring上下文中注册AppConfig来引导:new AnnotationConfigApplicationContext(AppConfig.class);

用@Profile注解

@Configuration类可以使用@Profile注解标记,以表明只有给定的一个或多个profile处于active时才应该处理它们:

@Profile("embedded")
 @Configuration
 public class EmbeddedDatabaseConfig {

     @Bean
     public DataSource dataSource() {
         // instantiate, configure and return embedded DataSource
     }
 }

 @Profile("production")
 @Configuration
 public class ProductionDatabaseConfig {

     @Bean
     public DataSource dataSource() {
         // instantiate, configure and return production DataSource
     }
 }

有关更多详细信息,请参阅@Profile和Environment javadocs。

使用@ImportResource注解导入Spring XML

如上所述,@Configuration类可以在Spring XML文件中声明为常规的Spring <bean>定义。也可以使用@ImportResource注解将Spring XML配置文件导入到@Configuration类中。 从XML导入的Bean定义可以使用@Autowired@Inject注入:

@Configuration
 @ImportResource("classpath:/com/acme/database-config.xml")
 public class AppConfig {

     @Inject DataSource dataSource; // from XML

     @Bean
     public MyBean myBean() {
         // inject the XML-defined dataSource bean
         return new MyBean(this.dataSource);
     }
 }
嵌套的@Configuration类

@Configuration类可以如下嵌套在一起:

@Configuration
 public class AppConfig {

     @Inject DataSource dataSource;

     @Bean
     public MyBean myBean() {
         return new MyBean(dataSource);
     }

     @Configuration
     static class DatabaseConfig {
         @Bean
         DataSource dataSource() {
             return new EmbeddedDatabaseBuilder().build();
         }
     }
 }

当引导这样的配置时,只有AppConfig需要针对应用上下文进行注册。由于是一个嵌套的@Configuration类,DatabaseConfig将被自动注册。这样可以避免使用@Import注解。

配置延迟初始化

默认情况下,@Bean方法将在容器引导时被迫切地实例化。
为了避免这种情况,可以将@Configuration@Lazy注解结合使用,以表明在类中声明的所有@Bean方法在默认情况下是懒惰地初始化的。请注意@Lazy也可以用于单独的@Bean方法。

Testing对@Configuration类的支持

spring-test模块中提供的Spring TestContext框架提供@ContextConfiguration注解,从Spring 3.1开始,它可以接受一个@Configuration Class对象的数组:

@RunWith(SpringJUnit4ClassRunner.class)
 @ContextConfiguration(classes={AppConfig.class, DatabaseConfig.class})
 public class MyTests {

     @Autowired MyBean myBean;

     @Autowired DataSource dataSource;

     @Test
     public void test() {
         // assertions against myBean ...
     }
 }

有关详细信息,请参阅TestContext框架参考文档。

使用@Enable注解启用内置的Spring功能

诸如异步方法执行,计划任务执行,注解驱动事务管理,甚至Spring MVC等Spring特性可以使用各自的@Enable*注解在@Configuration类中启用和配置。有关详细信息,请参阅@EnableAsync@EnableScheduling@EnableTransactionManagement@EnableAspectJAutoProxy@EnableWebMvc

@EnableWebMvc

看下声明:

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

将此注解添加到@Configuration类中,从WebMvcConfigurationSupport导入Spring MVC配置,例如:

@Configuration
 @EnableWebMvc
 @ComponentScan(basePackageClasses = { MyConfiguration.class })
 public class MyWebConfiguration {

 }

要自定义导入的配置,请实现WebMvcConfigurer接口,或者更好的方式是扩展包含一系列空方法的基类WebMvcConfigurerAdapter并覆盖单个方法,例如:

@Configuration
 @EnableWebMvc
 @ComponentScan(basePackageClasses = { MyConfiguration.class })
 public class MyConfiguration extends WebMvcConfigurerAdapter {

           @Override
           public void addFormatters(FormatterRegistry formatterRegistry) {
         formatterRegistry.addConverter(new MyConverter());
           }

           @Override
           public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
         converters.add(new MyHttpMessageConverter());
           }

     // More overridden methods ...
 }

如果WebMvcConfigurer没有暴露某些需要配置的高级设置,请考虑删除@EnableWebMvc注解并直接扩展WebMvcConfigurationSupportDelegatingWebMvcConfiguration,例如:

@Configuration
 @ComponentScan(basePackageClasses = { MyConfiguration.class })
 public class MyConfiguration extends WebMvcConfigurationSupport {

           @Override
           public void addFormatters(FormatterRegistry formatterRegistry) {
         formatterRegistry.addConverter(new MyConverter());
           }

           @Bean
           public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
         // Create or delegate to "super" to create and
         // customize properties of RequestMappingHandlerAdapter
           }
 }

DelegatingWebMvcConfiguration

声明如下:

@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {

WebMvcConfigurationSupport的一个子类,用于检测并委托所有类型为WebMvcConfigurer的Bean,使其可以自定义由WebMvcConfigurationSupport提供的配置。 这是由@EnableWebMvc实际导入的类。

总结

上面介绍了几个核心的API,下面说下他们彼此之间是如何关联,以及如何起作用的。

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

一、用户创建Java Config配置类,并使用@Configuration注解注释。

二、引导@Configuration配置类,上面提到三种方式:

  1. 通过AnnotationConfigApplicationContext

    @Configuration类通常使用AnnotationConfigApplicationContext或web版本AnnotationConfigWebApplicationContext进行引导。

    前者的一个简单例子如下:

    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
    ctx.register(AppConfig.class);
    ctx.refresh();
    MyBean myBean = ctx.getBean(MyBean.class);
    // use myBean ...
    
  2. 通过Spring <beans> XML

    在Spring XML文件中将@Configuration类声明为<bean>定义

    <beans>
       <context:annotation-config/>
       <bean class="com.acme.AppConfig"/>
    </beans>
    
    • <context:annotation-config/>启用ConfigurationClassPostProcessor和其他与注解有关的后置处理器来处理@Configuration类。
    • <context:component-scan>annotation-config属性作用同<context:annotation-config/>
  3. 通过组件扫描

    @Component@Configuration元注解,因此@Configuration类是组件扫描的候选对象。

    可以自己使用@ComponentScan注解来配置组件扫描:

     @Configuration
     @ComponentScan("com.acme.app.services")
     public class AppConfig {
         // various @Bean definitions ...
     }
    

    TODO @ComponentScan默认的annotation config processing(例如处理@Autowired之类)is assumed。此外,当使用AnnotationConfigApplicationContext和web版本AnnotationConfigWebApplicationContext时,annotation config processors总是被注册。

三、将@EnableWebMvc注解添加到@Configuration类中,从WebMvcConfigurationSupport导入Spring MVC配置

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

@EnableWebMvc注解通过@Import(DelegatingWebMvcConfiguration.class)导入Spring MVC配置。

DelegatingWebMvcConfiguration类中又通过如下方法注入了WebMvcConfigurer,用于导入用于的自定义配置。

可以看到,

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

通过@Autowired(required = false)注入了上下文中所有类型为WebMvcConfigurer的bean,其中required为false,说明自定义配置是可选的)。如果你创建的配置类实现WebMvcConfigurer接口,并交给Spring去管理,则会被注入到WebMvcConfigurerComposite中。

WebMvcConfigurerComposite的声明如下:

class WebMvcConfigurerComposite implements WebMvcConfigurer {

    private final List<WebMvcConfigurer> delegates = new ArrayList<WebMvcConfigurer>();

    public void addWebMvcConfigurers(List<WebMvcConfigurer> configurers) {
        if (configurers != null) {
            this.delegates.addAll(configurers);
        }
    }

WebMvcConfigurerComposite维护了一个WebMvcConfigurer的List集合,addWebMvcConfigurers方法将所有的自定义配置加入该集合中。

TODO

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容