START
两个自定义注解:
@Documented
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Import(LogRegistry.class)
public @interface EnableLog {
String basePackage() default "";
}
该注解的作用是扫描指定的basePackage目录中使用了@Log注解的类,并将这些类注册到容器中;
如果basePackage为空,那么扫描使用了@EnableLog的目录;
@Documented
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
}
使用了@Log注解的类都会被注册到容器中。
下面是最关键的LogRegistry类,这个类会将所有使用@Log直接的类实际地注册到容器中:
public class LogRegistry implements ImportBeanDefinitionRegistrar {
private static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
AnnotationAttributes attributes = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(EnableLog.class.getName()));
final String basePackage = Optional.ofNullable(StringUtils.isEmpty(attributes.getString("basePackage")) ? null : attributes.getString("basePackage")).orElseGet(() -> {
try {
final String className = importingClassMetadata.getClassName();
final Class<?> aClass = Class.forName(className);
return aClass.getPackage().getName();
} catch (Exception e) {
return "";
}
});
registry(registry, Log.class.getName(), basePackage);
}
public void registry(BeanDefinitionRegistry registry, String className, String basePackage) {
ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver();
Set<BeanDefinition> candidates = findCandidateComponents(basePackage, className);
for (BeanDefinition beanDefinition : candidates) {
ScopeMetadata scopeMetadata = scopeMetadataResolver.resolveScopeMetadata(beanDefinition);
beanDefinition.setScope(scopeMetadata.getScopeName());
String beanName = buildDefaultBeanName(beanDefinition);
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(beanDefinition, beanName);
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);
}
}
private String buildDefaultBeanName(BeanDefinition definition) {
String beanClassName = definition.getBeanClassName();
String shortClassName = ClassUtils.getShortName(beanClassName);
return Introspector.decapitalize(shortClassName);
}
private Set<BeanDefinition> findCandidateComponents(String basePackage, String className) {
Set<BeanDefinition> candidates = new LinkedHashSet<>();
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
CachingMetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory();
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
basePackage.replace(".", "/") + '/' + DEFAULT_RESOURCE_PATTERN;
Resource[] resources = new Resource[0];
try {
resources = resolver.getResources(packageSearchPath);
for (Resource resource : resources) {
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
if (metadataReader.getAnnotationMetadata().isAnnotated(className)) {
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setSource(resource);
candidates.add(sbd);
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
return candidates;
}
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
ImportBeanDefinitionRegistrar.super.registerBeanDefinitions(importingClassMetadata, registry);
}
}
很重要的一点是这个类实现了ImportBeanDefinitionRegistrar接口。
手动注册bean的大致流程:
- 获取basePackage
- 通过basePackage,扫描指定的注解(@Log),获取最终的BeanDefinition集合
- 对所有的BeanDefinition进行注册
关于为什么没有使用BeanDefinitionRegistryPostProcessor?以下是BeanDefinitionRegistryPostProcessor 和 ImportBeanDefinitionRegistrar 的区别(来自ChatGPT):
- BeanDefinitionRegistryPostProcessor 和 ImportBeanDefinitionRegistrar 都是用来动态注册 BeanDefinition 的接口,但是它们的注册方式和作用有所区别。
- BeanDefinitionRegistryPostProcessor 接口允许 Spring 用户在应用程序上下文装配之前修改 BeanDefinition 或添加额外的 BeanDefinition。这个接口的 postProcessBeanDefinitionRegistry() 方法会在 BeanDefinition 装载之前被调用,允许用户向应用程序上下文中添加、修改或删除 BeanDefinition。
- ImportBeanDefinitionRegistrar 接口的主要作用是在当前应用程序上下文中注册额外的 BeanDefinition。它的作用不同于 BeanDefinitionRegistryPostProcessor,它是在应用程序上下文已经装载配置之后运行的,它的registerBeanDefinitions() 方法允许用户向应用程序上下文中注册额外的 BeanDefinition。
- 所以说,BeanDefinitionRegistryPostProcessor 是主要用来调整和修改已有的 BeanDefinition,而 ImportBeanDefinitionRegistrar 是用来注册新的 BeanDefinition 的。这两个接口都可以用来动态地注册 Bean,但是它们发挥作用的时间和目的不同。
看一下BeanDefinitionRegistryPostProcessor接口的定义:
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}
这个接口有一个问题,虽然能注册bean,和从容器中获取bean。
但是如果自定义注解时,它没有办法拿到自定义注解的信息,而ImportBeanDefinitionRegistrar接口registerBeanDefinitions方法的AnnotationMetadata参数可以获取到自定义注解的信息。这样才能针对使用了自定义注解的类进行注册。
END