最近碰到个这样的需求,需要同一套代码适配个版本数据库(数据库不同,且部分表的字段及关联关系可能会不同),即这套代码配置不同的数据库都能跑。项目采用的框架为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
@Component
3
@ConditionalOnProperty(name = "conditionKey", havingValue = "1.0")
4
public class Manage1Impl  implements  MyManage{
5

6
    @Override
7
    public void sayHello() {
8
        System.out.println("我是实现类01");
9
    }
10

11
    @PostConstruct
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
@Retention(RetentionPolicy.RUNTIME)
12
@Target({ElementType.TYPE, ElementType.METHOD})
13
@Documented
14
@Conditional(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
    @Override
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)定义一个条件的校验规则

SpringBoot自定义Condition注解的更多相关文章

  1. springboot + 拦截器 + 注解 实现自定义权限验证

    springboot + 拦截器 + 注解 实现自定义权限验证最近用到一种前端模板技术:jtwig,在权限控制上没有用springSecurity.因此用拦截器和注解结合实现了权限控制. 1.1 定义 ...

  2. Springboot中使用自定义参数注解获取 token 中用户数据

    使用自定义参数注解获取 token 中User数据 使用背景 在springboot项目开发中需要从token中获取用户信息时通常的方式要经历几个步骤 拦截器中截获token TokenUtil工具类 ...

  3. [技术博客] SPRINGBOOT自定义注解

    SPRINGBOOT自定义注解 在springboot中,有各种各样的注解,这些注解能够简化我们的配置,提高开发效率.一般来说,springboot提供的注解已经佷丰富了,但如果我们想针对某个特定情景 ...

  4. 更加灵活的参数校验,Spring-boot自定义参数校验注解

    上文我们讨论了如何使用@Min.@Max等注解进行参数校验,主要是针对基本数据类型和级联对象进行参数校验的演示,但是在实际中我们往往需要更为复杂的校验规则,比如注册用户的密码和确认密码进行校验,这个时 ...

  5. SpringBoot自定义注解

    1.注解的概念 注解是一种能被添加到java代码中的元数据,类.方法.变量.参数和包都可以用注解来修饰.注解对于它所修饰的代码并没有直接的影响. 2.注解的使用范围 1)为编译器提供信息:注解能被编译 ...

  6. SpringBoot 自定义注解 实现多数据源

    SpringBoot自定义注解实现多数据源 前置学习 需要了解 注解.Aop.SpringBoot整合Mybatis的使用. 数据准备 基础项目代码:https://gitee.com/J_look/ ...

  7. SpringBoot自定义注解@YamlPropertySource加载yml或者yaml文件(扩展了@PropertySource)

    1:概述 SpringBoot的@PropertySource注解只支持加载 properties结尾的文件.当使用@ConfigurationProperties 注解配合@EnableConfig ...

  8. SpringBoot 使用 JSR303 自定义校验注解

    JSR303 是 Java EE 6 中的一项子规范,叫做 Bean Validation,官方参考实现是hibernate Validator,有了它,我们可以在实体类的字段上标注不同的注解实现对数 ...

  9. 自定义ConditionalOnXX注解

    一.Conditional注解介绍 对SpringBoot有足够了解的小伙伴应该都用过Conditional系列注解,该注解可用在类或者方法上用于控制Bean的初始化. 常用的Conditional注 ...

随机推荐

  1. JDOJ 2254 Who am I?

    JDOJ 2254: Who am I? Description 输出程序自己本身的源代码. Input 无 Output 输出程序自己本身的源代码. 我真是搞不懂了出这道题还把它归到程序语法基础题里 ...

  2. C++ string 字符串函数详解

    运算符重载 + 和 +=:连接字符串 =:字符串赋值 >.>=.< 和 <=:字符串比较(例如a < b, aa < ab) ==.!=:比较字符串 << ...

  3. VC修改本机IP地址

    http://www.vcchar.com/thread-1527-1-1.html 设置IP地址只需要更改注册表中关于适配器的相应设置,但更改后需要重新启动系统才能生效,而AddIPAddress函 ...

  4. git开发中常用命令

    项目代码克隆岛本地 git clone 项目地址 #如:git clone http://cngit.fir.ai/data_service/distributedstorage.git 克隆指定分支 ...

  5. Spring Cloud Alibaba整合Sentinel流控

    前面我们都是直接通过集成sentinel的依赖,通过编码的方式配置规则等.对于集成到Spring Cloud中阿里已经有了一套开源框架spring-cloud-alibaba,就是用于将一系列的框架成 ...

  6. ConcurrentHashMap竟然也有死循环问题?

    前几天和朋友闲聊,说遇到了一个ConcurrentHashMap死循环问题,当时心里想这不科学呀?ConcurrentHashMap怎么还有死循环呢,毕竟它已经解决HashMap中rehash中死循环 ...

  7. python2升级python3

    需求: centos环境,python2.7需要升级为python3.x 1.请先手动(再次)安装 openssl .否则你升级之后,你的pip不能下载,会各种报错的. 比如这种错误: ImportE ...

  8. SQL --------------- between 和< >

    between值 and 值 运算符用于选取介于两个值之间的数据范围内的值,常与where一块使用between运算符选择给定范围内的值.值可以是数字,文本或日期. 使用between的时候会与and ...

  9. Dubbo简介与使用

    1:Dubbo是什么 阿里生产的一种rpc 实现框架  Dubbo 是一个分布式服务框架,是阿里巴巴开源项目 ,被国内电商及互联网项目中使用. Dubbo 致力于提供高性能和透明化的RPC远程服务调用 ...

  10. Vue.js 源码分析(十三) 基础篇 组件 props属性详解

    父组件通过props属性向子组件传递数据,定义组件的时候可以定义一个props属性,值可以是一个字符串数组或一个对象. 例如: <!DOCTYPE html> <html lang= ...