• springboot:@ConditionalOnProperty根据不同配置注入不同实现的bean


    一、引言

    在开发中经常会碰到这样的情形,一个接口会有不同的实现,但在开发中都是基于接口的注入,那么怎么根据不同的需求注入不同的类型就是一个值得考虑的问题。在注入属性时常用的两个注解是@Autowired和@Resource,使用它们便可以实现,同时spring提供了很多@ConditionalXXX的注解,可以很好的完成上述功能;

    二、代码演示

    1、问题代码描述

    使用代码的方式描述下上面提到的问题,后面给出解决方案。

    controller类,TestConditionalOnProperty.java

    package com.atssg.controller;
    
    import com.atssg.service.MyService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class TestConditionalOnProperty {
    
    //注入MyService @Autowired
    private MyService myService; @GetMapping("/test/test1") public void test(){ myService.test(); } }

    下面是MyService接口,MyService.java

    package com.atssg.service;
    
    public interface MyService {
         void test();
    }

    下面是两个实现类,MyServiceImpl.java

    package com.atssg.service.impl;
    
    import com.atssg.service.MyService;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.stereotype.Service;
    
    
    @Service
    @Slf4j
    public class MyServiceImpl implements MyService {
    
        @Override
        public void test() {
            log.info("I am Myservice");
        }
    }

    下面是MyServiceImpl2.java

    package com.atssg.service.impl;
    
    import com.atssg.service.MyService;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.stereotype.Service;
    
    @Service
    @Slf4j
    public class MyServiceImpl2 implements MyService {
    
        @Override
        public void test() {
            log.info("I am MyServiceImpl2");
        }
    }

    程序启动报错,

    Description:
    
    Field myService in com.atssg.controller.TestConditionalOnProperty required a single bean, but 2 were found:
        - myServiceImpl: defined in file [D:codecloud2020cloud-sync-7002	argetclassescomatssgserviceimplMyServiceImpl.class]
        - myServiceImpl2: defined in file [D:codecloud2020cloud-sync-7002	argetclassescomatssgserviceimplMyServiceImpl2.class]

    大体意思是TestConditionalOnProperty需要一个单例bean,但是发现了两个,也就是MyServiceImpl和MyServicImpl2。那如何才能注入一个那。

    2、解决方案

    2.1、@Qualifier

    @Autowired默认条件下会按照id注入,找不到id会按照类型注入,上面的错误便是这种情况,我们可以给@Autowired指定要注入的id即可,使用@Qualifier可以实现指定id。

    TestConditionalOnProperty改动如下,

    package com.atssg.controller;
    
    import com.atssg.service.MyService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class TestConditionalOnProperty {
    
        @Qualifier("myServiceImpl2")
        @Autowired
        private MyService myServiceImpl;
    
        @GetMapping("/test/test1")
        public void test(){
    
            myServiceImpl.test();
        }
    
    }

    同理,两个MyService的实现类也要指定生成bean的id。默认情况下是其类名首字母小写,如MyServiceImpl如果不指定生成id则为myServiceImpl。修改如下

    MyServiceImpl.java

    package com.atssg.service.impl;
    
    import com.atssg.service.MyService;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.stereotype.Service;
    
    
    @Service("myServiceImpl")
    @Slf4j
    public class MyServiceImpl implements MyService {
    
        @Override
        public void test() {
            log.info("I am Myservice");
        }
    }

    MyServiceImpl2.java

    package com.atssg.service.impl;
    
    import com.atssg.service.MyService;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.stereotype.Service;
    
    @Service("myServiceImpl2")
    @Slf4j
    public class MyServiceImpl2 implements MyService {
    
        @Override
        public void test() {
            log.info("I am MyServiceImpl2");
        }
    }

    @Service可以指定value值,也就是指定生成bean的id值。

    测试结果如下,

     从上面可以看出已经可以实现注入一个MyService的实现类,但是这种方式有一个弊端,那就是不够灵活,虽然实现了加载一个实现类,但是每次都需要修改代码,而且有可能会修改错误,而且是硬编码。

    其实还有另外一种方式,也是使用@Autowired,只不过被@Autowired注解修饰的变量名必须是要注入的bean的id,如

     这里注入的是myServiceImpl,也就是MyServiceImpl的实现类,测试结果如下,

     这样便实现了注入一个bean的目的,但这种方式和上面的方式是一样的,不够灵活且是硬编码。下面看springboot为我们提供的另外一种方式。

    2.2、@ConditionalOnProperty

    @ConditionalOnProperty注解是springboot开发的众多@ConditionalXX注解中的一个,根据properties文件中的属性值来决定注入哪一个。

    先在applicaiton.properties文件中定义一个变量

    myService=service1

    TestConditionalOnProperty中使用@Resource进行注入

    package com.atssg.controller;
    
    import com.atssg.service.MyService;
    
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import javax.annotation.Resource;
    
    @RestController
    public class TestConditionalOnProperty {
    
    
        @Resource
        private MyService myService;
    
        @GetMapping("/test/test1")
        public void test(){
    
            myService.test();
        }
    
    }

    MyService的两个实现类,MyServiceImpl.java

    package com.atssg.service.impl;
    
    import com.atssg.service.MyService;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
    import org.springframework.stereotype.Component;
    
    @Component
    @ConditionalOnProperty(name = "myService",havingValue = "service1")
    @Slf4j
    public class MyServiceImpl implements MyService {
    
        @Override
        public void test() {
            log.info("I am Myservice");
        }
    }

    MyServiceImpl2.java

    package com.atssg.service.impl;
    
    import com.atssg.service.MyService;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
    import org.springframework.stereotype.Component;
    
    @Component
    @ConditionalOnProperty(name = "myService",havingValue = "service2")
    @Slf4j
    public class MyServiceImpl2 implements MyService {
    
        @Override
        public void test() {
            log.info("I am MyServiceImpl2");
        }
    }

    每个实现类中均使用了@ConditionalOnProperty注解,并指定了name和havingValue属性,name指定applicaiton.properties文件中的属性名,havingValue指定了属性值,在上面的配置的是service1,即调用MyServiceImpl中的test()方法,测试如下

    2021-09-20 19:18:44.499  INFO 23892 --- [)-192.168.117.1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 5 ms
    2021-09-20 19:23:38.576  INFO 23892 --- [nio-8080-exec-1] com.atssg.service.impl.MyServiceImpl     : I am Myservice

    从测试结果来看,调用的的确是MyServiceImpl中的test()方法,那么改成service2的结果如下,

     这样只需要修改配置文件便可以实现动态加载不同的实现类。

    三、总结

    要想实现加载不同的实现类,还有其他的方式,这里不一一列举,本文旨在介绍@ConditionalOnProperty注解的使用。@ConditionalOnProperty注解可以实现根据配置文件中的值注入不同的实现类。

     

    一个爱写文章的程序员,欢迎关注我的公众号“北漂程序员”。我有故事,你有酒吗
  • 相关阅读:
    hdu 5045 Contest
    hdu 4068 SanguoSHA
    TSP 旅行商问题(状态压缩dp)
    haoi2015 树上操作
    noi 2015 软件包管理器(树链剖分)
    zjoi2008 树链剖分
    读入优化
    动态规划类型总结
    有关Rujia Liu 动态规划的·一些总结
    输入优化
  • 原文地址:https://www.cnblogs.com/teach/p/15315046.html
Copyright © 2020-2023  润新知