springboot自定义注解,项目启动时扫描注解类并注入容器
作者:互联网
以下是核心流程的实现示例,如果需要更完整的实现,可参考:
注意:需要切换到simple-rpc-like-feign分支
- https://gitee.com/mr_wenpan/basis-simple-rpc/blob/master/simple-rpc-starter/src/main/java/org/simple/rpc/starter/registrar/SimpleRpcClientsRegistrar.java
- https://gitee.com/mr_wenpan/basis-simple-rpc/blob/master/simple-rpc-starter/src/main/java/org/simple/rpc/starter/registrar/ExampleRegistrar.java
一、需求说明
-
有两个自定义注解
@EnableSimpleRpcClients
和@SimpleRpcClient
-
@EnableSimpleRpcClients
注解标注在启动类上,并且可以指定要扫描的包(basePackages)@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented public @interface EnableSimpleRpcClients { String[] value() default {}; String[] basePackages() default {}; }
-
@SimpleRpcClient
注解标注在需要注入到容器的类或接口上@Documented @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface SimpleRpcClient { @AliasFor("name") String value() default ""; @AliasFor("value") String name() default ""; }
-
-
要求项目启动时自动扫描
@EnableSimpleRpcClients
注解的basePackages属性指定的包路径下的所有标注了@SimpleRpcClient
注解的类或接口,并将这些类或接口都注入到容器中
二、实现过程分析
主要是借助spring留给我们的扩展点ImportBeanDefinitionRegistrar
以及一些工具类BeanDefinitionReaderUtils
- 利用
ImportBeanDefinitionRegistrar
我们就可以在容器启动前期,扫描指定basePackages
下所有标注了@SimpleRpcClient
注解的类或接口信息 - 然后借助spring提供的工具类
BeanDefinitionReaderUtils
,将扫描到的类或接口按自定义逻辑注入容器即可。(比如我这里是扫描到basePackages包下,所有标注了@SimpleRpcClient
注解的接口,然后借助factoryBean为这些接口创建【动态代理类】,然后将动态代理类注入容器中)
三、代码实现
①、先定义两个自定义注解
EnableSimpleRpcClients
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface EnableSimpleRpcClients {
String[] value() default {};
String[] basePackages() default {};
}
SimpleRpcClient
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface SimpleRpcClient {
@AliasFor("name")
String value() default "";
@AliasFor("value")
String name() default "";
}
②、自定义ImportBeanDefinitionRegistrar
@Component
public class ExampleRegistrar implements ImportBeanDefinitionRegistrar,
ResourceLoaderAware, EnvironmentAware {
/**
* 资源加载器
*/
private ResourceLoader resourceLoader;
/**
* 环境
*/
private Environment environment;
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
// 创建scanner
ClassPathScanningCandidateComponentProvider scanner = getScanner();
scanner.setResourceLoader(resourceLoader);
// 设置扫描器scanner扫描的过滤条件
AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(SimpleRpcClient.class);
scanner.addIncludeFilter(annotationTypeFilter);
// 获取指定要扫描的basePackages
Set<String> basePackages = getBasePackages(metadata);
// 遍历每一个basePackages
for (String basePackage : basePackages) {
// 通过scanner获取basePackage下的候选类(有标@SimpleRpcClient注解的类)
Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(basePackage);
// 遍历每一个候选类,如果符合条件就把他们注册到容器
for (BeanDefinition candidateComponent : candidateComponents) {
if (candidateComponent instanceof AnnotatedBeanDefinition) {
// verify annotated class is an interface
AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
Assert.isTrue(annotationMetadata.isInterface(), "@SimpleRpcClient can only be specified on an interface");
// 获取@SimpleRpcClient注解的属性
Map<String, Object> attributes = annotationMetadata.getAnnotationAttributes(SimpleRpcClient.class.getCanonicalName());
// 注册到容器
registerSimpleRpcClient(registry, annotationMetadata, attributes);
}
}
}
}
/**
* 利用factoryBean创建代理对象,并注册到容器
*/
private static void registerSimpleRpcClient(BeanDefinitionRegistry registry,
AnnotationMetadata annotationMetadata,
Map<String, Object> attributes) {
// 类名(接口全限定名)
String className = annotationMetadata.getClassName();
// 创建SimpleRpcClientFactoryBean的BeanDefinition
BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(SimpleRpcClientFactoryBean.class);
// 解析出@SimpleRpcClient注解的name
String name = getName(attributes);
if (!StringUtils.hasText(name)) {
throw new ProviderNameNullException(String.format("class [%s] , @SimpleRpcClient name or value can not be null, please check.", className));
}
// 给factoryBean添加属性值
definition.addPropertyValue("name", name);
definition.addPropertyValue("type", className);
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
String alias = name + "SimpleRpcClient";
AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
// 注册bean定义信息到容器
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, new String[]{alias});
// 使用BeanDefinitionReaderUtils工具类将BeanDefinition注册到容器
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}
/**
* 创建扫描器
*/
protected ClassPathScanningCandidateComponentProvider getScanner() {
return new ClassPathScanningCandidateComponentProvider(false, environment) {
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
boolean isCandidate = false;
if (beanDefinition.getMetadata().isIndependent()) {
if (!beanDefinition.getMetadata().isAnnotation()) {
isCandidate = true;
}
}
return isCandidate;
}
};
}
/**
* 获取base packages
*/
protected static Set<String> getBasePackages(AnnotationMetadata importingClassMetadata) {
// 获取到@EnableSimpleRpcClients注解所有属性
Map<String, Object> attributes = importingClassMetadata.getAnnotationAttributes(EnableSimpleRpcClients.class.getCanonicalName());
Set<String> basePackages = new HashSet<>();
assert attributes != null;
// value 属性是否有配置值,如果有则添加
for (String pkg : (String[]) attributes.get("value")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
// basePackages 属性是否有配置值,如果有则添加
for (String pkg : (String[]) attributes.get("basePackages")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
// 如果上面两步都没有获取到basePackages,那么这里就默认使用当前项目启动类所在的包为basePackages
if (basePackages.isEmpty()) {
basePackages.add(ClassUtils.getPackageName(importingClassMetadata.getClassName()));
}
return basePackages;
}
/**
* 获取name
*/
protected static String getName(Map<String, Object> attributes) {
String name = (String) attributes.get("name");
if (!StringUtils.hasText(name)) {
name = (String) attributes.get("value");
}
return name;
}
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
}
③、具体使用
// 启动类上标注@EnableSimpleRpcClients注解,如果不指定value和basePackages那么默认扫描classpath下
@EnableSimpleRpcClients
@SpringBootApplication
public class SimpleRpcConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(SimpleRpcConsumerApplication.class, args);
}
}
标签:basePackages,name,自定义,SimpleRpcClient,String,attributes,注解,springboot 来源: https://blog.csdn.net/Hellowenpan/article/details/122683971