基于SpringBoot实现类似@MapperScan自定义注解扫描接口管理对象

125 阅读2分钟

摘要:本文主要介绍基于ImportBeanDefinitionRegistrarClassPathBeanDefinitionScanner实现自定义注解扫描注入到Spring IOC中管理。

注解定义

Rbac注解

该注解用来标识该类是否需要被扫描到,后续scanner会用到

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

    String value() default "";

}

RbacScan注解

该注解用来标识扫描的路径和@MapperScan类似

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Import(RbacScannerRegistrar.class)
public @interface RbacScan {

    String[] value();
}

扫描和配置相关代码

RbacInvocationHandler

该类是基于接口的代理类,也可以实现MethodInterceptor,用cglib的增强类

public class RbacInvocationHandler implements InvocationHandler {

    private static final Logger log = LoggerFactory.getLogger(RbacInvocationHandler.class);

    public static Object build(Class<?> type){
        return Proxy.newProxyInstance(type.getClassLoader(),new Class[]{type},new RbacInvocationHandler());
    }

    @Override
    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
        // 自己实现对应的代理逻辑,发起http请求或者校验,看个人需求,这里全部返回null了
        Class<?> returnType = method.getReturnType();
        log.info("进入了代理方法"+method.getName());
        return null;
    }
}

RbacFactoryBean工厂实现类

该类用于spring在用工厂模式实现具体类的时候调用

public class RbacFactoryBean<T> implements FactoryBean<T> {

    public final Class<T> type;

    public RbacFactoryBean(Class<T> type) {
        this.type = type;
    }

    @Override
    public T getObject() throws Exception {
        return (T) RbacInvocationHandler.build(type);
    }

    @Override
    public Class<?> getObjectType() {
        return type;
    }
}

RbacBeanDefinitionScanner扫描路径类

该类主要作用是过滤扫描路径下的类,并且构造出来代理对象

public class RbacBeanDefinitionScanner extends ClassPathBeanDefinitionScanner {

    public RbacBeanDefinitionScanner(BeanDefinitionRegistry registry) {
        super(registry, false);
        this.registerFilters();
    }

    public void registerFilters(){
        // 设置社火获取所有的接口还是只有注解的
        //super.addIncludeFilter((metadataReader, metadataReaderFactory) -> true);
        super.addIncludeFilter(new AnnotationTypeFilter(Rbac.class));
    }

    @Override
    protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
        return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
    }

    @Override
    protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
        // 进行扫描,获取BeanDefinitionHolder
        Set<BeanDefinitionHolder> beanDefinitionHolders = super.doScan(basePackages);
        // 循环替换beanDefinition中的beanClass为FactoryBean,不进行替换的话,beanClass是接口,Spring无法通过接口得到Bean对象
        for (BeanDefinitionHolder definitionHolder : beanDefinitionHolders) {
            // 获取beanDefinition
            AbstractBeanDefinition beanDefinition = (AbstractBeanDefinition) definitionHolder.getBeanDefinition();
            // 获取扫描出的接口的beanClassName
            String beanClassName = beanDefinition.getBeanClassName();
            // 设置BeanClass为DemoFactoryBean
            beanDefinition.setBeanClass(RbacFactoryBean.class);
            // 添加构造方法的参数,DemoFactoryBean的构造方法需要Class对象,这里设置的是string的参数,spring会使用Class.forName加载
            // 成DemoFactoryBean中构造方法需要的类参数
            beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName);
        }
        return beanDefinitionHolders;
    }
}

RbacScannerRegistrar注册类

该类和RbacScan注解一起使用,获取扫描的路径并启动扫描器注册bean对象

public class RbacScannerRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        AnnotationAttributes mapperScanAttrs = AnnotationAttributes
                .fromMap(importingClassMetadata.getAnnotationAttributes(RbacScan.class.getName()));
        if (mapperScanAttrs != null) {
            RbacBeanDefinitionScanner scanner = new RbacBeanDefinitionScanner(registry);
            scanner.scan(mapperScanAttrs.getStringArray("value"));
        }
    }

}

Application类上添加注解

@RbacScan(value = "com.github.huzhihui.registrar.proxy.inter")

测试

UserInter接口定义

注意该接口必须要和@RbacScan扫描的包路径一直,不然获取不到

@Rbac
public interface UserInter {

    String getAll();

    Object getOne();

}

RbacController控制器

@RestController
@RequestMapping(value = "rbac")
public class RbacController {

    @Autowired
    private UserInter userInter;

    @GetMapping(value = "get-all-user")
    public Object getAllUser(){
        userInter.getAll();
        return "SUCCESS";
    }

}
  • 访问

curl http://localhost:8080/rbac/get-all-user

  • 控制台输出
2022-10-27 14:33:06.995  INFO 19028 --- [nio-8080-exec-1] c.g.h.r.proxy.RbacInvocationHandler      : 进入了代理方法getAll