Spring中Configuration的理解
基本用途
从Spring3.0,@Configuration用于定义配置类,可替换xml配置文件,被注解的类内部包含有一个或多个被@Bean注解的方法,这些方法将会被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext类进行扫描,并用于构建bean定义,初始化Spring容器。例如:
@Configuration
public class AppConfig {
@Bean
public MyBean myBean() {
// instantiate, configure and return bean ...
}
}
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(AppConfig.class);//加载配置类
ctx.refresh();//刷新并创建容器
MyBean myBean = ctx.getBean(MyBean.class);
// use myBean ...
注意:
@Configuration不可以是final类型;
@Configuration不可以是匿名类;
嵌套的configuration必须是静态类。
加载配置类方法
硬编码,例如ctx.register(AppConfig.class);
使用xml配置
<beans>
//这个注解用于启用ConfigurationClassPostProcessor等后置处理器,以加载以下类到容器。
<context:annotation-config/>
<bean class="com.acme.AppConfig"/>
</beans>
XmlWebApplicationContext xmlWebApplicationContext = new XmlWebApplicationContext();
xmlWebApplicationContext.setConfigLocation("abc.xml");
1
组件扫描。@Configuration本身是继承自@Component,因此也可以和正常被@Component一样被扫描到,或使用autowired。
package com.acme.app.services
@Configuration
public class AppConfig {
private final SomeBean someBean;
//这里可以通过Spring注入someBean
public AppConfig(SomeBean someBean) {
this.someBean = someBean;
}
// @Bean definition using "SomeBean"
}
要想AppConfig被扫描到,可以
@Configuration
@ComponentScan("com.acme.app.services")
public class RootConfig {
// various @Bean definitions ...
}
使用外部变量
注入Environment属性(可用于获取系统、JVM等环境变量),并配合@PropertySources注解,加载配置文件,使用@Value加载配置项。
@Configuration
@PropertySource("classpath:/com/acme/app.properties")
public class AppConfig {
@Inject Environment env;
//PropertySourcesPlaceholderConfigurer,usually enabled via XML with <context:property-placeholder/>
@Value("${bean.name}") String beanName;
@Bean
public MyBean myBean() {
return new MyBean(env.getProperty("bean.name"));
}
}
组合多个配置类
类似于xml的\标签,可以使用@Import组合多个配置类,例如:
@Configuration
public class DatabaseConfig {
@Bean
public DataSource dataSource() {
// instantiate, configure and return DataSource
}
}
@Configuration
@Import(DatabaseConfig.class)
public class AppConfig {
private final DatabaseConfig dataConfig;
public AppConfig(DatabaseConfig dataConfig) {
this.dataConfig = dataConfig;
}
@Bean
public MyBean myBean() {
// reference the dataSource() bean method
return new MyBean(dataConfig.dataSource());
}
}
//最后只需要导入一个即可
new AnnotationConfigApplicationContext(AppConfig.class);
@Configuration导入xml配置
@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
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();
}
}
}
@Lazy懒加载
可以与@Configuration和@Bean配合使用。Spring默认不是懒加载。
@EnableXXX注解
配合@Configuration使用,包括 @EnableAsync, @EnableScheduling, @EnableTransactionManagement, @EnableAspectJAutoProxy, @EnableWebMvc。
@EnableWebMvc
常见使用方式如下所示:
@Configuration
@EnableWebMvc
@ComponentScan(basePackageClasses = { MyConfiguration.class })
public class MyWebConfiguration {
}
一旦使用了该注解,则会默认加载WebMvcConfigurationSupport配置,包括:
1. HandlerMappings
1. RequestMappingHandlerMapping 用于处理url到注解controller的method映射,序号是0
2. HandlerMapping 用于处理url到view的映射,序号是1
3. BeanNameUrlHandlerMapping 用于处理url到controller的beanname映射,序号是2
4. HandlerMapping 用于处理静态资源,序号是Integer.MAX_VALUE-1
5. HandlerMapping 用于forward到default servlet,序号是Integer.MAX_VALUE
2. HandlerAdapters
1. RequestMappingHandlerAdapter 用于处理带有注解Controller的响应处理方法
2. HttpRequestHandlerAdapter 用于处理实现 HttpRequestHandler 的响应处理器
3. SimpleControllerHandlerAdapter 用于处理实现Controller接口的响应处理器
3. HandlerExceptionResolverComposite
1. ExceptionHandlerExceptionResolver 用于处理@ExceptionHandler注解
2. ResponseStatusExceptionResolver 用于处理@ResponseStatus注解
3. DefaultHandlerExceptionResolver 用于处理已知的Spring异常
4. AntPathMatcher 和 UrlPathHelper
你也可以不使用该注解,而是配置类继承WebMvcConfigurationSupport类或DelegatingWebMvcConfiguration,。或者使用@EnableWebMvc,并实现 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 ...
}
@Profile逻辑组配置
用于指定配置类加载的逻辑组(a named logical grouping),只有在组内的配置类才会被加载。该注解类似于在xml的配置,例如\。@Profile可以在配置类上,也可以在配置类的方法上。如果不配置该注解,默认是无论如何都会加载。
可以通过以下方式设置:
在JVM参数、系统环境参数或web.xml的initParam spring.profiles.active、spring.profiles.default(来自org.springframework.core.env.AbstractEnvironment) 。如果当spring.profiles.active属性被设置时,那么Spring会优先使用该属性对应值来激活Profile。当spring.profiles.active没有被设置时,那么Spring会根据spring.profiles.default属性的对应值来进行Profile进行激活。如果上面的两个属性都没有被设置,那么就不会有任务Profile被激活,只有定义在Profile之外的Bean才会被创建。
或者使用ConfigurableEnvironment.setActiveProfiles 指定需要激活的逻辑组名。
在测试类中,使用@ActiveProfiles注解指定。
@Profile("embedded", "!abc")//表示当embedded激活,abc不激活时加载本配置类
@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
}
}
等同于
<beans profile="development">
<!-- 只扫描开发环境下使用的类 -->
<context:component-scan base-package="" />
<!-- 加载开发使用的配置文件 -->
<util:properties id="config" location="classpath:dev/config.properties"/>
</beans>
我们甚至可以自定义Profile注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Profile("dev")
pubilc @interface Dev {
}
ConfigurableEnvironment
用于在没有配置项的时候,用硬编码方式指定激活逻辑组、设置默认逻辑组,或增加新的逻辑组等。
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles("dev");
ctx.register(TransferServiceConfig.class, StandaloneDataConfig.class, JndiDataConfig.class);
ctx.refresh();
配置信息
最主要需要搞懂三个类PropertySource、PropertyResolver、Environment,PropertySource用于定于基本的配置数据来源,PropertyResolver用于对PropertySource进行解析,包括EL表达式(占位符替换),或进行类型转换(String->Integer等),Environment继承自PropertyResolver,这也是为何使用Environment.getProperty可以用el表达式的原因。
propertySource
propertySource
propertyResolver
propertyResolver
Environment
Environment
被使用
继承
PropertySource
加载各种配置信息。其中ComposePropertySource提供了组合PropertySource的功能,查找顺序就是注册顺序。默认提供了一个MutablePropertySources实现,我们可以调用addFirst添加到列表的开头,addLast添加到末尾,另外可以通过addBefore(propertySourceName, propertySource)或addAfter(propertySourceName, propertySource)添加到某个propertySource前面/后面;最后大家可以通过iterator迭代它,然后按照顺序获取属性。
Map<String, Object> map = new HashMap<>();
map.put("encoding", "gbk");
PropertySource propertySource1 = new MapPropertySource("map", map);
System.out.println(propertySource1.getProperty("encoding"));
ResourcePropertySource propertySource2 = new ResourcePropertySource("resource", "classpath:resources.properties"); //name, location
System.out.println(propertySource2.getProperty("encoding"));
PropertyResolver 与 Environment
Environment环境,比如JDK环境,Servlet环境,Spring环境等等;每个环境都有自己的配置数据,如System.getProperties()、System.getenv()等可以拿到JDK环境数据;ServletContext.getInitParameter()可以拿到Servlet环境配置数据等等;也就是说Spring抽象了一个Environment来表示环境配置。也可以获取或设置Profile。
MockEnvironment:模拟的环境,用于测试时使用;
StandardEnvironment:标准环境,普通Java应用时使用,会自动注册System.getProperties() 和 System.getenv()到环境;
StandardServletEnvironment:标准Servlet环境,其继承了StandardEnvironment,Web应用时使用,除了StandardEnvironment外,会自动注册ServletConfig(DispatcherServlet)、ServletContext及JNDI实例到环境;