利用ImportBeanDefinitionRegistrar和BeanPostProcessor实现Spring对自定义注解bean的管理

12 篇文章 1 订阅

最近有需求是将某些类用代理来调用,屏蔽一些通信上的细节。直接写的话需要手动创建代理对象来用,比较麻烦,转成Spring来进行管理。利用自定义注解来规定需要bean容器管理的类,之后在BeanPostProcessor中进行增强处理即可。把这部分抽离出来写了个demo记录下。

自定义注解

ServiceRegisterClass

作用在类上的注解,Spring扫描到该注解类会进行bean创建并管理。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ServiceRegisterClass {
}

ServiceRegisterField

作用在成员上的注解,在Bean初始化过程中进行该对象的代理对象的注入。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ServiceRegisterField {
}

ServiceScan

作用在调用主方法的类上的注解,scanPackage用来标记扫描范围。这个注解最重要的是import了ServiceScannerRegistrar.class。这个类实现了ImportBeanDefinitionRegistrar来注册我们的自定义注解给bean容器。

// 自定义扫描注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(ServiceScannerRegistrar.class)
public @interface ServiceScan {
    // 用于设置扫描的包
    String[] scanPackage();
}

Spring bean 扫描与创建规则改写

ServiceScannerRegistrar 扫描规则注册类

这个类是最关键的之一,通过实现ImportBeanDefinitionRegistrar中的registerBeanDefinitions方法,就可以进行增加我们的自定义注解到bean容器扫描的范围中。

public class ServiceScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {

    private ResourceLoader resourceLoader;

    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

首先对获取注解的元信息,找到注解中属性scanPackage的值,未找到就默认扫描当前启动类所在包。

        // 获取自定义扫描注解的信息
        AnnotationAttributes annotationAttributes = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(ServiceScan.class.getName()));
        String[] scanPackages = new String[0];
        if (annotationAttributes != null) {
            scanPackages = annotationAttributes.getStringArray("scanPackage");
        }
        if (scanPackages.length == 0) {
            // 未设置就默认为自定义注解作用类所在包
            scanPackages = new String[]{((StandardAnnotationMetadata) importingClassMetadata).getIntrospectedClass().getPackage().getName()};
        }

除了Spring自带的bean注解(@Component等),增加自定义注解@ServiceRegisterClass作为bean注解让bean容器进行解析。ClassPathBeanDefinitionScanner构造方法的第二个参数为是否保留自带的bean注解。

        // 增加自定义注解的扫描,同时保留spring预设bean注解的扫描(@Component等)
        ClassPathBeanDefinitionScanner serviceScanner = new ClassPathBeanDefinitionScanner(registry,true);
        serviceScanner.addIncludeFilter(new AnnotationTypeFilter(ServiceRegisterClass.class));
        if (resourceLoader != null) {
            serviceScanner.setResourceLoader(resourceLoader);
        }

最后扫描即可。

        // 开始扫描
        int beanCount = serviceScanner.scan(scanPackages);
        System.out.println("bean count:" + beanCount);
    }

ServiceBeanPostProcessor bean初始化增强类

实现BeanPostProcessor,在处理bean的时候寻找@ServiceRegisterField注解的对象并注入代理对象。

// BeanPostProcessor对bean创建过程处理
@Component
public class ServiceBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        // 获取bean的field中的注解信息,注入有ServiceRegisterField注解的成员
        System.out.println("bean:" + beanName + " created");
        Field[] fields = bean.getClass().getDeclaredFields();
        for (Field field:fields) {
             ServiceRegisterField serviceRegisterField = field.getAnnotation(ServiceRegisterField.class);
             // 存在则注入
            if (serviceRegisterField != null) {
                System.out.println("find service field");
                Enhancer enhancer = new Enhancer();
                enhancer.setClassLoader(field.getType().getClassLoader());
                enhancer.setSuperclass(field.getType());
                enhancer.setCallback(new CGLibInterceptor());
                Object proxy = enhancer.create();
                field.setAccessible(true);
                try {
                    field.set(bean, proxy);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
        return bean;
    }
}

要注意的是BeanPostProcessor需要被Spring bean管理才能生效,所以加了个@Component

CGLibInterceptor CGLib代理增强类

因为没有接口所以用了CGLib来演示代理。

public class CGLibInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("CGLib proxying, do something in proxy");
        return methodProxy.invokeSuper(o, objects);
    }
}

演示

Hello.class

@ServiceRegisterClass
public class Hello {
    public String hello(){
        return "hello world test class";
    }
}

Hello2.class

public class Hello2 {
    public String hello(){
        return "hello world test field";
    }
}

MainController

@Component
public class MainController {

    @ServiceRegisterField
    Hello2 hello2;

    @Autowired
    Hello hello;

    public void printHello(){
        System.out.println("output1:" + hello.hello());
        System.out.println("output2:" + hello2.hello());
    }
}

启动类 StartApplication.class

// 调用类
// 自定义扫描注解
@ServiceScan(scanPackage = {"com.huiluczp"})
public class StartApplication {
    public static void main(String[] args) {
        // 基于注解的容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(StartApplication.class);
        MainController mainController = (MainController)applicationContext.getBean(MainController.class);
        mainController.printHello();
    }
}

输出结果:
在这里插入图片描述
总结:
把注入过程交给spring管理确实方便很多,通过代理屏蔽细节在调用对象的时候只需要打上对应注解就行,拓展很方便挺好的。简单demo记录,觉得有用就看看吧。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值