摘要:本文主要介绍基于ImportBeanDefinitionRegistrar和ClassPathBeanDefinitionScanner实现自定义注解扫描注入到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