SpringBoot自定义Condition注解
最近碰到个这样的需求,需要同一套代码适配个版本数据库(数据库不同,且部分表的字段及关联关系可能会不同),即这套代码配置不同的数据库都能跑。项目采用的框架为SpringBoot+Mybatis。经过一番思考,思路如下:
(1)在业务层(service)和数据访问层(Mapper)之间添加一层适配层,用来屏蔽数据库的差异
(2)适配层中代码均采用接口加实现类的方式,不同的数据库用的实现类不同
(3)业务层(service)中全部采用面向接口编程
(4)项目启动后只实例化和数据库相匹配的适配层实现类
实现上面的一个关键点是对bean的实例化添加一个条件判断来控制。其实SpringBoot里面新增了很多条件注解,能实现这个功能。但是都有些局限性,最终是采用自定义条件注解的方案。
一、SpringBoot自带的注解ConditionalOnProperty
这个注解不做过多的解释,只说通过这个注解怎么实现我们的功能。
假设我们application.properties中配置一个配置项为
#bean实例化条件配置项
conditionKey=1.0
2
1
#bean实例化条件配置项
2
conditionKey=1.0
那么只需要加上@ConditionalOnProperty的name和havingValue就能实现,只有配置文件中name对应的配置项的值和havingValue内容一致才实例化这个对象。
针对我们上面配置的application.properties的内容,@ConditionalOnProperty的使用案例如下面代码所示
// 仅当conditionKey==1.0的时候实例化这个类
@Component
@ConditionalOnProperty(name = "conditionKey", havingValue = "1.0")
public class Manage1Impl implements MyManage{
@Override
public void sayHello() {
System.out.println("我是实现类01");
}
@PostConstruct
public void init() {
this.sayHello();
}
}
15
1
// 仅当conditionKey==1.0的时候实例化这个类
2
3
name = "conditionKey", havingValue = "1.0") (
4
public class Manage1Impl implements MyManage{
5
6
7
public void sayHello() {
8
System.out.println("我是实现类01");
9
}
10
11
12
public void init() {
13
this.sayHello();
14
}
15
}
这个注解的局限性:这个注解的havingValue里面只能配置一个值。
由于项目个性化需求,希望这个havingValue可以配置多个值,name对应的配置项的Value只要满足havingValue里面多个值的就表示匹配正确。即,havingValue里面可以配置多个值,name对应配置项的值来和havingValue匹配时,采用逻辑或匹配,满足一个值就算匹配正确。
二、自定义条件注解
(1)思路
注解里面有2个属性,具体如下
- name:String类型,用来接受application.properties的配置项的key
- havingValue:String数组类型,用来和name对应key的Value进行匹配
(2)定义注解
package com.zxy.config;
import org.springframework.context.annotation.Conditional;
import java.lang.annotation.*;
/**
* 自定义条件注解
* @author ZENG.XIAO.YAN
* @version 1.0
* @Date 2019-04-15
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
@Conditional(CustomOnPropertyCondition.class)
public @interface CustomConditionalOnProperty {
/**
* 条件变量的name
*/
String name() default "";
/**
* havingValue数组,支持or匹配
*/
String[] havingValue() default {};
}
28
1
package com.zxy.config;
2
3
import org.springframework.context.annotation.Conditional;
4
import java.lang.annotation.*;
5
/**
6
* 自定义条件注解
7
* @author ZENG.XIAO.YAN
8
* @version 1.0
9
* @Date 2019-04-15
10
*/
11
RetentionPolicy.RUNTIME) (
12
ElementType.TYPE, ElementType.METHOD}) ({
13
14
CustomOnPropertyCondition.class) (
15
public @interface CustomConditionalOnProperty {
16
17
/**
18
* 条件变量的name
19
*/
20
String name() default "";
21
22
/**
23
* havingValue数组,支持or匹配
24
*/
25
String[] havingValue() default {};
26
27
}
28
(3)定义注解的匹配规则
package com.zxy.config;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
import java.util.Map;
/**
* 自定义条件注解的验证规则
* @author ZENG.XIAO.YAN
* @version 1.0
* @Date 2019-04-15
*/
public class CustomOnPropertyCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
Map<String, Object> annotationAttributes = annotatedTypeMetadata.getAnnotationAttributes(CustomConditionalOnProperty.class.getName());
String propertyName = (String) annotationAttributes.get("name");
String[] values = (String[]) annotationAttributes.get("havingValue");
if (0 == values.length) {
return false;
}
String propertyValue = conditionContext.getEnvironment().getProperty(propertyName);
// 有一个匹配上就ok
for (String havingValue : values) {
if (propertyValue.equalsIgnoreCase(havingValue)) {
return true;
}
}
return false;
}
}
x
1
package com.zxy.config;
2
3
import org.springframework.context.annotation.Condition;
4
import org.springframework.context.annotation.ConditionContext;
5
import org.springframework.core.type.AnnotatedTypeMetadata;
6
import java.util.Map;
7
8
/**
9
* 自定义条件注解的验证规则
10
* @author ZENG.XIAO.YAN
11
* @version 1.0
12
* @Date 2019-04-15
13
*/
14
public class CustomOnPropertyCondition implements Condition {
15
16
17
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
18
Map<String, Object> annotationAttributes = annotatedTypeMetadata.getAnnotationAttributes(CustomConditionalOnProperty.class.getName());
19
String propertyName = (String) annotationAttributes.get("name");
20
String[] values = (String[]) annotationAttributes.get("havingValue");
21
if (0 == values.length) {
22
return false;
23
}
24
String propertyValue = conditionContext.getEnvironment().getProperty(propertyName);
25
// 有一个匹配上就ok
26
for (String havingValue : values) {
27
if (propertyValue.equalsIgnoreCase(havingValue)) {
28
return true;
29
}
30
}
31
return false;
32
}
33
}
34
(4)使用案例
直接参考下面2图吧
三、小结
自定义Condition注解,主要就2步
(1)定义一个条件注解(2)定义一个条件的校验规则
作者:zeng1994
出处:http://www.cnblogs.com/zeng1994/
本文版权归作者和博客园共有,欢迎转载!但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接!