基于条件,向Spring容器中注册Bean.
适用于,不同环境下,代码的生效,以及多环境切换!
以下,以一个例子来进行记录!
基于不同的操作系统,来展示不同的文件展开命令。
首先,定义一个接口!
public interface ShowCmd {
String cmd();
}
然后,写出两种环境不同的实现类。
public class LinuxCmd implements ShowCmd{
@Override
public String cmd() {
return "ls";
}
}
public class WindowsCmd implements ShowCmd{
@Override
public String cmd() {
return "dir";
}
}
紧接着,创建两个条件,系统根据条件判断是否将bean注册到Spring容器中!
public class LinuxCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String property = context.getEnvironment().getProperty("os.name");
return property.toLowerCase().contains("linux");
}
}
public class WindowsCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String property = context.getEnvironment().getProperty("os.name");
return property.toLowerCase().contains("window");
}
}
最后,则进行注册
public class JavaConfig {
@Bean
@Conditional(WindowsCondition.class)
ShowCmd winCmd(){
return new WindowsCmd();
}
@Bean
@Conditional(LinuxCondition.class)
ShowCmd linuxCmd(){
return new LinuxCmd();
}
}
获取,调用!
public static void main(String[] args) {
AnnotationConfigApplicationContext javaConfig = new AnnotationConfigApplicationContext(JavaConfig.class);
ShowCmd bean = javaConfig.getBean(ShowCmd.class);
System.out.println(bean.cmd());
}
多环境切换,基于java代码!
多环境切换的例子,使用Profile实现!
定义一个多数据源,这边是例子,所以很多都是简写!
public class DataSource {
private String username;
private String password;
private String url;
//省略,get set toString等方法!
}
进行注册
public class JavaConfig {
@Bean
@Profile("dev")
DataSource devDataSource(){
DataSource dataSource = new DataSource();
dataSource.setUrl("jdbc:mysql:///dev");
dataSource.setUsername("dev");
dataSource.setPassword("dev");
return dataSource;
}
@Bean
@Profile("prod")
DataSource prodDataSource(){
DataSource dataSource = new DataSource();
dataSource.setUrl("jdbc:mysql:///prod");
dataSource.setUsername("prod");
dataSource.setPassword("prod");
return dataSource;
}
}
进行调用!
public static void main(String[] args) {
//这里需要注意,不能直接添加配置类,如果添加了,refresh方法会被直接调用
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ConfigurableEnvironment environment = ctx.getEnvironment();
//这里表示,设置当前环境为prod
environment.addActiveProfile("prod");
//再去加载配置类
ctx.register(JavaConfig.class);
//初始化容器
ctx.refresh();
DataSource prodDataSource = ctx.getBean("prodDataSource", DataSource.class);
System.out.println(prodDataSource);
}
多环境切换,基于配置类!
<beans profile="dev">
<bean class="com.tongzhou.demo.DataSource">
<property name="password" value="dev"/>
<property name="url" value="jdbc:mysql:///dev"/>
<property name="username" value="dev"/>
</bean>
</beans>
<beans profile="prod">
<bean class="com.tongzhou.demo.DataSource">
<property name="password" value="prod"/>
<property name="url" value="jdbc:mysql:///prod"/>
<property name="username" value="prod"/>
</bean>
</beans>
调用!
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext();
ConfigurableEnvironment environment = ctx.getEnvironment();
environment.addActiveProfile("dev");
ctx.setConfigLocation("datasource.xml");
ctx.refresh();
DataSource prodDataSource = ctx.getBean(DataSource.class);
System.out.println(prodDataSource);
}
原理解析
profile注解定义了一个组合注解,本质上,就是一个条件注解ProfileCondition。
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({ProfileCondition.class})
public @interface Profile {
String[] value();
}
紧接着,查看ProfileCondition,可以看到,这可类就实现了Condition 接口
class ProfileCondition implements Condition {
ProfileCondition() {
}
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
//查看profile全部属性,对于condition来说,实际上就只有一个value属性
MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
if (attrs != null) {
//获取value,value是一个数组,所以需要遍历
Iterator var4 = ((List)attrs.get("value")).iterator();
Object value;
do {
if (!var4.hasNext()) {
return false;
}
value = var4.next();
}
//判断当前系统是否包含这个value
while(!context.getEnvironment().acceptsProfiles(Profiles.of((String[])((String[])value))));
return true;
} else {
return true;
}
}
}
自定义一个profile
首先,定义一个接口
//注解的生命周期,不仅保存在class,jvm加载class后依然存在
@Retention(RetentionPolicy.RUNTIME)
//方法级别上生效
@Target(ElementType.METHOD)
//生效的类
@Conditional(MyProfileCondition.class)
public @interface MyProfile {
String[] value();
}
定义实现类
public class MyProfileCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
MultiValueMap<String, Object> allAnnotationAttributes = metadata.getAllAnnotationAttributes(MyProfile.class.getName());
if(allAnnotationAttributes != null){
List<Object> values = allAnnotationAttributes.get("value");
for (Object value : values){
boolean b = context.getEnvironment().acceptsProfiles(Profiles.of((String[]) value));
if(b){
return true;
}
}
}
return false;
}
}
最后,则进行测试,与系统中的@profile效果一致!