必威体育Betway必威体育官网
当前位置:首页 > IT技术

spring boot 自动配置解密之注解@Conditional

时间:2019-06-08 08:45:19来源:IT技术作者:seo实验室小编阅读:55次「手机版」
 

conditional

spring boot 自动配置解密之注解@Conditional

在开发基于spring的应用程序时,我们可能需要根据环境条件注册不同的bean实例。比如常见的数据库的数据源的配置,日常环境、测试环境及线上环境,所连接的数据库地址及相关配置是不一样的(其实可以利用不同的环境对应的域名也

可解决,我们这里不讨论先)。

为了解决这个问题,spring3引入了profiles的概念,详细请参考官方文档。为了更能灵活的根据环境条件注册bean实例,

spring4又引入了@Conditional注解。有个@Conditional注解,我们可以根据任何环境条件来注册bean,

比如是否注册bean实例的条件:

  •  在classpath路径中是否存在某个特定的类
  • 在ApplicationContext中是否还没注册过一个特定类型的bean
  • 是否在某路径下存在某文件
  • 是否在配置文件中配置了某特定的属性
  • 是否存在某特定的系统环境变量
等等。

下面举些例子:

1、根据是否存在某特定的系统环境变量来注册bean

假如我们的应用程序既可以使用mysql数据库,也可以使用Mongo 数据库,我们需要根据系统环境变量dbType来使用不同的数据库,dbType 为mysql,程序使用mysql提供的api实现提供数据,dbType为mongo,程序使用Mongo提供的api实现提供数据。

我们首先定义公共接口:

import java.util.List;

/**
 * @author sdcuike
 * @date 2018/1/28
 * @since 2018/1/28
 */
public interface UserDao {
    List<String> getAllUserNames();
}
mysql数据源api的实现:

package com.sdcuike.springboot.practice.conditional.demo.properties;

import org.springframework.context.annotation.Conditional;
import org.springframework.stereotype.Repository;

import java.util.Arrays;
import java.util.List;

/**
 * @author sdcuike
 * @date 2018/1/28
 * @since 2018/1/28
 */
@Repository
public class JdbcUserDaoImpl implements UserDao {
    @Override
    public List<String> getAllUserNames() {
        return Arrays.asList("jdbc", "test");
    }
}

mongo数据源api的实现:

package com.sdcuike.springboot.practice.conditional.demo.properties;

import org.springframework.context.annotation.Conditional;
import org.springframework.stereotype.Repository;

import java.util.Arrays;
import java.util.List;

/**
 * @author sdcuike
 * @date 2018/1/28
 * @since 2018/1/28
 */
@Repository
public class MongoUserDaoImpl implements UserDao {
    @Override
    public List<String> getAllUserNames() {
        return Arrays.asList(" Mongo db ", "test");
    }
}
为了利用spring boot的@Conditional注解决定实例化哪个数据源api,我们还要实现判断条件:

package com.sdcuike.springboot.practice.conditional.demo.properties;

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;

/**
 * @author sdcuike
 * @date 2018/1/28
 * @since 2018/1/28
 */
public class MySqlDbTypeCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        final String dbType = System.getProperty("dbType");
        return "mysql".equalsIgnoreCase(dbType);
    }
}

package com.sdcuike.springboot.practice.conditional.demo.properties;

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;

/**
 * @author sdcuike
 * @date 2018/1/28
 * @since 2018/1/28
 */
public class MongoDbTypeCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        final String dbType = System.getProperty("dbType");
        return "mongo".equalsIgnoreCase(dbType);
    }
}

定义了两个Condition,我们还需要改变JdbcUserDaoImpl、MongoUserDaoImpl实例化的条件:

即加上注解@Conditional(MongoDbTypeCondition.class)、@Conditional(MySqlDbTypeCondition.class):

@Repository
@Conditional(MySqlDbTypeCondition.class)
public class JdbcUserDaoImpl implements UserDao {
    @Override
    public List<String> getAllUserNames() {
        return Arrays.asList("jdbc", "test");
    }
}

@Repository
@Conditional(MongoDbTypeCondition.class)
public class MongoUserDaoImpl implements UserDao {
    @Override
    public List<String> getAllUserNames() {
        return Arrays.asList(" Mongo db ", "test");
    }
}
测试用例:

package com.sdcuike.springboot.practice.conditional.demo.properties;

import com.sdcuike.springboot.practice.SpringApplicationBoot;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.Arrays;
import java.util.List;

/**
 * @author sdcuike
 * @date 2018/1/28
 * @since 2018/1/28
 */
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = SpringApplicationBoot.class)
public class UserDaoTest {
    @Autowired
    private UserDao userDao;
    
    
    @BeforeClass
    public static void init() {
        System.setProperty("dbType", "mongo");
        //System.setProperty("dbType", "mysql");
        
    }
    
    @AfterClass
    public static void close() {
        System.clearProperty("dbType");
    }
    
    @Test
    public void testMysqlDbType() {
        System.out.println("=====================");
        final List<String> allUserNames = userDao.getAllUserNames();
        Assert.assertEquals(Arrays.asList(" Mongo db ", "test"), allUserNames);
        
        System.out.println("=====================");
        
    }
}
代码详见:https://github.com/sdcuike/spring-boot-practice/tree/blog2018年01月28根据是否存在某特定的系统环境变量来注册bean

2、根据在classpath路径中是否存在某个特定的类来注册bean

现在我们来看一下根据在classpath路径中是否存在某个特定的类来注册bean,还是由以上的代码来改动,假如,

classpath路径下出现mysql驱动类com.mysql.jdbc.Driver就实例化JdbcUserDaoImpl,

否则就默认实例化MongoUserDaoImpl,现在只需要增加改动判断条件即可:

public class MySqlDriverPresentsCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        
        try {
            Class.forName("com.mysql.jdbc.Driver");
            return true;
        } catch (ClassNotFoundException e) {
            return false;
        }
        
    }
}
public class MySqlDriverNotPresentsCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        try {
            Class.forName("com.mysql.jdbc.Driver");
            return false;
        } catch (ClassNotFoundException e) {
            return true;
        }
    }
}

同时修改:

@Repository
@Conditional(MySqlDriverPresentsCondition.class)
public class JdbcUserDaoImpl implements UserDao {
    @Override
    public List<String> getAllUserNames() {
        return Arrays.asList("jdbc", "test");
    }
}

@Repository
@Conditional(MySqlDriverNotPresentsCondition.class)
public class MongoUserDaoImpl implements UserDao {
    @Override
    public List<String> getAllUserNames() {
        return Arrays.asList(" Mongo db ", "test");
    }
}
代码及测试用例见:https://github.com/sdcuike/spring-boot-practice/blob/blog2018年01月28根据在classpath路径中是否存在某个特定的类来注册bean/src/test/java/com/sdcuike/springboot/practice/conditional/demo/properties/UserDaoTest.java

3、其他条件类推

其他条件:比如容器中是否存在某个类的实例或者类是否有特殊的注解等等,我们可以举一反三,因为spring 提供的接口:

@FunctionalInterface
public interface Condition {

	/**
	 * Determine if the condition matches.
	 * @param context the condition context
	 * @param metadata metadata of the {@link org.springframework.core.type.AnnotationMetadata class}
	 * or {@link org.springframework.core.type.MethodMetadata method} being checked
	 * @return {@code true} if the condition matches and the component can be registered,
	 * or {@code false} to veto the annotated component's registration
	 */
	boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);

}
方法    boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata)里面的参数ConditionContext及AnnotatedTypeMetadata很容器获取这些信息。

4、spring boot提供的@Conditional注解

其实spring 提倡注解,所以spring boot也为我们提供了很多常见@Conditional注解。

具体实现的代码就不说了,感兴趣的看源码吧,原理是一样的。

相关阅读

springboot 2.0 使用Hikari连接池(号称java平台最快的,

1.springboot 2.0 默认连接池就是Hikari了,所以引用parents后不用专门加依赖 2.贴我自己的配置(时间单位都是毫秒) # jdbc_config

细说Spring——AOP详解(AOP概览)

一、对AOP的初印象 首先先给出一段比较专业的术语(来自百度): 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通

@Modifying 注解

@Query与@Modifying执行更新操作@Query 与 @Modifying这两个annotation一起声明,可定义个性化更新操作,例如只涉及某些字段更新时最

spring Quartz 源码分析--触发器类CronTriggerBean源

前面我们讲到了Quartz框架在项目中的实现,在Quartz中的重要API有两个重要的触发器类:CronTrigger 和SimpleTrigger 在Quartz框

秒懂,Java 注解 (Annotation)你可以这样学

文章开头先引入一处图片。这处图片引自老罗的博客。为了避免不必要的麻烦,首先声明我个人比较尊敬老罗的。至于为什么放这张图,自然

分享到:

栏目导航

推荐阅读

热门阅读