SpringBoot条件装配

上一篇写到了SpringBoot的自动装配:SpringBoot自动装配 - 简书 (jianshu.com)

springboot通过@EnableAutoConfiguration实现自动装配;可以将标注了@Configuration的配置类“一股脑的”的添加到ApplicationContext中。但是有时候我们需要自动装配更自能一些,需要在装配时满足一些条件或者调整加载顺序,这时,就需要在自动装配的基础上实现条件装配了。
目前接触到的实现条件装配有2种办法:

  1. @Profile注解
  2. @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")会自动生效
  • 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扩展注解

    1. @ConditionalOnBean
    @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;
     }
    
    它的意思就是说如果你配置了redis的相关配置信息那么我就实例化RedisTemplate供你进行操作,如果你没有配置redis的相关配置那么我就不实例化。
    1. @ConditionalOnMissingBean
      这个仅仅比1多了个Missing,啥意思呢,见名知意,就是不存在某个bean的时候实例化。
    1. @ConditionalOnClass
      存在某个类时,才会实例化一个Bean
    1. @ConditionalOnMissingClass
      不存在某个类时,才会实例化一个Bean
    1. @ConditionalOnProperty(prefix = “test”, name = “testname”, havingValue = “token”, matchIfMissing = true)
      这个就稍微复杂了一点,它的意思呢就是当存在配置文件中以test为前缀的属性,属性名称为testname,然后它的值为token时才会实例化一个类。
      matchIfMissing的意思呢就是说如果所有的都不满足的话就默认实现,不管这个属性test.testname是不是等于token
      了解更多,查看org.springframework.boot.autoconfigure.condition
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。