上一篇写到了SpringBoot的自动装配:SpringBoot自动装配 - 简书 (jianshu.com)。
springboot通过@EnableAutoConfiguration实现自动装配;可以将标注了@Configuration的配置类“一股脑的”的添加到ApplicationContext中。但是有时候我们需要自动装配更自能一些,需要在装配时满足一些条件或者调整加载顺序,这时,就需要在自动装配的基础上实现条件装配了。
目前接触到的实现条件装配有2种办法:
- @Profile注解
- @Conditional注解
1. @Profile注解
@Profile注解的目的是为了多环境开发,可以根据不同的环境动态的加载一系列组件。比如开发环境使用dev, 生产环境使用prod。
@Profile是在Spring3.1的时候引入的,一开始注解用在类上,在Spring3.2之后,注解也可用在方法上。
1. 使用示例:
/**
* @Profile用在类上
*/
@Configuration
@EnableSwagger2
@Profile("dev")
public class SwaggerConfig {
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
// 指定扫描1个包
.apis(RequestHandlerSelectors.basePackage("com.test.controller"))
// 只有标记了@Api的类方法才会暴露出给swagger
.apis(RequestHandlerSelectors.withClassAnnotation(Api.class))
// 只有标记了@ApiOperation的方法才会暴露出给swagger
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
.build()
.apiInfo(apiInfo());
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("接口文档")
.description("Demo接口测试")
.version("1.0")
.termsOfServiceUrl("")
.build();
}
}
/**
* @Profile注解用在方法上
*/
@Configuration
public class DataSourceConfig {
@Bean
@Profile("dev")
public DataSource devDataSource() {
System.out.println(" dev DataSource !!");
BasicDataSource basicDataSource = new BasicDataSource();
basicDataSource.setDriverClassName("com.mysql.jdbc.Driver");
basicDataSource.setUrl("jdbc:mysql://localhost:3308/zszxz");
basicDataSource.setUsername("root");
basicDataSource.setPassword("1234");
return basicDataSource;
}
@Bean
@Profile("prod")
public DataSource prodDataSource() {
System.out.println(" prod DataSource !!");
BasicDataSource basicDataSource = new BasicDataSource();
basicDataSource.setDriverClassName("com.mysql.jdbc.Driver");
basicDataSource.setUrl("jdbc:mysql://localhost:3306/zszxz");
basicDataSource.setUsername("root");
basicDataSource.setPassword("1234");
return basicDataSource;
}
}
<!-- xml中配置使用 -->
<beans profile="dev">
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"
p:driverClassName="com.mysql.jdbc.Driver"
p:url="jdbc:mysql://localhost:3306/zszxzb"
p:username="root"
p:password="1234"/>
</beans>
<beans profile="prod">
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"
p:driverClassName="com.mysql.jdbc.Driver"
p:url="jdbc:mysql://localhost:3306/zszxzb"
p:username="root"
p:password="1234"/>
</beans>
2. 开启方式
- SpringBoot项目
- 方式一 在application.yml(properties)文件中添加配置:
spring: profiles: active: dev - 方式二 将
@Profile("dev")换成@ActiveProfiles("dev")会自动生效
- 方式一 在application.yml(properties)文件中添加配置:
- SpringMVC项目
在web.xml 中配置
<context-param>
<param-name>spring.profiles.active</param-name>
<param-value>dev</param-value>
</context-param>
<servlet>
<servlet-name>test</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>spring.profiles.default</param-name>
<param-value>dev</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>test</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
2. @Conditional注解
1. @Conditional
@Conditional是Spring4.0引入的,它可以根据代码中设置的条件装载不同的bean。比如说当一个接口有两个实现类时,我们要把这个接口交给Spring管理时通常会只选择实现其中一个实现类,这个时候我们总不能使用if-else吧,所以@Conditional的注解就出现了,它配合@Configuration或者@Bean等注解来控制配置是否生效。
@Conditional注解的value通常是一个实现了Condition接口的自定义类,自定义类要重写Condition的matches方法:若返回true,则@Conditional注解的bean装配;返回false,不装配。
示例如下:
/**
* 自定义的类,自定义了2个
*/
public class DevEvn implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String dataBase = context.getEnvironment().getProperty("dataBase");
if ("mysql".equals(dataBase)) {
return true;
}
return false;
}
}
public class ProdEvn implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String dataBase = context.getEnvironment().getProperty("dataBase");
if ("oracle".equals(dataBase)) {
return true;
}
return false;
}
}
/** ************************************************ */
/**
* 有@Conditional注解的bean
* 哪个是true就初始化哪个数据库
*/
@Configuration
public class DataBaseConfig {
@Bean
@Conditional(DevEvn.class)
public MysqlConfig mysqlConfig() {
return new MysqlConfig();
}
@Bean
@Conditional(ProdEvn.class)
public OracleConfig oracleConfig() {
return new OracleConfig();
}
}
2. @Conditional扩展注解
-
- @ConditionalOnBean
它的意思就是说如果你配置了redis的相关配置信息那么我就实例化RedisTemplate供你进行操作,如果你没有配置redis的相关配置那么我就不实例化。@Bean @ConditionalOnBean(RedisConnectionFactory.class) public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) { RedisTemplate<Object, Object> template = new RedisTemplate<Object, Object>(); template.setConnectionFactory(connectionFactory); template.setKeySerializer(new StringRedisSerializer()); template.afterPropertiesSet(); return template; } - @ConditionalOnMissingBean
这个仅仅比1多了个Missing,啥意思呢,见名知意,就是不存在某个bean的时候实例化。
- @ConditionalOnMissingBean
- @ConditionalOnClass
存在某个类时,才会实例化一个Bean
- @ConditionalOnClass
- @ConditionalOnMissingClass
不存在某个类时,才会实例化一个Bean
- @ConditionalOnMissingClass
- @ConditionalOnProperty(prefix = “test”, name = “testname”, havingValue = “token”, matchIfMissing = true)
这个就稍微复杂了一点,它的意思呢就是当存在配置文件中以test为前缀的属性,属性名称为testname,然后它的值为token时才会实例化一个类。
matchIfMissing的意思呢就是说如果所有的都不满足的话就默认实现,不管这个属性test.testname是不是等于token
了解更多,查看org.springframework.boot.autoconfigure.condition包
- @ConditionalOnProperty(prefix = “test”, name = “testname”, havingValue = “token”, matchIfMissing = true)
