Sping源码(七)—ConfigurationClassPostProcessor ——@Bean解析

序言

过前几篇文章介绍了了@ComponentScan、@ProperySource、@Import、@Componrnt注解的解析,省略@ImportResource注解不太常用,还剩一个@Bean注解在这篇帖子中解析。

准备工作

测试类
MyBean类添加@Component使其能够被Spring识别,并创建两个方法返回new User,需要注意的是getUser2()方法上不但有@Bean注解,还有@Conditional注解。

@Component
public class MyBean implements MyInterface{

	@Bean("zhangsan")
	public User getUser1() {
		return new User();
	}

	@Bean("lisi")
	@Conditional(LinuxCondition.class)
	public Person getPsrson() {
		return new Person();
	}
}

LinuxCondition
类在Linux系统中才会进行加载。

public class LinuxCondition  implements Condition {

	/**
	 * @param conditionContext:判断条件能使用的上下文环境
	 * @param annotatedTypeMetadata:注解所在位置的注释信息
	 * */
	@Override
	public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
		//获取ioc使用的beanFactory
		ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
		//获取类加载器
		ClassLoader classLoader = conditionContext.getClassLoader();
		//获取当前环境信息
		Environment environment = conditionContext.getEnvironment();
		//获取bean定义的注册类
		BeanDefinitionRegistry registry = conditionContext.getRegistry();
		//获得当前系统名
		String property = environment.getProperty("os.name");
		//包含linux则说明是linux系统,返回true
		if (property.contains("Linux")){
			return true;
		}
		return false;
	}
}

MyInterface
MyBean实现MyInterface的作用我们将在源码中讲解。

public interface MyInterface {

	@Bean
	default User method1(){
		return new User();
	}
}

@Bean

准备工作已经完成,@Bean注解的解析相对来讲很简单,我们直接上源码即可。

源码片段

		// Process individual @Bean methods
		//将@Bean注解的方法转换成BeanMethod对象保存在集合中
		Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
		for (MethodMetadata methodMetadata : beanMethods) {
			configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
		}

		// Process default methods on interfaces
		//处理接口中的@Bean注解方法
		processInterfaces(configClass, sourceClass);

retrieveBeanMethodMetadata
直接获取@Bean注解中属性值并转换成MethodMetadata对象放入集合中。
值得注意的是 if 判断中的代码块,通过ASM读取文件,因为JVM加载类的方法是无序的,所以如果满足条件则通过ASM进行方法的读取。

private Set<MethodMetadata> retrieveBeanMethodMetadata(SourceClass sourceClass) {
		AnnotationMetadata original = sourceClass.getMetadata();
		Set<MethodMetadata> beanMethods = original.getAnnotatedMethods(Bean.class.getName());
		if (beanMethods.size() > 1 && original instanceof StandardAnnotationMetadata) {
			// Try reading the class file via ASM for deterministic declaration order...
			// Unfortunately, the JVM's standard reflection returns methods in arbitrary
			// order, even between different runs of the same application on the same JVM.
			try {
				AnnotationMetadata asm =
						this.metadataReaderFactory.getMetadataReader(original.getClassName()).getAnnotationMetadata();
				Set<MethodMetadata> asmMethods = asm.getAnnotatedMethods(Bean.class.getName());
				if (asmMethods.size() >= beanMethods.size()) {
					Set<MethodMetadata> selectedMethods = new LinkedHashSet<>(asmMethods.size());
					for (MethodMetadata asmMethod : asmMethods) {
						for (MethodMetadata beanMethod : beanMethods) {
							if (beanMethod.getMethodName().equals(asmMethod.getMethodName())) {
								selectedMethods.add(beanMethod);
								break;
							}
						}
					}
					if (selectedMethods.size() == beanMethods.size()) {
						// All reflection-detected methods found in ASM method set -> proceed
						beanMethods = selectedMethods;
					}
				}
			}
			catch (IOException ex) {
				// 省略....
			}
		}
		return beanMethods;
	}

可以看到处理过后beanMethods中包含我们写的方法名、返回值等元素。遍历转换成BeanMenthod对象放入集合中。
值得注意的是,此时没有对@Bean对应的方法做任何的处理。
在这里插入图片描述

代码继续向下执行,来到了processInterfaces()方法。

processInterfaces
如果类实现了接口,并且接口中默认方法中包含@Bean注解(JDK1.8),则进行递归处理,同样获取@Bean注解中对应的value,转换成BeanMethod对象放入集合中。(只针对Interface中的默认方法)。

	/**
	 * Register default methods on interfaces implemented by the configuration class.
	 */
	private void processInterfaces(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
		for (SourceClass ifc : sourceClass.getInterfaces()) {
			Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(ifc);
			for (MethodMetadata methodMetadata : beanMethods) {
				if (!methodMetadata.isAbstract()) {
					// A default method or other concrete method on a Java 8+ interface...
					configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
				}
			}
			processInterfaces(configClass, ifc);
		}
	}

最后的最后,如果当前传递进来进行解析的BeanDefinition中包含父类,则弗雷也进行解析。

		// 解析父类,如果被解析的配置类继承了某个类,那么配置类的父类也会被进行解析
		if (sourceClass.getMetadata().hasSuperClass()) {
			String superclass = sourceClass.getMetadata().getSuperClassName();
			if (superclass != null && !superclass.startsWith("java") &&
					!this.knownSuperclasses.containsKey(superclass)) {
				this.knownSuperclasses.put(superclass, configClass);
				// Superclass found, return its annotation metadata and recurse
				return sourceClass.getSuperClass();
			}
		}

至此,ConfigurationClassPostProcess类中的所有注解处理解析都已经完成。

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值