zoukankan      html  css  js  c++  java
  • 山寨一个Spring的@Component注解

    1. 前言

    我们在上一篇Mybatis如何将Mapper接口注入Spring IoC进行了分析,有同学问胖哥这个有什么用,这个作用其实挺大的,比如让你实现一个类似@Controller的注解(或者继承某个统一接口)来完成比如定时任务的统一注入或者Websocket处理器的统一注入等这种将某种共性的Bean动态注入。

    // 模仿 Controller  
    @XBean(description = "ETL JOB")
    public class JobShedule {
    
        @Caller(cron = "* * 0/5 * * ?")
        public void exec(){
            // job 
        }
    }
    

    以上伪代码就是一个模仿Controller的定时任务Bean。

    2. 设计思路

    详细的开发设计思路我已经总结好了,各位同学只要按部就班就可以实现这个功能了。

    2.1 定义扫描注解

    定义一个类似@MappScan的进行导入自定义ImportBeanDefinitionRegistrar,并指定扫描包范围。

    @Documented
    @Inherited
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE})
    @Import(XBeanDefinitionRegistrar.class)
    public @interface XBeanScan {
    
        String[] basePackages();
    }
    

    我们自定义了一个扫描注解@XBeanScan。它有两个作用:

    • 通过basePackages指定扫描包的范围。
    • 导入我们自定义ImportBeanDefinitionRegistrar 的实现XBeanDefinitionRegistrar

    2.2 定义目标Bean的通用标记

    通常我们可以选择一个标识接口,所有其实现类都会注入Spring IoC;或者用更加方便的注解,所有被该注解标记的类都将注入Spring IoC。这里我们使用更加灵活方便的注解,实现了一个@XBean标记注解:

    @Documented
    @Inherited
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE})
    public @interface XBean {
        String description() default "";
    }
    

    2.3 实现扫描器

    Spring框架为我们提供了扫描器来注册被标记的Bean,它就是上节提到的ClassPathBeanDefinitionScanner,我们继承它进行稍加改造:

    public class XBeanDefinitionScanner extends ClassPathBeanDefinitionScanner {
        public XBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) {
            super(registry, useDefaultFilters);
            super.addIncludeFilter(new AnnotationTypeFilter(XBean.class));
        }
    }
    

    这里我们不使用默认的过滤器,我们指定了扫描器扫描的目标为被@XBean标记的那些Bean

    2.4 实现 Bean 注册机

    重头戏来了,我们需要将2.12.3定义的这些组件在ImportBeanDefinitionRegistrar的实现中组装起来。

    /**
     * The type X bean definition registrar.
     *
     * @author felord.cn
     * @since 2020 /9/18 22:59
     */
    public class XBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
        private ResourceLoader resourceLoader;
    
        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            // 不使用默认过滤器
            XBeanDefinitionScanner xBeanDefinitionScanner = new XBeanDefinitionScanner(registry, false);
            xBeanDefinitionScanner.setResourceLoader(resourceLoader);
            // 扫描XBeanScan注解指定的包
            xBeanDefinitionScanner.scan(getBasePackagesToScan(importingClassMetadata));
        }
    
        @Override
        public void setResourceLoader(ResourceLoader resourceLoader) {
            this.resourceLoader = resourceLoader;
        }
    
        /**
         * 获取{@link XBeanScan}中声明的扫描包路径
         * @param metadata the meta
         * @return  包路径数组
         */
        private String[] getBasePackagesToScan(AnnotationMetadata metadata) {
            String name = XBeanScan.class.getName();
            AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name, true));
            Assert.notNull(attributes, () -> "No auto-configuration attributes found. Is " + metadata.getClassName()
                    + " annotated with " + ClassUtils.getShortName(name) + "?");
            return attributes.getStringArray("basePackages");
        }
    }
    

    从注解元数据importingClassMetadata解析我们需要的扫描路径basePackages等元数据,然后让扫描器在该路径扫描即可。

    2.5 使用

    在具有@Configuration标记的类或者Spring BootMain类上使用@XBeanScan即可,是不是非常简单!

    其实@ComponentScan提供类似的功能。

    3. 总结

    本篇是对上一篇理论的具体应用,说实话上一篇比较枯燥甚至抓不住重点,但是有时候理论就是这样的。一旦你结合本篇来看你会恍然大悟。如果你需要更加细粒度控制就加上那些BeanDefinitionRegistryPostProcessorFactoryBeanSpring提供的功能性接口。从这两篇中更多需要你学习的是如何从阅读源码中触类旁通,来利用已有的组件来实现自己的逻辑。这对你的提高是极大的。好了今天就到这里,多多关注:码农小胖哥 更多干货等着你。

    关注公众号:Felordcn 获取更多资讯

    个人博客:https://felord.cn

  • 相关阅读:
    解决远程连接mysql很慢的方法(网络正常)
    分布式系统中可用性及容错性的区别
    设计模式个人思考
    记第一次多用户在Git提交代码
    Git远程分支的回退
    Linux模拟控制网络时延
    ubuntu 软件
    编译cubieboard android 源码过程详解之(六):pack
    编译cubieboard android 源码过程详解之(五):make
    编译cubieboard android 源码过程详解之(四):extract-bsp
  • 原文地址:https://www.cnblogs.com/felordcn/p/13773358.html
Copyright © 2011-2022 走看看