SpringMVC配置原理

談及Spring的Java配置,核心類就是WebMvcConfigurationSupport。
我們從WebMvcConfigurationSupport這個類開始逐步深入了解Spring的配置原理。

WebMvcConfigurationSupport

這是提供SpringMVC Java config配置的主要類。通常通過將@EnableWebMvc添加到@Configuration注解的類來導入它。另一種更高級的方式是直接擴展這個類,并根據(jù)需要重寫其方法,記住要將@Configuration添加到擴展的子類中,并添加@Bean到重寫的@Bean方法。有關更多詳細信息,請參閱@EnableWebMvc的Javadoc。

這個類會注冊下面的HandlerMappings:
  • RequestMappingHandlerMapping排序索引為0,將請求映射到控制器方法。
  • HandlerMapping排序索引為1,直接映射URL路徑到視圖名稱。
  • BeanNameUrlHandlerMapping排序索引為2,以將URL路徑映射到控制器bean名稱。
  • HandlerMapping排序索引為Integer.MAX_VALUE-1,以提供靜態(tài)資源請求。
  • HandlerMapping排序索引為Integer.MAX_VALUE,將請求轉發(fā)到默認的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的實現(xiàn)存在于類路徑中)
  • 一系列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類對象是作為容器內(nèi)的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());
     }
 }

現(xiàn)在,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文件中聲明為常規(guī)的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對象的數(shù)組:

@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注解啟用內(nèi)置的Spring功能

諸如異步方法執(zhí)行,計劃任務執(zhí)行,注解驅(qū)動事務管理,甚至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 {

 }

要自定義導入的配置,請實現(xiàn)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,下面說下他們彼此之間是如何關聯(lián),以及如何起作用的。

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

一、用戶創(chuàng)建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,說明自定義配置是可選的)。如果你創(chuàng)建的配置類實現(xiàn)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

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容