背景
  
  同一个接口有多种实现,项目启动时按某种规则来选择性的启用其中一种实现,再具体一点,比如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的更多相关文章

  1. Spring boot将配置属性注入到bean类中

    一.@ConfigurationProperties注解的使用 看配置文件,我的是yaml格式的配置: // file application.yml my: servers: - dev.bar.c ...

  2. Spring boot将配置属性注入到bean 专题

    https://blog.csdn.net/wangmx1993328/article/details/81002901 Error starting ApplicationContext. To d ...

  3. 分布式事务、多数据源、分库分表中间件之spring boot基于Atomikos+XADataSource分布式事务配置(100%纯动态)

    本文描述spring boot基于Atomikos+DruidXADataSource分布式事务配置(100%纯动态),也就是增加.减少数据源只需要修改application.properties文件 ...

  4. Spring Boot自动配置与Spring 条件化配置

    SpringBoot自动配置 SpringBoot的自动配置是一个运行时(应用程序启动时)的过程,简化开发时间,无需浪费时间讨论具体的Spring配置,只需考虑如何利用SpringBoot的自动配置即 ...

  5. Spring boot 基于注解方式配置datasource

    Spring boot 基于注解方式配置datasource 编辑 ​ Xml配置 我们先来回顾下,使用xml配置数据源. 步骤: 先加载数据库相关配置文件; 配置数据源; 配置sqlSessionF ...

  6. Sping Boot入门到实战之入门篇(四):Spring Boot自动化配置

    该篇为Sping Boot入门到实战系列入门篇的第四篇.介绍Spring Boot自动化配置的基本原理与实现.   Spring Boot之所以受开发者欢迎, 其中最重要的一个因素就是其自动化配置特性 ...

  7. Springboot 系列(三)Spring Boot 自动配置原理

    注意:本 Spring Boot 系列文章基于 Spring Boot 版本 v2.1.1.RELEASE 进行学习分析,版本不同可能会有细微差别. 前言 关于配置文件可以配置的内容,在 Spring ...

  8. Spring Boot自动配置原理与实践(一)

    前言 Spring Boot众所周知是为了简化Spring的配置,省去XML的复杂化配置(虽然Spring官方推荐也使用Java配置)采用Java+Annotation方式配置.如下几个问题是我刚开始 ...

  9. Spring Boot自动配置原理(转)

    第3章 Spring Boot自动配置原理 3.1 SpringBoot的核心组件模块 首先,我们来简单统计一下SpringBoot核心工程的源码java文件数量: 我们cd到spring-boot- ...

随机推荐

  1. CentOS 5/6上安装EPEL源

    转自:http://www.vckai.com/p/25 EPEL 是什么? EPEL (Extra Packages for Enterprise Linux,企业版Linux的额外软件包) 是Fe ...

  2. linux下实现ssh无密码登录访问

    在192.168.9.51机器上 1)运行:#ssh-keygen -t rsa 2)然后拍两下回车(均选择默认) 3)运行: #ssh-copy-id -i /root/.ssh/id_rsa.pu ...

  3. 客户端SDK测试思路

    本文来自网易云社区 作者:万春艳 是什么 客户端SDK是为第三方开发者提供的软件开发工具包,包括SDK接口.开发文档和Demo示例等.SDK和应用之间是什么关系呢?以云信即时消息服务为例,如下图所示, ...

  4. generator-ivweb 基于react-redux的多页脚手架

    背景 每个公司甚至每个项目组,在开发新项目的时候都会有一些自己特色的东西,比如公共组件,ajax请求拦截处理,内部积累的一些业务逻辑等等,如果没有自己的脚手架,那么拷贝代码成为常态,每个项目的结构,甚 ...

  5. 容器云技术:容器化微服务,Istio占C位出道

    在精彩的软件容器世界中,当新项目涌现并解决你认为早已解决的问题时,这感觉就像地面在你的脚下不断地移动.在许多情况下,这些问题很久以前被解决,但现在的云原生架构正在推动着更大规模的应用程序部署,这就需要 ...

  6. 「专题训练」Machine Schedule(HDU-1150)

    题意 在一个工厂,有两台机器\(A, B\)生产产品.\(A\)机器有\(n\)种工作模式(模式\(0\),模式\(1\)--模式\(n-1\)).\(B\)机器有\(m\)种工作模式(模式\(0\) ...

  7. 关联分析FPGrowth算法在JavaWeb项目中的应用

    关联分析(关联挖掘)是指在交易数据.关系数据或其他信息载体中,查找存在于项目集合或对象集合之间的频繁模式.关联.相关性或因果结构.关联分析的一个典型例子是购物篮分析.通过发现顾客放入购物篮中不同商品之 ...

  8. 微信小程序—day04

    元素水平+垂直居中 昨天的用户页的用户头像,是根据已知的像素大小,设置固定的值,达到居中的效果. 今日切换机型进行适配,发现对不同尺寸大小的屏幕不匹配.所以对wxss进行修改,真正达到水平+垂直居中. ...

  9. VMware 15.0下载及安装教程

    虚拟机 VMware WorkStation Pro15 下载及安装详细解 9虚拟机 VMware WorkStation Pro15 下载及安装详细解. 虚拟机官方网站: https://www.v ...

  10. ZOJ 2760 How Many Shortest Path(最短路径+最大流)

    Description Given a weighted directed graph, we define the shortest path as the path who has the sma ...