spring容器一个接口多个实现类如何指定

本文介绍在Spring框架下,如何根据不同项目需求,灵活选择并切换接口的实现类。具体包括使用@Autowired、@Qualifier、@Resource、自定义TypeFilter、@Primary及@ConditionalOnProperty等方法。

一个接口在生成过程中难免会遇到场景变更等等需求 需要跟换实现类 或者有多个实现类,不同的实现类有各自的需求,如何在项目中实现根据不同项目,自由切换实现类呢?
1.方法一:使用@Autowired @Qualifier(value = “xxx”)
2.方法二:使用@Resource(name = “xxx”),没有指定bean注入名字的,使用该类首字符小写的bean的名字
3.方法三:使用@Resource(name = “xxx”),通过@Resource注入,根据@Service指定的名称区分
4.自定义TypeFilter指定过滤规则
5.@Primary注解
6.@ConditionalOnProperty通过配置文件来指定

实例1-3:
接口:

package com.cj.interfaces;

public interface ITestService {
    void test();
}


package com.cj.interfaces;
import org.springframework.stereotype.Service;

@Service("s1")
public class TestServiceImpl1 implements ITestService {
    @Override
    public void test() {
        System.out.println("接口1实现类 ...");
    }
}


package com.cj.interfaces;

import org.springframework.stereotype.Service;

@Service
public class TestServiceImpl2 implements ITestService {
    @Override
    public void test() {
        System.out.println("接口2实现类 ...");
    }
}


package com.cj.interfaces;

import org.springframework.stereotype.Service;

@Service("s3")
public class TestServiceImpl3 implements ITestService {
    @Override
    public void test() {
        System.out.println("接口3实现类 ...");
    }
}

package com.cj.interfaces;

import org.junit.Test;

import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import javax.annotation.Resource;

import static org.junit.jupiter.api.Assertions.*;

@RunWith(SpringRunner.class)
@SpringBootTest
public class ITestServiceTest {

    //有指定bean注入名字的,使用bean的名字
    //qualifier的意思是合格者,通过这个标示,表明了哪个实现类才是我们所需要的,我们修改调用代码,添加@Qualifier注解,
    // 需要注意的是@Qualifier的参数名称必须为我们之前定义@Service注解的名称之一!
    @Autowired
    @Qualifier(value = "s1")
    ITestService testService;    //正常启动

    //没有指定bean注入名字的,使用该类首字符小写的bean的名字
    //使用默认的
    @Resource(name = "testServiceImpl2")
    ITestService testService2;    //正常启动

    //通过@Resource注入,根据@Service指定的名称区分
    @Resource(name = "s3")
    ITestService testService3;    //正常启动

    @Test
   public void test1() {
        testService.test();

        testService2.test();

        testService3.test();     
    }
}

结果如图:在这里插入图片描述

实例4:
实现TypeFilter接口

package com.gcxzflgl.filter;
 
import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;
 
import java.io.IOException;
 
public class MyTypeFilter implements TypeFilter {
 
    /**
     * metadataReader:读取到的当前正在扫描的类的信息
     * metadataReaderFactory:可以获取到其他任何类信息的
     */
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
            throws IOException {
        // TODO Auto-generated method stub
        //获取当前类注解的信息
        AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
        //获取当前正在扫描的类的类信息
        ClassMetadata classMetadata = metadataReader.getClassMetadata();
        //获取当前类资源(类的路径)
        Resource resource = metadataReader.getResource();
 
        String className = classMetadata.getClassName();
        System.out.println("--->"+className);
        if(className.contains("ll")){
            return true;
        }
        return false;
    }
}
@ComponentScan(basePackages={"com.ylz.ryjs"},excludeFilters = {@ComponentScan.Filter(type= FilterType.CUSTOM,classes = {ComponentScanFilter.class})})
public class YlzRyjsWebApplication {
//scanBasePackages="com.ylz.ryjs"
	public static void main(String[] args) {
		SpringApplication.run(YlzRyjsWebApplication.class, args);
		
	}

}

实例5:

package com.cj.interfaces;

public interface IPrimaryService {
    void test();
}


package com.cj.interfaces;

import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;

@Service
@Primary
public class TestPrimaryImpl1 implements IPrimaryService{
    @Override
    public void test() {
        System.out.println("Primary 1");
    }
}


package com.cj.interfaces;

import org.springframework.stereotype.Service;

@Service
public class TestPrimaryImpl2  implements IPrimaryService{
    @Override
    public void test() {
        System.out.println("Primary 2");
    }
}


     @Autowired
    IPrimaryService primaryService;

    @Test
   public void test1() {
        primaryService.test();
    }

结果如下:
在这里插入图片描述
实例6:

package com.cj.interfaces;

public interface ITestServiceConditional {
    void test();
}


package com.cj.interfaces;

import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Service;

/**
 * 使用配置文件控制注入:
 * 我们需要在实现类上添加配置文件注解,使用配置文件控制该实现类是否生效
 */
@Service
@Configuration
@ConditionalOnProperty(prefix = "demo.test.service",name = "impl",havingValue = "one")
public class TestServiceImpl4 implements ITestServiceConditional {
    @Override
    public void test() {
        System.out.println("接口4实现类 ...");
    }
}

package com.cj.interfaces;

import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Service;

@Service
@Configuration
@ConditionalOnProperty(prefix = "demo.test.service",name = "impl",havingValue = "two")
public class TestServiceImpl5 implements ITestServiceConditional {
    @Override
    public void test() {
        System.out.println("接口5实现类 ...");
    }
}

demo:
  test:
    service:
      impl: one



    @Autowired
    ITestServiceConditional testServiceConditional;

    @Test
   public void test1() {
        testServiceConditional.test();
    }

结果如下:
在这里插入图片描述

Java项目中,当接口`GitlabService`有多个实现类时,在`GitlabController`中通过`private final GitlabService gitlabService;`定义对象后直接调用方法,如果没有进行额外的配置或处理,Spring框架在启动时会抛出`NoUniqueBeanDefinitionException`异常。这是因为Spring容器在进行依赖注入时,发现有多个`GitlabService`的实现类,无法确定应该将哪个实现类的实例注入到`gitlabService`变量中。 以下是示例代码说明: ```java // GitlabService接口 public interface GitlabService { void doSomething(); } // 实现类1 import org.springframework.stereotype.Service; @Service("gitlabService1") public class GitlabServiceImpl1 implements GitlabService { @Override public void doSomething() { System.out.println("GitlabServiceImpl1 is doing something."); } } // 实现类2 import org.springframework.stereotype.Service; @Service("gitlabService2") public class GitlabServiceImpl2 implements GitlabService { @Override public void doSomething() { System.out.println("GitlabServiceImpl2 is doing something."); } } // GitlabController import org.springframework.stereotype.Controller; import lombok.RequiredArgsConstructor; @Controller @RequiredArgsConstructor public class GitlabController { private final GitlabService gitlabService; public void performAction() { gitlabService.doSomething(); } } ``` 在上述代码中,由于有两个`GitlabService`的实现类`GitlabServiceImpl1`和`GitlabServiceImpl2`,Spring容器在创建`GitlabController`实例时,无法确定将哪个实现类的实例注入到`gitlabService`中,从而会抛出异常。 为了解决这个问题,可以采用以下几种方式: 1. 使用`@Qualifier`注解指定要注入的实现类的名称: ```java import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Controller; import lombok.RequiredArgsConstructor; @Controller @RequiredArgsConstructor public class GitlabController { @Qualifier("gitlabService1") private final GitlabService gitlabService; public void performAction() { gitlabService.doSomething(); } } ``` 2. 使用`@Primary`注解标记首选的实现类: ```java import org.springframework.stereotype.Service; import org.springframework.context.annotation.Primary; @Service @Primary public class GitlabServiceImpl1 implements GitlabService { @Override public void doSomething() { System.out.println("GitlabServiceImpl1 is doing something."); } } ``` 这样,在没有指定具体实现类时,Spring容器会优先注入被`@Primary`注解标记的实现类
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值