Spring-Boot基于配置按条件装Bean
背景
同一个接口有多种实现,项目启动时按某种规则来选择性的启用其中一种实现,再具体一点,比如Controller初始化的时候,根据配置文件的指定的实现类前缀,来记载具体Service,不同Service使用不同的Dao和数据库。
看到这里,我们会想到使用SPI机制,或Spring按条件加载Bean机制来实现,下面主要讨论后者。
定义接口
定义2个Service层接口:OrderService、OrderPromotionService,分别有一个方法,如下:
// OrderService.java
public interface OrderService {
/**
* 通过tid查询订单信息
* @param tid 订单主键
*/
List<Order> findByTid(Long tid);
}
...
// OrderPromotionService.java
public interface OrderPromotionService {
/**
* 通过tid获取促销详情.
* @param tid 订单唯一标识
*/
默认实现
分别实现上面2个接口的各自的方法,包路径:com.a.b。
为达到根据规则装载不同ServiceImpl的目的,需要使用@Conditional注解,并且实现规则定义DefaultCondition。当Spring扫描到@Service注解时,会判断DefaultCondition#matches()方法,决定是否装载该ServiceImpl。
DefaultCondition实现如下:
// DefaultCondition.java
public class DefaultCondition extends ParentCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
Environment environment = conditionContext.getEnvironment();
String interfaceName = getInterFaceName(annotatedTypeMetadata);
String implPrefix = environment.getProperty(interfaceName);
if (StringUtils.isEmpty(implPrefix)) {
return true;
}
return Constant.DEFAULT_PREFIX.equals(implPrefix);
}
}
...
// Constant.java
public class Constant {
public static final String THIRD_PARTY_PREFIX = "ThirdParty";
public static final String DEFAULT_PREFIX = "Default";
DefaultCondition实现了Spring的Condition接口,先通过方法ParentCondition#getInterFaceName()获取ServiceImpl实现的接口名称interfaceName,然后从配置文件中获取该接口指定的实现类的前缀implPrefix,然后判断是否为Constant.DEFAULT_PREFIX,如果是,则装载该ServiceImpl。
获取接口名称的实现定义在ParentCondition类中(是自己实现的),是这里需要继承ParentCondition的原因。
Service层实现
// DefaultOrderServiceImpl.java
@Conditional(DefaultCondition.class)
@Service
public class DefaultOrderServiceImpl implements OrderService {
@Autowired
private OrderDao orderDao;
@Override
public List<Order> findByTid(Long tid) {
return orderDao.findByTid(tid);
}
}
...
/**
* 默认实现.
*/
@Conditional(DefaultCondition.class)
@Service
public class DefaultOrderPromotionServiceImpl implements OrderPromotionService {
@Autowired
private OrderPromotionDao promotionDao;
@Override
public List<OrderPromotion> findByTid(Long tid) {
Dao层实现
// OrderDao.java
@Repository
public class OrderDao {
@Autowired
@Qualifier("firstJdbcTemplate")
private JdbcTemplate jdbcTemplate;
public List<Order> findByTid(Long tid) {
// 省略...
}
}
...
@Repository
public class OrderPromotionDao {
@Autowired
@Qualifier("firstJdbcTemplate")
private JdbcTemplate jdbcTemplate;
public List<OrderPromotion> findByTid(final Long tid) {
// 省略...
Dao层使用的是自己的数据源,代码注入的是firstJdbcTemplate,Spring-Boot多数据源配置不是今天讨论的重点,这里不再详细说明。
第三方实现
这里假设上面的2个Service接口还有一种第三方的实现,包路径:com.c.d。跟默认实现类似,这里也要定义自己的ServiceImpl装载规则ThirdPartyCondition,实现如下:
// ThirdPartyOrderServiceImpl.java
public class ThirdPartyCondition extends ParentCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
Environment environment = conditionContext.getEnvironment();
String interfaceName = getInterFaceName(annotatedTypeMetadata);
String implPrefix = environment.getProperty(interfaceName);
return Constant.THIRD_PARTY_PREFIX.equals(implPrefix);
先获取ServiceImpl实现的接口名称interfaceName,然后从配置文件中获取该接口指定的实现类的前缀implPrefix,然后判断是否为Constant.THIRD_PARTY_PREFIX,如果是,则装载该ServiceImpl。
Service层实现
// ThirdPartyOrderServiceImpl.java
@Conditional({ThirdPartyCondition.class})
@Service
public class ThirdPartyOrderServiceImpl implements OrderService {
@Autowired
private ThirdPartyOrderDao thirdPartyOrderDao;
@Override
public List<Order> findByTid(Long tid) {
return thirdPartyOrderDao.findByTid(tid);
}
}
...
//ThirdPartyOrderPromotionServiceImpl.java
@Conditional(ThirdPartyCondition.class)
@Service
public class ThirdPartyOrderPromotionServiceImpl implements OrderPromotionService {
@Autowired
private ThirdPartyOrderPromotionDao thirdPartyOrderPromotionDao;
@Override
public List<OrderPromotion> findByTid(Long tid) {
Dao层实现
省略,Dao层实现使用另一个数据源,注入secondJdbcTemplate。
以上分别为OrderService和OrderPromotionService提供了两种实现,如下:
Service接口 Service默认实现 Service第三方实现
OrderService DefaultOrderServiceImp
使用Dao层OrderDao,数据源first www.michenggw.com JdbcTemplate ThirdPartyOrderServiceImpl
使用Dao层ThirdPartyOrderDao,数据源secondJdbcTemplate
OrderPromotionService DefaultOrderPromotionServiceImpl
使用Dao层DefaultOrderPromotionServiceImpl,数据源firstJdbcTemplate ThirdPartyOrderPromotionServiceImpl
使用Dao层ThirdPartyOrderPromotionDao,数据源secondJdbcTemplate
配置规则
ServiceImpl定义完成,装载规则也定义了,下面我们在Spring-Boot中分别指定两个类的加载对象
// application.properties
com.free.spring.jdbc.demo.service.OrderPromotionService=ThirdParty
com.free.spring.jdbc.demo.service.OrderService=Default
如上,定义了OrderPromotionService接口使用第三方的实现,OrderService接口使用默认实现
下面简单的写两个Controller看一下效果。
运行效果
1. 数据准备
为first库的2张表分别添加1条数据,如下:
为second库的2张表添加数据
2. Controller定义
// OrderController.java
@RestController
@RequestMapping(www.dasheng178.com"/order")
public class OrderController {
@Autowired
private OrderService orderService;
@GetMapping("/{tid}")
@ResponseBody
public List<Order>www.haom178.com findByTid(@PathVariable(www.gouyiflb.cn"tid") Long tid) {
return orderService.findByTid(tid);
}
}
...
// OrderPromotionController.java
@RestController
@RequestMapping("/promotion")
public class OrderPromotionController {
@Autowired
private OrderPromotionService promotionService;
@GetMapping("www.gcyl159.com/ /{tid}")
@ResponseBody
public List<OrderPromotion> query(@PathVariable("tid") Long tid) {
try {
return promotionService.findByTid(tid);
} catch (Exception e) {
e.printStackTrace();
}
return null;
我们通过Controller分别请求OrderService和OrderPromotionService,通过返回的数据判断是否真的实现了选择性装载ServiceImpl。
然后我们分别请求上面两个接口,观察结果:
OK!没问题!
如果大家有更简单的方式,欢迎探讨~
Spring-Boot基于配置按条件装Bean的更多相关文章
- Spring boot将配置属性注入到bean类中
一.@ConfigurationProperties注解的使用 看配置文件,我的是yaml格式的配置: // file application.yml my: servers: - dev.bar.c ...
- Spring boot将配置属性注入到bean 专题
https://blog.csdn.net/wangmx1993328/article/details/81002901 Error starting ApplicationContext. To d ...
- 分布式事务、多数据源、分库分表中间件之spring boot基于Atomikos+XADataSource分布式事务配置(100%纯动态)
本文描述spring boot基于Atomikos+DruidXADataSource分布式事务配置(100%纯动态),也就是增加.减少数据源只需要修改application.properties文件 ...
- Spring Boot自动配置与Spring 条件化配置
SpringBoot自动配置 SpringBoot的自动配置是一个运行时(应用程序启动时)的过程,简化开发时间,无需浪费时间讨论具体的Spring配置,只需考虑如何利用SpringBoot的自动配置即 ...
- Spring boot 基于注解方式配置datasource
Spring boot 基于注解方式配置datasource 编辑 Xml配置 我们先来回顾下,使用xml配置数据源. 步骤: 先加载数据库相关配置文件; 配置数据源; 配置sqlSessionF ...
- Sping Boot入门到实战之入门篇(四):Spring Boot自动化配置
该篇为Sping Boot入门到实战系列入门篇的第四篇.介绍Spring Boot自动化配置的基本原理与实现. Spring Boot之所以受开发者欢迎, 其中最重要的一个因素就是其自动化配置特性 ...
- Springboot 系列(三)Spring Boot 自动配置原理
注意:本 Spring Boot 系列文章基于 Spring Boot 版本 v2.1.1.RELEASE 进行学习分析,版本不同可能会有细微差别. 前言 关于配置文件可以配置的内容,在 Spring ...
- Spring Boot自动配置原理与实践(一)
前言 Spring Boot众所周知是为了简化Spring的配置,省去XML的复杂化配置(虽然Spring官方推荐也使用Java配置)采用Java+Annotation方式配置.如下几个问题是我刚开始 ...
- Spring Boot自动配置原理(转)
第3章 Spring Boot自动配置原理 3.1 SpringBoot的核心组件模块 首先,我们来简单统计一下SpringBoot核心工程的源码java文件数量: 我们cd到spring-boot- ...
随机推荐
- CC2640 LaunchPad入门试用-第一篇
1. 先安装固件ble_cc26xx_setupwin32_2_01_00_44423.exe. 2. 打开IAR先找到一个例程测试一下D:\ti\simplelink\ble_cc26xx_2_01 ...
- 「日常训练」Equation(HDU-5937)
题意与分析 时隔一个月之后来补题.说写掉的肯定会写掉. 题意是这样的:给1~9这些数字,每个数字有\(X_i\)个,问总共能凑成多少个不同的等式\(A+B=C\)(\(A,B,C\)均为1位,\(1+ ...
- Java+Selenium 3.x 实现Web自动化 - 1.自动化准备
(一)自动化准备 说明:本文主要记录了基于公司现有项目(一个电子商务平台),从0开始实现UI自动化的历程.从准备阶段,部分内容直接省略了基础知识,一切以最终做成自动化项目为目标,难免会有晦涩之处.文章 ...
- Python全栈 Web(概述、HTML基础语法)
原文地址: https://yq.aliyun.com/articles/631222 ........................................................ ...
- [转载]CENTOS 6.0 iptables 开放端口80 3306 22端口
原文地址:6.0 iptables 开放端口80 3306 22端口">CENTOS 6.0 iptables 开放端口80 3306 22端口作者:云淡风轻 #/sbin/iptab ...
- ThinkPHP - 3 - IDE选择以及Eclipse PDT打开ThinkPHP项目
ThinkPHP框架已部署到SAE(新浪云),且代码已获取到本地.眼前面临的问题就是,对ThinkPHP项目选择哪种开发工具(IDE)? 经过简单的查找比较,以及电脑里已装有Eclipse的因素,遂决 ...
- 测试下markdown!
目录 目的 代码 目的 测试markdown 代码 void static void main(args String[]){ System.out.println("hollw" ...
- GitHub把自己整个文件夹上传
我已经有了自己github,但是我怎么对我的项目进行上传呢,普通的上传只有上传单一的文件 这不我去下载了Git(链接至机房ftp文件夹下文件ftp://10.64.130.1/%C8%ED%BC%FE ...
- 软件工程-东北师大站-第二次作业psp
1.本周PSP 2.本周进度条 3.本周累计进度图 代码累计折线图 博文字数累计折线图 本周PSP饼状图
- oracle数据库之触发器
触发器是许多关系数据库系统都提供的一项技术.在 ORACLE 系统里,触发器类似过程和函数,都有声明,执行和异常处理过程的 PL/SQL 块. 一. 触发器类型 触发器在数据库里以独立的对象存储,它与 ...