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


    实验项目是想要使用多种数据库访问方式,比如 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 在静态语法检查时,实在没办法搞定这个自动装配的判断。

    没关系,不影响运行!

  • 相关阅读:
    Loki 初体验
    柠檬研究院及第一期活动预告
    Apache SkyWalking 为.NET Core带来开箱即用的分布式追踪和应用性能监控
    聊聊AspectCore动态代理中的拦截器
    使用AspectCore动态代理
    AspectCore中的IoC容器和依赖注入
    AspectCore.Extension.Reflection : .NET Core反射扩展库
    Asp.Net Core轻量级Aop解决方案:AspectCore
    [精品书单] C#/.NET 学习之路——从入门到放弃
    创建vue项目
  • 原文地址:https://www.cnblogs.com/matchless/p/10389918.html
Copyright © 2020-2023  润新知