实验项目是想要使用多种数据库访问方式,比如 JPA 和 MyBatis。

项目的 Service 层业务逻辑相同,只是具体实现代码不同,自然是一组接口,两组实现类的架构比较合理。

不过这种模式却有一个问题,如果 Bean 是按实现类装配,则在切换数据库访问方式时,就需要大量的代码修改。如果按接口装配,则会出现歧义(同一接口有两个实现,无法自动装配)。

虽然可以使用「首选Bean」或「限定」装配,但是与直接使用实现类装配一样,切换数据库访问地,仍然要大量修改源码。

经过实验,使用「条件装配」实现了利用配置切换数据库访问方式,不需要修改代码了。

示例:

先定义 Service 接口:

public interface UserServiceInterface {
......
}

再定义 MyBatis 实现类:

@Service
@Conditional(MybatisCondition.class)
public class UserServiceMybatisImpl implements UserServiceInterface {
......
}

注意其中的 @Conditional(MybatisCondition.class),MybatisCondition 类必须实现 org.springframework.context.annotation.Condition 接口,该接口仅有一个 matches 方法,当该方法返回真时,UserServiceMybatisImpl 被装配。

MybatisCondition 的 matches 方法的逻辑被实现为根据配置文件中的 use.data.access.method 属性是否为 mybatis,来决定是否装配 UserServiceMybatisImpl 类:

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata; public class MybatisCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment env = context.getEnvironment();
if (env.getProperty("use.data.access.method").equals("mybatis")) {
return true;
}
return false;
}
}

再定义 SPA 实现类及其 SpaCondition 类,实现方式与 Mybatis 相同,仅在配置项为 spa 时,装配 UserServiceSpaImpl 类:

@Service
@Conditional(SpaCondition.class)
public class UserServiceSpaImpl implements UserServiceInterface {
......
}
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata; public class SpaCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment env = context.getEnvironment();
if (env.getProperty("use.data.access.method").equals("spa")) {
return true;
}
return false;
}
}

定义一个类,自动装配 UserServiceInterface,并打印其类名:

public class Test {
@Autowired
private UserServiceInterface userServiceInterface; public void testUserService() {
System.out.println(userServiceInterface.getClass();
}
}

现在还不能运行,需要添加配置项。

先将配置项配置为 mybatis:

use.data.access.method = mybatis

运行 Test 类的 testUserService 方法,结果为:

UserServiceMybatisImpl

将配置项修改为 spa:

use.data.access.method = spa

运行 Test 类的 testUserService 方法,结果为:

UserServiceSpaImpl

不过,有个小小的缺憾,就是在 Idea 中,如下代码行:

@Autowired
private UserServiceInterface userServiceInterface;

会有错误提示:

Could not autowire. There is more than one bean of 'UserServiceInterface' type.
Beans:
userServiceMybatisImpl   (UserServiceMybatisImpl.java)
userServiceSpaImpl   (UserServiceSpaImpl.java)

由于配置项是在运行时读取的,Idea 在静态语法检查时,实在没办法搞定这个自动装配的判断。

没关系,不影响运行!

Spring Boot 2 实践记录之 条件装配的更多相关文章

  1. Spring Boot 2 实践记录之 使用 ConfigurationProperties 注解将配置属性匹配至配置类的属性

    在 Spring Boot 2 实践记录之 条件装配 一文中,曾经使用 Condition 类的 ConditionContext 参数获取了配置文件中的配置属性.但那是因为 Spring 提供了将上 ...

  2. Spring Boot 2 实践记录之 封装依赖及尽可能不创建静态方法以避免在 Service 和 Controller 的单元测试中使用 Powermock

    在前面的文章中(Spring Boot 2 实践记录之 Powermock 和 SpringBootTest)提到了使用 Powermock 结合 SpringBootTest.WebMvcTest ...

  3. Spring Boot 2 实践记录之 MyBatis 集成的启动时警告信息问题

    按笔者 Spring Boot 2 实践记录之 MySQL + MyBatis 配置 中的方式,如果想正确运行,需要在 Mapper 类上添加 @Mapper 注解. 但是加入此注解之后,启动时会出现 ...

  4. Spring Boot 2 实践记录之 Redis 及 Session Redis 配置

    先说 Redis 的配置,在一些网上资料中,Spring Boot 的 Redis 除了添加依赖外,还要使用 XML 或 Java 配置文件做些配置,不过经过实践并不需要. 先在 pom 文件中添加 ...

  5. Spring Boot 2 实践记录之 Powermock 和 SpringBootTest

    由于要代码中使用了 Date 类生成实时时间,单元测试中需要 Mock Date 的构造方法,以预设其行为,这就要使用到 PowerMock 在 Spring Boot 的测试套件中,需要添加 @Ru ...

  6. Spring Boot 2 实践记录之 使用 Powermock、Mockito 对 UUID 进行 mock 单元测试

    由于注册时,需要对输入的密码进行加密,使用到了 UUID.sha1.md 等算法.在单元测试时,使用到了 Powermock,记录如下. 先看下加密算法: import org.apache.comm ...

  7. Spring Boot 2 实践记录之 组合注解原理

    Spring 的组合注解功能,网上有很多文章介绍,不过都是介绍其使用方法,鲜有其原理解析. 组合注解并非 Java 的原生能力.就是说,想通过用「注解A」来注解「注解B」,再用「注解B」 来注解 C( ...

  8. Spring Boot 2 实践记录之 MySQL + MyBatis 配置

    如果不需要连接池,那么只需要简单的在pom文件中,添加mysql依赖: <dependency> <groupId>mysql</groupId> <arti ...

  9. Spring Boot 报错记录

    Spring Boot 报错记录 由于新建的项目没有配置数据库连接启动报错,可以通过取消自动数据源自动配置来解决 解决方案1: @SpringBootApplication(exclude = Dat ...

随机推荐

  1. springboot取得resources下的文件

    参考http://blog.csdn.net/programmeryu/article/details/58002218 ResourceUtils.getFile("classpath:p ...

  2. SpringCloud 简单理解

    0.SpringCloud,微服务架构.包括 服务发现(Eureka),断路器(Hystrix),服务网关(Zuul),客户端负载均衡(Ribbon).服务跟踪(Sleuth).消息总线(Bus).消 ...

  3. Dungeon Game (GRAPH - DP)

    QUESTION The demons had captured the princess (P) and imprisoned her in the bottom-right corner of a ...

  4. 最小重组缓冲区和路径MTU发现

    概括: 主要来源于unp,可参考:http://blog.csdn.net/ysu108/article/details/7764461 疑惑: 1. 最小重组缓冲区大小: ipv4为576,ipv6 ...

  5. C++重载、重写(覆盖)、隐藏

    类成员函数中重载/重写(覆盖)/重定义(隐藏)的区别? 答:分别简单讲述一下函数重载,函数覆盖,函数隐藏的概念与特征: 函数重载:重载函数通常用来命名一组功能相似的函数 1.函数要在相同的类域 2.函 ...

  6. fragment 事务回滚 ---动态创建fragment

    import java.util.Date; import java.util.LinkedList; import com.qianfeng.gp08_day23_fragment5.fragmen ...

  7. springboot与elasticsearch

    1.安装elasticsearch 下载elasticsearch docker pull registry.docker-cn.com/library/elasticsearch 运行elastic ...

  8. PHP下ajax跨域的解决方案之jsonp

    首先要说明一下json和jsonp的区别? json是一种基于文本的数据交换方式,或者叫做描述数据的一种格式. var person = { "name": "test& ...

  9. .NET获得上传图片宽高

    代码: //图片大小限制--------------------------- System.Drawing.Image txtStoryimage = System.Drawing.Image.Fr ...

  10. Linux 配置文件管理

    一.简介 参考:https://robots.thoughtbot.com/rcm-for-rc-files-in-dotfiles-repos http://dotfiles.github.io/ ...