最近碰到个这样的需求,需要同一套代码适配个版本数据库(数据库不同,且部分表的字段及关联关系可能会不同),即这套代码配置不同的数据库都能跑。项目采用的框架为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. nexus php composer 私服搭建

    nexus 社区也提供了php composer 私服(当前还在开发中,还没有ga),测试使用构建好的docker 镜像 环境准备 docker-compose 文件 version: "3 ...

  2. Linux构建DNS主从服务器

    所有服务器:iptables -Fsystemctl stop firewalldsetenforce 0 配置yum 主服务器:[root@localhost ~]# yum -y install ...

  3. 8.11 NOIP模拟测试17 入阵曲+将军令+星空

    T1 入阵曲 前缀和维护可以得60分 f[x1][y1][x2][y2]=sum[x2][y2]-sum[x1-1][y2]-sum[x2][y1-1]+sum[x1-1][y1-1];  O(n4) ...

  4. 洛谷P5437/5442 约定(概率期望,拉格朗日插值,自然数幂)

    题目大意:$n$ 个点的完全图,点 $i$ 和点 $j$ 的边权为 $(i+j)^k$.随机一个生成树,问这个生成树边权和的期望对 $998244353$ 取模的值. 对于P5437:$1\le n\ ...

  5. [LeetCode] 24. Swap Nodes in Pairs 成对交换节点

    Given a linked list, swap every two adjacent nodes and return its head. You may not modify the value ...

  6. c语言编译器一个不会报错的陷阱

    1, 由于数字1和小写字母L(l)长得特别像,特别是VS默认字体里的,所以 double a; scanf("%1f",&a); double b; scanf(" ...

  7. python3 获取当前日期的时间戳,以及n天后的日期时间戳

    #coding=utf- import time import datetime t=datetime.datetime.now() #当前日期 t1 =t.strftime('%Y-%m-%d 00 ...

  8. Knative Serving 进阶: Knative Serving SDK 开发实践

    作者 | 阿里云智能事业群技术专家 牛秋霖(冬岛) 导读:通过前面的一系列文章你已经知道如何基于 kubectl 来操作 Knative 的各种资源.但是如果想要在项目中集成 Knative 仅仅使用 ...

  9. HUSKY CLOCK1.0上线啦!

    有人需要HUSKY CLOCK1.0下载资源的请联系1335415335@qq.com! 感谢支持,您的认可是我们前进的动力!

  10. freemarker中8个常用的指令

    这里列举出Freemarker模板文件中8个常用的指令. 1. assign assign指令用于创建或替换一个顶层变量,assign指令的用法有多种,包括创建或替换一个顶层变量,创建或替换多个变量等 ...