一个接口多个实现类,@Primary帮你智能选择

背景:

        最近做产品的开发,其中涉及到文件上传的功能,不同的场景使用的文件存储方式不一样,比如本地文件存储、FastDfs存储、minio存储,如何做到不改动业务代码,快速切换呢。

实现方式:

        首先Service层需要一个接口,包含一个上传方法。

public interface ISysFileService
{
    /**
     * 文件上传接口
     * 
     * @param file 上传的文件
     * @return 访问地址
     * @throws Exception
     */
    public String uploadFile(MultipartFile file) throws Exception;
}

         其次分别实现三种不同的文件上传方法。

         1、本地存储

@Service
public class LocalSysFileServiceImpl implements ISysFileService
{
    /**
     * 资源映射路径 前缀
     */
    @Value("${file.prefix}")
    public String localFilePrefix;

    /**
     * 域名或本机访问地址
     */
    @Value("${file.domain}")
    public String domain;
    
    /**
     * 上传文件存储在本地的根路径
     */
    @Value("${file.path}")
    private String localFilePath;

    /**
     * 本地文件上传接口
     * 
     * @param file 上传的文件
     * @return 访问地址
     * @throws Exception
     */
    @Override
    public String uploadFile(MultipartFile file) throws Exception
    {
        String name = FileUploadUtils.upload(localFilePath, file);
        String url = domain + localFilePrefix + name;
        return url;
    }
}

         2、FastDfs存储。

@Service
public class FastDfsSysFileServiceImpl implements ISysFileService
{
    /**
     * 域名或本机访问地址
     */
    @Value("${fdfs.domain}")
    public String domain;

    @Autowired
    private FastFileStorageClient storageClient;

    /**
     * FastDfs文件上传接口
     * 
     * @param file 上传的文件
     * @return 访问地址
     * @throws Exception
     */
    @Override
    public String uploadFile(MultipartFile file) throws Exception
    {
        StorePath storePath = storageClient.uploadFile(file.getInputStream(), file.getSize(),
                FilenameUtils.getExtension(file.getOriginalFilename()), null);
        return domain + "/" + storePath.getFullPath();
    }
}

        3、minio存储

@Service
public class MinioSysFileServiceImpl implements ISysFileService
{
    @Autowired
    private MinioConfig minioConfig;

    @Autowired
    private MinioClient client;

    /**
     * 本地文件上传接口
     * 
     * @param file 上传的文件
     * @return 访问地址
     * @throws Exception
     */
    @Override
    public String uploadFile(MultipartFile file) throws Exception
    {
        String fileName = FileUploadUtils.extractFilename(file);
        PutObjectArgs args = PutObjectArgs.builder()
                .bucket(minioConfig.getBucketName())
                .object(fileName)
                .stream(file.getInputStream(), file.getSize(), -1)
                .contentType(file.getContentType())
                .build();
        client.putObject(args);
        return minioConfig.getUrl() + "/" + minioConfig.getBucketName() + "/" + fileName;
    }
}

       以上三个具体实现都完成了,如果想选择其中某一种存储怎么办呢,直接在实现类上添加

@Primary就可以自由切换了。

      那么原理是什么呢?在我们创建bean时,会先获取bean的依赖bean,当匹配的bean的个数大于1时,

//如果匹配的bean⼤于1,执⾏determineAutowireCandidate⽅法
if (matchingBeans.size() > 1) {
autowiredBeanName = this.determineAutowireCandidate(matchingBeans, descriptor);

在determineAutowireCandidate方法中会进行判断

String primaryCandidate = this.determinePrimaryCandidate(candidates, requiredType);
if (primaryCandidate != null) {
return primaryCandidate;
} else {
String priorityCandidate = this.determineHighestPriorityCandidate(candidates, requiredType);
if (priorityCandidate != null) {
return priorityCandidate;
} else {
Iterator var6 = candidates.entrySet().iterator();
String candidateName;
Object beanInstance;
do {
if (!var6.hasNext()) {
return null;
}

在determinePrimaryCandidate中,会遍历所有同类型的bean,并找到带有Primary注解的bean进行返回,如果有多个就会报错。

Iterator var4 = candidates.entrySet().iterator();
while(var4.hasNext()) {
Entry<String, Object> entry = (Entry)var4.next();
String candidateBeanName = (String)entry.getKey();
Object beanInstance = entry.getValue();
if (this.isPrimary(candidateBeanName, beanInstance)) {
if (primaryBeanName != null) {
boolean candidateLocal = this.containsBeanDefinition(candidateBeanName);
boolean primaryLocal = this.containsBeanDefinition(primaryBeanName);
//isPrimary⽅法是判断是否有@Primary注解,如果有多个满⾜条件的bean有@Primary注解就会抛出异常:
if (candidateLocal && primaryLocal) {
throw new NoUniqueBeanDefinitionException(requiredType, candidates.size(), "more than one 'primary' bean found among candidates: " + candid }
if (candidateLocal) {
primaryBeanName = candidateBeanName;
}
} else {
primaryBeanName = candidateBeanName;
}
}
}
return primaryBeanName;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

chenjp111111

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值