序言
过前几篇文章介绍了了@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类中的所有注解处理解析都已经完成。