栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Spring 中 BeanFactoryAware 的实战之注册一接口多实现场景

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Spring 中 BeanFactoryAware 的实战之注册一接口多实现场景

文章目录
  • 前言
  • 1. 准备工作
    • 1.1 创建一个普通的SpringBoot项目
    • 1.2 项目目录结构
  • 2. 配置文件 application.properties
  • 3. org.feng.util中的类内容
    • 3.1 ApplicationRunnerSupport
    • 3.2 BeanFactorySupport
    • 3.3 Business
    • 3.4 BusinessType
    • 3.5 CommonsUtil
    • 3.6 SpringUtils
  • 4. 测试运行
    • 4.1 BusinessService
    • 4.2 AddServiceImpl
    • 4.3 UpdateServiceImpl
  • 5. 启动测试
    • 5.1 测试类
    • 5.2 控制台输出
  • 6. 总结

前言

说说问题出现的场景:

在区分业务场景的前提下,有一种多租户的情景,就是每种业务实现都会有多个。
一般出现在多平台的对接的时候。
这个时候,需要将某一接口下的多个实现类注入到Spring容器中进行管理。
但是,获取 Bean 和使用的时候,却不太好使。可能还很麻烦。

今天就处理这个问题!

1. 准备工作 1.1 创建一个普通的SpringBoot项目

引入依赖需要 lombok和web的starter。
然后是事先了解:

  • BeanFactoryAware:在实现这个接口后,可以获取到 BeanFactory,可以用来手动注册 Bean。
  • ApplicationRunner:在Spring初始化后执行该实现的方法。
  • ApplicationContextAware: 实现该接口后,可以获取 applicationContext 对象。可以从 Spring 中获取Bean。
  • PostConstruct:该注解使用在非静态方法上,表示在依赖注入之后自动执行。
1.2 项目目录结构


其中,service 包是为了验证功能写的,所有的要用到的类、接口、注解都放在了这个 org.feng.util包中。

2. 配置文件 application.properties

文件中定义的是需要扫描的包名,多个包时使用英文逗号隔开。

business.scan.path = org.feng.service.add,org.feng.service.update
3. org.feng.util中的类内容 3.1 ApplicationRunnerSupport
package org.feng.util;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.Set;


@Slf4j
@Order(1)
@Component
public class ApplicationRunnerSupport implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) {
        // 获得注册 bean 时使用的bean命名
        Set beanNames = BeanFactorySupport.getBeanNames();
        for (String beanName : beanNames) {
            Object bean = SpringUtils.getBean(beanName);
            CommonsUtil.BEANS.put(beanName, bean);
            log.info("注册Bean {} 成功!", bean);
        }
        log.info("注册结束,共注册 {} 个 Bean", CommonsUtil.BEANS.size());
        BeanFactorySupport.getBeanNames().clear();
    }
}
3.2 BeanFactorySupport
package org.feng.util;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.util.CollectionUtils;

import javax.annotation.PostConstruct;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;


@Slf4j
@Configuration
public class BeanFactorySupport implements BeanFactoryAware {
    private static BeanFactory beanFactory;
    
    private static final Set BEAN_NAMES = new HashSet<>(16);

    
    @Value("${business.scan.path}")
    private String path;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        BeanFactorySupport.beanFactory = beanFactory;
    }

    public static BeanFactory getBeanFactory() {
        return BeanFactorySupport.beanFactory;
    }

    public static Set getBeanNames() {
        return BEAN_NAMES;
    }

    
    @PostConstruct
    public void beforeInit() throws ClassNotFoundException {
        Objects.requireNonNull(path);
        String[] split = path.split(",");

        for (String packagePath : split) {
            if (!"".equals(packagePath)) {
                register(packagePath);
            }
        }
    }

    
    public static void register(String path) throws ClassNotFoundException {
        DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) getBeanFactory();
        ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
        // 扫描带有自定义注解的类
        provider.addIncludeFilter(new AnnotationTypeFilter(Business.class));
        Set scanList = provider.findCandidateComponents(path);
        if (CollectionUtils.isEmpty(scanList)) {
            log.error("未扫描到 Bean 资源....");
            return;
        }

        // 注册Bean
        for (BeanDefinition beanDefinition : scanList) {
            Business business = Class.forName(beanDefinition.getBeanClassName()).getAnnotation(Business.class);
            String prefix = business.prefix();
            String beanName = String.join("-", prefix, beanDefinition.getBeanClassName(), business.business());
            log.info("注册:{}", beanName);
            BEAN_NAMES.add(beanName);
            beanFactory.registerBeanDefinition(beanName, beanDefinition);
        }
    }
}
3.3 Business
package org.feng.util;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Business {
    
    String prefix() default "";

    
    String business() default "";
}
3.4 BusinessType
package org.feng.util;


public interface BusinessType {
    String ADD = "add";
    String UPDATE = "update";
    String DELETE = "delete";
    String SEARCH = "search";
}
3.5 CommonsUtil
package org.feng.util;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


public class CommonsUtil {
    public static final Map BEANS = new ConcurrentHashMap<>();
}
3.6 SpringUtils
package org.feng.util;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.NonNull;

import java.util.Objects;


@Slf4j
@Configuration
public class SpringUtils implements ApplicationContextAware {
    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SpringUtils.applicationContext = applicationContext;
    }

    
    public static  T getBean(String beanName, Class clazz) {
        return applicationContext.getBean(beanName, clazz);
    }

    
    public static Object getBean(String beanName) {
        return applicationContext.getBean(beanName);
    }

    
    public static Object getBusinessBean(@NonNull String prefix, @NonNull String beanName, @NonNull String businessType) {
        String beanKey = String.join("-", prefix, beanName, businessType);
        Object bean = CommonsUtil.BEANS.get(beanKey);
        if (Objects.isNull(bean)) {
            log.debug("参数错误:prefix={},beanName={},businessType={}", prefix, beanName, businessType);
            throw new IllegalArgumentException("参数错误,无法找到对应的 Bean");
        }
        return bean;
    }
}
4. 测试运行

在 service包中:

定义一个接口,两个实现类。

4.1 BusinessService
package org.feng.service;


public interface BusinessService {
    String business();
}
4.2 AddServiceImpl
package org.feng.service.add;

import org.feng.service.BusinessService;
import org.feng.util.Business;
import org.feng.util.BusinessType;


@Business(prefix = "Feng", business = BusinessType.ADD)
public class AddServiceImpl implements BusinessService {
    @Override
    public String business() {
        return "增加的业务 AddServiceImpl";
    }
}
4.3 UpdateServiceImpl
package org.feng.service.update;

import org.feng.service.BusinessService;
import org.feng.util.Business;
import org.feng.util.BusinessType;


@Business(prefix = "Feng", business = BusinessType.UPDATE)
public class UpdateServiceImpl implements BusinessService {
    @Override
    public String business() {
        return "更新的业务 UpdateServiceImpl";
    }
}
5. 启动测试 5.1 测试类
package org.feng;

import org.feng.service.add.AddServiceImpl;
import org.feng.util.BusinessType;
import org.feng.util.SpringUtils;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class SpringbootDemoApplicationTests {
    @Test
    void contextLoads() {
        Object businessBean = SpringUtils.getBusinessBean("Feng", AddServiceImpl.class.getName(), BusinessType.ADD);
        AddServiceImpl businessBean1 = (AddServiceImpl) businessBean;
        System.out.println(businessBean1.business());
    }
}

5.2 控制台输出
2021-11-11 17:28:23.674  INFO 19096 --- [           main] org.feng.util.BeanFactorySupport         : 注册:Feng-org.feng.service.add.AddServiceImpl-add
2021-11-11 17:28:23.676  INFO 19096 --- [           main] org.feng.util.BeanFactorySupport         : 注册:Feng-org.feng.service.update.UpdateServiceImpl-update
2021-11-11 17:28:24.259  INFO 19096 --- [           main] org.feng.SpringbootDemoApplicationTests  : Started SpringbootDemoApplicationTests in 1.339 seconds (JVM running for 2.104)
2021-11-11 17:28:24.262  INFO 19096 --- [           main] org.feng.util.ApplicationRunnerSupport   : 注册Bean org.feng.service.update.UpdateServiceImpl@18d900 成功!
2021-11-11 17:28:24.263  INFO 19096 --- [           main] org.feng.util.ApplicationRunnerSupport   : 注册Bean org.feng.service.add.AddServiceImpl@9f5761 成功!
2021-11-11 17:28:24.263  INFO 19096 --- [           main] org.feng.util.ApplicationRunnerSupport   : 注册结束,共注册 2 个 Bean
增加的业务 AddServiceImpl
6. 总结

本次测试圆满成功,在一接口,多实现的情境下,可以自主控制注入 Bean 到Spring中,并使用工具类获取。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/462123.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号