Spring Boot 2 实践记录之 条件装配

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

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

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

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

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

示例:ide

先定义 Service 接口:spa

public interface UserServiceInterface {
    ......  
}

再定义 MyBatis 实现类:code

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

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

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 在静态语法检查时,实在没办法搞定这个自动装配的判断。

不要紧,不影响运行!

相关文章
相关标签/搜索