談及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注解并直接擴展WebMvcConfigurationSupport或DelegatingWebMvcConfiguration,例如:
@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配置類,上面提到三種方式:
-
通過
AnnotationConfigApplicationContext@Configuration類通常使用AnnotationConfigApplicationContext或web版本AnnotationConfigWebApplicationContext進行引導。前者的一個簡單例子如下:
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.register(AppConfig.class); ctx.refresh(); MyBean myBean = ctx.getBean(MyBean.class); // use myBean ... -
通過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/>。
-
-
通過組件掃描
@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