Spring中自定义注解的解析过程-学习
在学习spring源码的过程中,最好奇的一件事就是Sprint的注解是怎么被读取到的,又是怎么进行解析的。然后又是怎么将注解的内容注入到spring容器中的。带着这个好奇心,写了一个小的Spring的自定义注解的demo,记录一下,怕自己忘记!
- 在spring中,对于注解的解析,有一个非常重要的类就是ConfigurationClassPostProcessor,它是BeanDefinitionRegistryPostProcessor的其中一个实现类。关于后置处理器的暂时不记录,只要知道它是一个后置处理器,暂时就可以了。他是在容器启动时会被调用的一个后置处理器,调用的方法是postProcessBeanDefinitionRegistry,在这个方法中调用了processConfigBeanDefinitions方法。
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
int registryId = System.identityHashCode(registry);
if (this.registriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
}
if (this.factoriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanFactory already called on this post-processor against " + registry);
}
this.registriesPostProcessed.add(registryId);
processConfigBeanDefinitions(registry);
}
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse");
parser.parse(candidates);
}
- 知道注解的解析,然后开始自己定义一个自定义注解TanxiiValue
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TanxiiValue {
String value() default "";
}
- 不采用上面的解析,我想自己写一个后置处理器来处理这个注解相关的逻辑,因此需要准备一个ApplicationContext、BeanFactoryPostProcessor以及一个BeanPostProcessor
public class TanxiiApplicationContext extends AnnotationConfigApplicationContext {
public TanxiiApplicationContext(Class<MyConfig> myConfigClass) {
super(myConfigClass);
}
@Override
protected void initPropertySources() {
this.getEnvironment().setRequiredProperties("abc");
}
@Override
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
System.out.println("Application的postProcessBeanFactory");
}
@Override
public void addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor) {
Assert.notNull(postProcessor,"postProcessor is must not null");
super.addBeanFactoryPostProcessor(postProcessor);
}
}
@Component
public class TanxiiBeanFactoryPostProcessor implements BeanFactoryPostProcessor, ApplicationContextAware {
private BeanDefinitionRegistry registry;
private ApplicationContext applicationContext;
@Override
public void postProcessBeanFactory(@NotNull ConfigurableListableBeanFactory beanFactory) throws BeansException {
if(beanFactory instanceof BeanDefinitionRegistry){
System.out.println("======BeanDefinitionRegistry");
this.registry = (BeanDefinitionRegistry) beanFactory;
}
String packageName = this.applicationContext.getClass().getPackageName();
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
packageName.replaceAll("\\.","/") + '/' + "**/*.class";
CachingMetadataReaderFactory cachingMetadataReaderFactory = new CachingMetadataReaderFactory();
try {
// 扫描符合packageSearchPath 路径下的所有类
Resource[] resources = this.applicationContext.getResources(packageSearchPath);
// 遍历所有类
for (Resource resource : resources) {
// 将类变成一个reader 给后面构成beandefinition使用
MetadataReader metadataReader = cachingMetadataReaderFactory.getMetadataReader(resource);
// 构造beandefinition
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
// BeanNameGengrator bean的名称生成器
AnnotationBeanNameGenerator annotationBeanNameGenerator = new AnnotationBeanNameGenerator();
// 设置资源
sbd.setSource(resource);
// 设置单例
sbd.setScope(BeanDefinition.SCOPE_SINGLETON);
// 获取注解
AnnotationMetadata metadata = sbd.getMetadata();
// 获取指定注解的方法 主要的地方,在这里获取到了被TanxiiValue修饰的类
// 因为被TanxiiValue修饰的类没有@Component注解,因此在spring启动的时候不会将该类扫描到SPring容器中,需要手动进行注册。这里就是我的目的,获取被注解的类,将其注入到spring容器中
if(metadata.isAnnotated(TanxiiValue.class.getName())){
// 构造一个BeanDefinitionHolder 然后将其注册到spring容器中
BeanDefinitionHolder holder = new BeanDefinitionHolder(sbd,annotationBeanNameGenerator.generateBeanName(sbd,this.registry));
BeanDefinitionReaderUtils.registerBeanDefinition(holder,this.registry);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
// 获取spring容器中的applicationContext对象
@Override
public void setApplicationContext(@NotNull ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
// beanPostProcessor 在bean实例化并已经进行属性填充之后,在执行初始化方法的前后进行调用处理的地方,因此,我们自定义的注解在bean初始化前后都可以进行相应的个性化处理 这里只是写了一个测试的方法,勉强够看
@Component
public class TanxiiBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
Annotation[] annotations = bean.getClass().getAnnotations();
for (Annotation annotation : annotations) {
if(annotation instanceof TanxiiValue){
TanxiiValue tanxiiValue = bean.getClass().getAnnotation(TanxiiValue.class);
System.out.println("获取到注解的值" + tanxiiValue.value());
}
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("BeanPostProcessor======after " + beanName);
Annotation[] annotations = bean.getClass().getAnnotations();
for (Annotation annotation : annotations) {
if(annotation instanceof TanxiiValue){
TanxiiValue tanxiiValue = bean.getClass().getAnnotation(TanxiiValue.class);
System.out.println("获取到注解的值" + tanxiiValue.value());
}
}
return bean;
}
}
- 测试,需要注意的是,在TanxiiBeanFactoryPostProcessor类中,我是获取的applicationcontext的包路径,这个地方就有一个限制,测试的类必须在applicationcontext的同级或者下级包中
@TanxiiValue(value = "正好做一个注解值获取的测试")
public class TestAnnotation {
@Autowired
public UserService userService;
public void printf(){
System.out.println(userService.orderService.getHello());
}
}
- 启动spring容器
public class MyFirstSpringApplication {
public static void main(String[] args) {
ApplicationContext context = new TanxiiApplicationContext(MyConfig.class);
TestAnnotation testAnnotation = (TestAnnotation) context.getBean("testAnnotation");
testAnnotation.printf();
System.out.println(context.getBean("testAnnotation"));
}
}
可以发现被TanxiiValue注解修饰的类也可以正常从Spring容器中获取。当前注解可以被正常解析。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)