Spring实战系列(四)-动态注入接口Bean

小鱼儿 2022-05-22 23:57 532阅读 1赞

“对于Spring框架,现实公司使用的非常广泛,但是由于业务的复杂程度不同,了解到很多小伙伴们利用Spring开发仅仅是利用了Spring的IOC,即使是AOP也很少用,但是目前的Spring是一个大家族,形成了一个很大的生态,覆盖了我们平时开发的方方面面,抛开特殊的苛刻要求之外,Spring的生态其实已经很全面了,所以在此开个系列来研究下Spring提供给我们的一些平时不太却又很实用的内容。”

上一篇我们分析了BeanPostProcessor的基本使用,接下来我们分析下如何使用该类实现动态的接口注入,示例说明:在BeetlSQL框架中,在使用自动扫描注入时,我们通常只需要配置上要扫描的包路径,然后在该路径下声明对应的Dao接口类,这些接口类都默认继承BaseMapper接口类,然后我们在使用这些Dao类的时候,直接根据类型注入(@Autowired)即可使用,这个其实和Mybatis的那一套相似,也和Spring自身的Spring-data框架也类似。这个经常用于框架的开发,那么我就该部分的实现做相应的解释,这三个框架具体实现可能有差距,感兴趣的小伙伴自行去查看源码,我会以一个很简单的例子来讲解大概的实现逻辑。

问题描述:

继承Spring框架,实现声明某个自定义接口(UserMapper),改接口继承通用接口BaseMapper,(通用接口BaseMapper有默认的实现类),实现通过类型注入UserMapper类,然后通过Spring框架的上下文类(ApplicationContext实现类)的getBean()方法拿到UserMapper类来调用内部提供的方法。

1、声明BaseMapper接口类

  1. public interface BaseMapper {
  2. /**
  3. * @param value
  4. */
  5. public void add(String value);
  6. /**
  7. * @param key
  8. */
  9. public void remove(String key);
  10. }

2、默认BaseMapper实现类:CustomBaseMapper

  1. public class CustomBaseMapper implements BaseMapper {
  2. private final Logger logger = Logger.getLogger(this.getClass().getName());
  3. private List<String> dataList = new CopyOnWriteArrayList<>();
  4. /**
  5. * @param value
  6. */
  7. @Override
  8. public void add(String value) {
  9. logger.info("添加数据:" + value);
  10. dataList.add(value);
  11. }
  12. /**
  13. * @param key
  14. */
  15. @Override
  16. public void remove(String key) {
  17. if (dataList.isEmpty())
  18. throw new IllegalArgumentException("Can't remove because the list is Empty!");
  19. }
  20. }

接下来是继承Spring的核心代码

3、首先我们要先定义一个扫描某路径下的类,该类继承ClassPathBeanDefinitionScanner,自定义扫描类:DefaultClassPathScanner

  1. public class DefaultClassPathScanner extends ClassPathBeanDefinitionScanner {
  2. private final String DEFAULT_MAPPER_SUFFIX = "Mapper";
  3. public DefaultClassPathScanner(BeanDefinitionRegistry registry) {
  4. super(registry, false);
  5. }
  6. private String mapperManagerFactoryBean;
  7. /**
  8. * 扫描包下的类-完成自定义的Bean定义类
  9. *
  10. * @param basePackages
  11. * @return
  12. */
  13. @Override
  14. protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
  15. Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
  16. // 如果指定的基础包路径中不存在任何类对象,则提示
  17. if (beanDefinitions.isEmpty()) {
  18. logger.warn("系统没有在 '" + Arrays.toString(basePackages) + "' 包中找到任何Mapper,请检查配置");
  19. } else {
  20. processBeanDefinitions(beanDefinitions);
  21. }
  22. return beanDefinitions;
  23. }
  24. /**
  25. * 注册过滤器-保证正确的类被扫描注入
  26. */
  27. protected void registerFilters() {
  28. addIncludeFilter(new TypeFilter() {
  29. @Override
  30. public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
  31. throws IOException {
  32. String className = metadataReader.getClassMetadata().getClassName();
  33. //TODO 这里设置包含条件-此处是个扩展点,可以根据自定义的类后缀过滤出需要的类
  34. return className.endsWith(DEFAULT_MAPPER_SUFFIX);
  35. }
  36. });
  37. addExcludeFilter(new TypeFilter() {
  38. @Override
  39. public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
  40. throws IOException {
  41. String className = metadataReader.getClassMetadata().getClassName();
  42. return className.endsWith("package-info");
  43. }
  44. });
  45. }
  46. /**
  47. * 重写父类的判断是否能够实例化的组件-该方法是在确认是否真的是isCandidateComponent
  48. * 原方法解释:
  49. * 确定给定的bean定义是否有资格成为候选人。
  50. * 默认实现检查类是否不是接口,也不依赖于封闭类。
  51. * 以在子类中重写。
  52. *
  53. * @param beanDefinition
  54. * @return
  55. */
  56. protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
  57. // 原方法这里是判断是否为顶级类和是否是依赖类(即接口会被排除掉-由于我们需要将接口加进来,所以需要覆盖该方法)
  58. return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
  59. }
  60. /**
  61. * 扩展方法-对扫描到的含有BeetlSqlFactoryBean的Bean描述信息进行遍历
  62. *
  63. * @param beanDefinitions
  64. */
  65. void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
  66. GenericBeanDefinition definition;
  67. for (BeanDefinitionHolder holder : beanDefinitions) {
  68. definition = (GenericBeanDefinition) holder.getBeanDefinition();
  69. String mapperClassName = definition.getBeanClassName();
  70. // 必须在这里加入泛型限定,要不然在spring下会有循环引用的问题
  71. definition.getConstructorArgumentValues().addGenericArgumentValue(mapperClassName);
  72. //依赖注入
  73. definition.getPropertyValues().add("mapperInterface", mapperClassName);
  74. // 根据工厂的名称创建出默认的BaseMapper实现
  75. definition.getPropertyValues().add("mapperManagerFactoryBean", new RuntimeBeanReference(this.mapperManagerFactoryBean));
  76. definition.setBeanClass(BaseMapperFactoryBean.class);
  77. // 设置Mapper按照接口组装
  78. definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
  79. logger.info("已开启自动按照类型注入 '" + holder.getBeanName() + "'.");
  80. }
  81. }
  82. public void setMapperManagerFactoryBean(String mapperManagerFactoryBean) {
  83. this.mapperManagerFactoryBean = mapperManagerFactoryBean;
  84. }
  85. }

4、核心的接口实现类:BaseMapperFactoryBean

  1. public class BaseMapperFactoryBean<T> implements FactoryBean<T>, InitializingBean, ApplicationListener<ApplicationEvent>, ApplicationContextAware {
  2. /**
  3. * 要注入的接口类定义
  4. */
  5. private Class<T> mapperInterface;
  6. /**
  7. * Spring上下文
  8. */
  9. private ApplicationContext applicationContext;
  10. //也因该走工厂方法注入得来
  11. private BaseMapper mapperManagerFactoryBean;
  12. public BaseMapperFactoryBean(Class<T> mapperInterface) {
  13. this.mapperInterface = mapperInterface;
  14. }
  15. @Override
  16. public T getObject() throws Exception {
  17. //采用动态代理生成接口实现类,核心实现
  18. return (T) Proxy.newProxyInstance(applicationContext.getClassLoader(), new Class[]{mapperInterface}, new MapperJavaProxy(mapperManagerFactoryBean, mapperInterface));
  19. }
  20. @Override
  21. public Class<?> getObjectType() {
  22. return this.mapperInterface;
  23. }
  24. @Override
  25. public boolean isSingleton() {
  26. return true;
  27. }
  28. @Override
  29. public void afterPropertiesSet() throws Exception {
  30. //TODO 判断属性的注入是否正确-如mapperInterface判空
  31. if (null == mapperInterface)
  32. throw new IllegalArgumentException("Mapper Interface Can't Be Null!!");
  33. }
  34. /**
  35. * Handle an application event.
  36. *
  37. * @param event the event to respond to
  38. */
  39. @Override
  40. public void onApplicationEvent(ApplicationEvent event) {
  41. //TODO 可依据事件进行扩展
  42. }
  43. @Override
  44. public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
  45. this.applicationContext = applicationContext;
  46. }
  47. public void setMapperInterface(Class<T> mapperInterface) {
  48. this.mapperInterface = mapperInterface;
  49. }
  50. public void setMapperManagerFactoryBean(BaseMapper mapperManagerFactoryBean) {
  51. this.mapperManagerFactoryBean = mapperManagerFactoryBean;
  52. }
  53. }

5、定义默认的BaseMapper的FactoryBean-MapperManagerFactoryBean

  1. public class MapperManagerFactoryBean implements FactoryBean<BaseMapper>, InitializingBean, ApplicationListener<ApplicationEvent> {
  2. @Override
  3. public BaseMapper getObject() throws Exception {
  4. return new CustomBaseMapper();
  5. }
  6. @Override
  7. public Class<?> getObjectType() {
  8. return BaseMapper.class;
  9. }
  10. @Override
  11. public boolean isSingleton() {
  12. return true;
  13. }
  14. @Override
  15. public void afterPropertiesSet() throws Exception {
  16. //TODO 判断属性的注入是否正确
  17. }
  18. /**
  19. * Handle an application event.
  20. *
  21. * @param event the event to respond to
  22. */
  23. @Override
  24. public void onApplicationEvent(ApplicationEvent event) {
  25. System.out.println(event.toString());
  26. }
  27. }

6、核心的java动态代理类:MapperJavaProxy

  1. public class MapperJavaProxy implements InvocationHandler {
  2. private BaseMapper baseMapper;
  3. private Class<?> interfaceClass;
  4. public MapperJavaProxy(BaseMapper baseMapper, Class<?> interfaceClass) {
  5. this.baseMapper = baseMapper;
  6. this.interfaceClass = interfaceClass;
  7. }
  8. @Override
  9. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  10. if (!interfaceClass.isInterface()) {
  11. throw new IllegalArgumentException("mapperInterface is not interface.");
  12. }
  13. if (baseMapper == null) {
  14. baseMapper = new CustomBaseMapper();
  15. }
  16. return method.invoke(baseMapper, args);
  17. }
  18. }

7、调用时的核心配置类:DefaultClassRegistryBeanFactory

  1. public class DefaultClassRegistryBeanFactory implements ApplicationContextAware, BeanDefinitionRegistryPostProcessor, BeanNameAware {
  2. private String scanPackage;
  3. private String beanName;
  4. private String mapperManagerFactoryBean;
  5. private ApplicationContext applicationContext;
  6. public String getScanPackage() {
  7. return scanPackage;
  8. }
  9. public void setScanPackage(String scanPackage) {
  10. this.scanPackage = scanPackage;
  11. }
  12. public String getMapperManagerFactoryBean() {
  13. return mapperManagerFactoryBean;
  14. }
  15. public void setMapperManagerFactoryBean(String mapperManagerFactoryBean) {
  16. this.mapperManagerFactoryBean = mapperManagerFactoryBean;
  17. }
  18. @Override
  19. public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
  20. if (StringUtils.isEmpty(this.scanPackage)) {
  21. throw new IllegalArgumentException("scanPackage can't be null");
  22. }
  23. String basePackage2 = this.applicationContext.getEnvironment().resolvePlaceholders(this.scanPackage);
  24. String[] packages = StringUtils.tokenizeToStringArray(basePackage2, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
  25. DefaultClassPathScanner defaultClassPathScanner = new DefaultClassPathScanner(beanDefinitionRegistry);
  26. defaultClassPathScanner.setMapperManagerFactoryBean(mapperManagerFactoryBean);
  27. defaultClassPathScanner.registerFilters();
  28. defaultClassPathScanner.doScan(packages);
  29. }
  30. @Override
  31. public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
  32. }
  33. @Override
  34. public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
  35. this.applicationContext = applicationContext;
  36. }
  37. @Override
  38. public void setBeanName(String name) {
  39. this.beanName = name;
  40. }
  41. }

8、调用测试

8.1、假设你在包目录:colin.spring.basic.advanced.inject.dao下声明自定义的类UserMapper

  1. public interface UserMapper extends BaseMapper {
  2. }

8.2、声明配置类:ClassRegistryBeanScannerConfig

  1. @Configuration
  2. public class ClassRegistryBeanScannerConfig {
  3. @Bean(name = "mapperManagerFactoryBean")
  4. public MapperManagerFactoryBean configMapperManagerFactoryBean() {
  5. MapperManagerFactoryBean mapperManagerFactoryBean = new MapperManagerFactoryBean();
  6. return mapperManagerFactoryBean;
  7. }
  8. @Bean
  9. public DefaultClassRegistryBeanFactory configDefaultClassRegistryBeanFactory() {
  10. DefaultClassRegistryBeanFactory defaultClassRegistryBeanFactory = new DefaultClassRegistryBeanFactory();
  11. defaultClassRegistryBeanFactory.setScanPackage("colin.spring.basic.advanced.inject.dao");
  12. defaultClassRegistryBeanFactory.setMapperManagerFactoryBean("mapperManagerFactoryBean");
  13. return defaultClassRegistryBeanFactory;
  14. }
  15. }

8.3、测试调用

  1. public static void main(String[] args) {
  2. AnnotationConfigApplicationContext acApplicationCOntext = new AnnotationConfigApplicationContext("colin.spring.basic.advanced.inject");
  3. UserMapper userMapper = acApplicationCOntext.getBean(UserMapper.class);
  4. userMapper.add("lalaldsf");
  5. acApplicationCOntext.stop();
  6. }

总结:

此处对于BeanPostProcessor接口的调用应该属于高级应用了,该思路常用来解决扩展或集成Spring框架,其核心的思路可以分为以下几步:

1、自定义实现类路径扫描类,决定哪些类应该被注入进Spring容器。

2、采用Java动态代理来动态实现对于声明接口类的注入。

3、实现BeanDefinitionRegistryPostProcessor,在Spring初始化初期将需要扫描导入Spring容器的类进行注入。

(PS:需要对Spring的BeanDefinition类有一定的了解,下一张我们来分析该类的)

发表评论

表情:
评论列表 (有 0 条评论,532人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Spring5系列() | 依赖注入

    上篇文章我们讲解了如何通过spring的工厂创建对象。对象有了,但是其实往往我们也不能够直接进行使用,有时候需要对对象进行赋值的操作。而spring中有一个比较重要的概念叫做依