山寨一个Spring的@Component注解

Posted Gerald Newton

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了山寨一个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,我们继承它进行稍加改造:

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.
 */
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提供的功能性接口。

在此我向大家推荐一个架构学习交流圈。交流学习微信:539413949(里面有大量的面试题及答案)里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化、分布式架构等这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

Spring @Component 注解的使用

使用说明

这个注解用于声明当前的类是一个组件类,Spring 会通过类路径扫描来自动侦测和自动装配这些组件,创建一个个 bean 后,注册到 Spring 容器中。

带 @Component 注解的类和自动创建的 bean 之间存在隐式的一对一映射关系。由于只需要声明一个注解,其他过程都是自动化的,所以对 bean 的创建过程可控程度较低。

该注解相当于:

<bean id="useService" class="com.test.service.UserServiceImpl"/>

普通组件

@Component
public class UserServiceImpl implements IUserService {
	private String name;
	// getter&&setter...
}
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
IUserService service = (IUserService)context.getBean(UserServiceImpl.class);

命名组件

@Component(value = "userService")
public class UserServiceImpl implements IUserService {
	private String name;
	// getter&&setter...
}
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
IUserService service = (IUserService)context.getBean("userService");

以上是关于山寨一个Spring的@Component注解的主要内容,如果未能解决你的问题,请参考以下文章

@Component注解的用法

Spring @Component 注解的使用

spring的注解@Component@Bean,@Autowire一遍搞定

spring注解注入:<context:component-scan>详解(转)

注解service和component的区别

java spring component与autowire区别