Spring源码深度解析(郝佳)-Spring 常用注解使用及源码解析

  我们在看Spring Boot源码时,经常会看到一些配置类中使用了注解,本身配置类的逻辑就比较复杂了,再加上一些注解在里面,让我们阅读源码更加难解释了,因此,这篇博客主要对配置类上的一些注解的使用以及实现原理做分析,从而让阅读源码更加简单一点。
  Spring boot 集成mybatis时,就有一个非常重要的配置类MybatisAutoConfiguration,这个类上配置了一堆注解,如下

@org.springframework.context.annotation.Configuration
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
@ConditionalOnBean(DataSource.class)
@EnableConfigurationProperties(MybatisProperties.class)
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class MybatisAutoConfiguration {
...
}
ConditionalOnClass注解

  话不多说,先来看ConditionalOnClass注解的使用。先来看

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnClassCondition.class)
public @interface ConditionalOnClass {

	Class<?>[] value() default {};

	String[] name() default {};
}

  从ConditionalOnClass注解的属性可以看出,我们可以配置一个Class数组,也可以配置一个字符串数组,显然配置Class数组,肯定在编译环境中存在该类。如果配置字符串数组的话,字符串构成的类名,在编译环境中不一定存在,基于以上的可能性,我们来测试一把。

  1. 创建普通类
public class ConditionalOnClassAnnocation {

}
  1. 创建ConditionalOnClassUser测试类
@ConditionalOnClass(ConditionalOnClassAnnocation.class)
@Service("conditionalOnClassUser")
public class ConditionalOnClassUser {
    public void a() {
        System.out.println("a");
    }
}
  1. 创建测试方法
@RequestMapping("conditionalOnClassTest")
public String conditionalOnClassTest(){
   Object object =  SpringContextUtils.getBean("conditionalOnClassUser");
    System.out.println(object);
    return "Sucess";
}

测试结果
在这里插入图片描述

  从上面测试结果得知,ConditionalOnClassAnnocation类并没有在Spring容器中,只存在编译环境,因此一般我们开发业务项目时,如果ConditionalOnClassAnnocation不存在,编译都不会过,更不用谈ConditionalOnClassUser存储到容器中了。我们修改一下测试条件。

@ConditionalOnClass(name = "com.example.springbootstudy.service.ConditionalOnClassAnnocation")
@Service("conditionalOnClassUser")
public class ConditionalOnClassUser {

    public void a() {
        System.out.println("a");
    }
}

接着测试
在这里插入图片描述
  依然没有问题,容器中仍然注入了conditionalOnClassUser对象,我们再来测试,将ConditionalOnClassAnnocation改成XXX

@ConditionalOnClass(name = "com.example.springbootstudy.service.XXX")
@Service("conditionalOnClassUser")
public class ConditionalOnClassUser {

    public void a() {
        System.out.println("a");
    }
}

  显然,我们代码中不存在XXX类,再次测试。
在这里插入图片描述

  当context.getBean(“conditionalOnClassUser”)时,容器中并没有注入conditionalOnClassUser实例。
  既然Class和name是一个数组,如果配置多个会怎样呢?新加普通类

public class ConditionalOnClassAnnocation1 {

}

  修改测试类

@ConditionalOnClass(name = {"com.example.springbootstudy.service.ConditionalOnClassAnnocation"
        ,"com.example.springbootstudy.service.ConditionalOnClassAnnocation1"})
@Service("conditionalOnClassUser")
public class ConditionalOnClassUser {
    public void a() {
        System.out.println("a");
    }
}

再次测试
在这里插入图片描述
  从上述测试中可以看到。ConditionalOnClassAnnocation和ConditionalOnClassAnnocation1类,当前编译环境都存在,因此容器中注入了conditionalOnClassUser类,但如果我们将ConditionalOnClassAnnocation1改成当着环境中不存在的类ConditionalOnClassAnnocation2,会怎样呢?

@ConditionalOnClass(name = {"com.example.springbootstudy.service.ConditionalOnClassAnnocation"
        ,"com.example.springbootstudy.service.ConditionalOnClassAnnocation2"})
@Service("conditionalOnClassUser")
public class ConditionalOnClassUser {
    public void a() {
        System.out.println("a");
    }
}

测试结果
在这里插入图片描述

  从上面的测试中,我们得出一个结论ConditionalOnClass注解内配置的类,必需在编译环境中存在,此时被注解的类【ConditionalOnClassUser】才会注入到容器中,编译环境中任意一个类不存在,【ConditionalOnClassUser】都不会被注入到容器中,和ConditionalOnClass注解中配置的类是否会注入到容器无关
  我发现ConditionalOnMissingClass的源码和ConditionalOnClass源码是写在一块的,为了方便理解,那我们先来看ConditionalOnMissingClass的示例吧。

ConditionalOnMissingClass注解

  我们先来看一看ConditionalOnMissingClass注解的源码。

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnClassCondition.class)
public @interface ConditionalOnMissingClass {
	String[] value() default {};
}

  从上述源码中我们可以看到,和ConditionalOnClass注解的区别在于ConditionalOnMissingClass没有Class[] 属性。而从字面意思上来理解,就是和ConditionalOnClass功能刚好相反,是否真的相反,看看例子再说。

  1. 创建普通类ConditionalOnMissingClassAnnocation和ConditionalOnMissingClassAnnocation1
public class ConditionalOnMissingClassAnnocation {
}
public class ConditionalOnMissingClassAnnocation1 {
}
  1. 创建测试类ConditionalOnMissingClassUser,上面配置了ConditionalOnMissingClass注解
@Service("conditionalOnMissingClassUser")
@ConditionalOnMissingClass("com.example.springbootstudy.service.ConditionalOnMissingClassAnnocation")
public class ConditionalOnMissingClassUser {

}

  我们知道ConditionalOnMissingClassAnnocation在编译环境中定义肯定存在。

  1. 测试1
    在这里插入图片描述

  容器中并不存在conditionalOnMissingClassUser类,因此,初步得出结论,ConditionalOnMissingClassUser类和其注解ConditionalOnMissingClass中配置的类是你死我活的关系,只要编译环境中存在ConditionalOnMissingClass注解配置的类,则Spring容器中就不会注册ConditionalOnMissingClass类。真是这样吧,要多举几个例子看看。修改ConditionalOnMissingClass属性信息,配置一个不存在的类

@Service("conditionalOnMissingClassUser")
@ConditionalOnMissingClass("com.example.springbootstudy.service.ConditionalOnMissingClassAnnocation2")
public class ConditionalOnMissingClassUser {

}

  我们知道ConditionalOnMissingClassAnnocation2类肯定在编译环境中不存在。开始测试

  1. 测试2
    在这里插入图片描述

  显然当ConditionalOnMissingClass注解中配置的类在当前编译环境不存在时,则当前类会被注入到Spring容器中。

  1. 测试3 ,当ConditionalOnMissingClass注解中配置两个类,一个类存在,另一个类不存在时。
@Service("conditionalOnMissingClassUser")
@ConditionalOnMissingClass({"com.example.springbootstudy.service.ConditionalOnMissingClassAnnocation",
        "com.example.springbootstudy.service.ConditionalOnMissingClassAnnocation2"})
public class ConditionalOnMissingClassUser {
}

在这里插入图片描述
  当ConditionalOnMissingClass注解中配置一个类存在,另一个类不存在时,仍然不会注册到Spring容器中。

  1. 测试4 ,当ConditionalOnMissingClass注解中配置两个类都不存在时。
    在这里插入图片描述

  当ConditionalOnMissingClass注解中配置的两个类都不存在时,ConditionalOnMissingClassUser类会被注入到容器中。
  从测试中,我们得出一个结论,ConditionalOnMissingClass注解中配置的类,只要在编译环境中存在,则被配置注解的类就不会被注册到Spring容器中。结论刚好和ConditionalOnClass注解相反
  那么如果配置了ConditionalOnMissingClass和ConditionalOnClass注解,且一个条件满足,一个条件不满足,会是什么情况呢?
  创建测试类

@Service("conditionalOnMissingClassOnClassUser")
@ConditionalOnMissingClass({"com.example.springbootstudy.service.ConditionalOnMissingClassAnnocation4",
        "com.example.springbootstudy.service.ConditionalOnMissingClassAnnocation3"})
@ConditionalOnClass(name = "com.example.springbootstudy.service.ConditionalOnMissingClassAnnocation3")
public class ConditionalOnMissingClassOnClassUser {

}

  从示例中,我们知道ConditionalOnMissingClassAnnocation4类和ConditionalOnMissingClassAnnocation3类不存在于编译环境中,因此
@ConditionalOnMissingClass({“com.example.springbootstudy.service.ConditionalOnMissingClassAnnocation4”,
“com.example.springbootstudy.service.ConditionalOnMissingClassAnnocation3”}) 的条件为true,但是@ConditionalOnClass(name = “com.example.springbootstudy.service.ConditionalOnMissingClassAnnocation3”) 条件为false,而我们再来看看测试结果

在这里插入图片描述
  容器中并没有注册conditionalOnMissingClassOnClassUser类,因此当类配置了多个条件注解时,类是否注册到容器中的判断条件是,条件注解之间是AND关系,因此,ConditionalOnMissingClass条件注解为true时,ConditionalOnClass条件注解为false时,最终conditionalOnMissingClassOnClassUser类没有被注册到容器中。当然举一反三,如果ConditionalOnMissingClass条件注解为true,同时ConditionalOnClass条件注解也为true时,肯定conditionalOnMissingClassOnClassUser会被注入到容器中。

@Service("conditionalOnMissingClassOnClassUser")
@ConditionalOnMissingClass({"com.example.springbootstudy.service.ConditionalOnMissingClassAnnocation4",
        "com.example.springbootstudy.service.ConditionalOnMissingClassAnnocation3"})
@ConditionalOnClass(name = "com.example.springbootstudy.service.ConditionalOnMissingClassAnnocation")
public class ConditionalOnMissingClassOnClassUser {

}

测试结果:
**加粗样式**
  因为ConditionalOnMissingClassAnnocation类存在,而ConditionalOnMissingClassAnnocation3类和ConditionalOnMissingClassAnnocation4类不存在。
  ConditionalOnMissingClass和ConditionalOnClass注解的使用己经了解了,那么Spring源码中又是如何实现这个注解功能的呢?我们来看源码。

public Set<BeanDefinition> findCandidateComponents(String basePackage) {
	Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();
	try {
		String packageSearchPath = "classpath*:"+
				resolveBasePackage(basePackage) + '/' + this.resourcePattern;
		Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
		boolean traceEnabled = logger.isTraceEnabled();
		boolean debugEnabled = logger.isDebugEnabled();
		for (Resource resource : resources) {
			if (traceEnabled) {
				logger.trace("Scanning " + resource);
			}
			if (resource.isReadable()) {
				try {
					MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
					if (isCandidateComponent(metadataReader)) {
						ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
						sbd.setResource(resource);
						sbd.setSource(resource);
						if (isCandidateComponent(sbd)) {
							if (debugEnabled) {
								logger.debug("Identified candidate component class: " + resource);
							}
							candidates.add(sbd);
						}
						else {
							if (debugEnabled) {
								logger.debug("Ignored because not a concrete top-level class: " + resource);
							}
						}
					}
					else {
						if (traceEnabled) {
							logger.trace("Ignored because not matching any filter: " + resource);
						}
					}
				}
				catch (Throwable ex) {
					throw new BeanDefinitionStoreException(
							"Failed to read candidate component class: " + resource, ex);
				}
			}
			else {
				if (traceEnabled) {
					logger.trace("Ignored because not readable: " + resource);
				}
			}
		}
	}
	catch (IOException ex) {
		throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
	}
	return candidates;
}

  从上述代码中看到,只有满足条件的bean才会被加入到candidates中,因此在这个方法isCandidateComponent中应该就是判断,当前bean是否注入到容器中。我们跟进代码。

protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
	for (TypeFilter tf : this.excludeFilters) {
		if (tf.match(metadataReader, this.metadataReaderFactory)) {
			return false;
		}
	}
	for (TypeFilter tf : this.includeFilters) {
		//当前类或父类或接口的注解匹配上TypeFilter配置的注解
		if (tf.match(metadataReader, this.metadataReaderFactory)) {
			return isConditionMatch(metadataReader);
		}
	}
	return false;
}

  在上述方法中,只要返回false,bean则不会注册到容器中,那excludeFilters和includeFilters到底是什么东西呢?excludeFilters默认为空,includeFilters又是在什么时候初始化值的呢?在代码中寻寻觅觅。找到了注入方法registerDefaultFilters(),为了找到在哪里调用,我们在这个方法中打一个断点。
在这里插入图片描述
  从registerDefaultFilters方法中得知。new AnnotationTypeFilter(Component.class)肯定会被加入到includeFilters集合中的,如果当前编译环境中存在javax.annotation.ManagedBean类,则会将new AnnotationTypeFilter(ManagedBean.class)加入到includeFilters集合中,如果当前环境存在javax.inject.Named类,则会将new AnnotationTypeFilter(Named.class)加入到includeFilters中。而registerDefaultFilters方法最初是哪里调用的呢?我们跟踪到createApplicationContext()方法,而createApplicationContext()方法是Spring Boot启动时必调的方法。我们来看看createApplicationContext()方法的实现。

protected ConfigurableApplicationContext createApplicationContext() {
	Class<?> contextClass = this.applicationContextClass;
	if (contextClass == null) {
		try {
			contextClass = Class.forName(this.webEnvironment
					? "org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext" : "org.springframework.context."
			+ "annotation.AnnotationConfigApplicationContext");
		}
		catch (ClassNotFoundException ex) {
			throw new IllegalStateException(
					"Unable create a default ApplicationContext, "
							+ "please specify an ApplicationContextClass",
					ex);
		}
	}
	return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
}

  最终在实例化AnnotationConfigEmbeddedWebApplicationContext类时,会调用registerDefaultFilters方法。为什么是实例化AnnotationConfigEmbeddedWebApplicationContext呢?因为我们是Spring Boot项目用来做测试的,而webEnvironment为true的条件是

private boolean deduceWebEnvironment() {
	for (String className : "{ "javax.servlet.Servlet",
			"org.springframework.web.context.ConfigurableWebApplicationContext" }") {
		if (!ClassUtils.isPresent(className, null)) {
			return false;
		}
	}
	return true;
}

  当前编译环境中存在javax.servlet.Servlet和org.springframework.web.context.ConfigurableWebApplicationContext类,不外乎其他的情况也会调用registerDefaultFilters()方法,来填充includeFilters的值,但分析方法一样,这里就不再过多举例说明了。

  上述过程中有一个非常重要的方法match方法,比如 AnnotationTypeFilter(Component.class)注解类型过滤器。我们实例HelloServiceImpl中配置了@Service注解。而要被AnnotationTypeFilter匹配为true,那是怎样匹配的呢?先看HelloServiceImpl类是否配置了@Component注解,如果没有配置,那么看HelloServiceImpl的注解类中是否配置了Component注解,如果配置了,则匹配成功,如果没有配置,则继续递归找所有HelloServiceImpl的注解类的注解是否配置Component注解,以此类推。直到找到为止,没有找到,匹配失败。我们先来看看@Service注解的结构 。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {
	String value() default "";
}

  我们看到@Service注解中配置了@Component注解,因此配置了@Service注解的类match方法返回true。

  关于@Component的匹配逻辑和@Transactional关于事务的匹配逻辑还是有一定区分的。Transactional允许继承,但是@Component不允许,我们来看一个例子。

  1. 创建MyService注解,添加Inherited注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Service
@Inherited
public @interface MyService {
    String value() default "";
}
  1. 创建ServiceTestImpl类,实现ServiceTestI接口,在ServiceTestI接口中配置@MyService注解
@MyService
public interface ServiceTestI {
}

public class ServiceTestImpl  implements ServiceTestI{

}
  1. 测试
    在这里插入图片描述

  项目报错。ServiceTestImpl并没有注入。显然不能继承父接口的注解。即使注解中MyService配置了Inherited注解

  接下来,我们来看Transactional注解,看一个关于事务的小例子。

  1. 创建Spring配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
       
    <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>

    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
          destroy-method="close">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/pple_test?characterEncoding=utf-8"></property>
        <property name="username" value="ldd_biz"></property>
        <property name="password" value="Hello1234"></property>
        <property name="initialSize" value="1"></property>
        <property name="maxIdle" value="2"></property>
        <property name="minIdle" value="1"></property>
    </bean>

    <bean id="userService" class="com.spring_1_100.test_71_80.test73_jdbc_transaction.UserServiceImpl">
        <property name="jdbcTemplate" ref="dataSource"></property>
    </bean>

</beans>
  1. 配置Transactional注解
@Transactional(propagation = Propagation.REQUIRED)
public interface UserService {

    void save(User user) throws Exception;
}

public class UserServiceImpl implements UserService {
    private JdbcTemplate jdbcTemplate;

    public void setJdbcTemplate(DataSource dataSource) {
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }
    @Override
    public void save(User user) throws Exception {
        jdbcTemplate.update("INSERT INTO lz_user (username, password, real_name, manager_id) VALUES ( ?, ?, ?, ?) ",
                new Object[]{user.getUsername(), user.getPassword(), user.getRealName(), user.getManagerId()});

        throw  new RuntimeException("bbbbbbbbbbbbb");
    }
}

  上述代码我们需要注意的是Transactional注解配置有接口上,而不是配置在实现类UserServiceImpl上。在save()方法中,当执行插入操作以后,并且抛出异常。下面就看测试结果,看数据有没有回滚。

  1. 编写测试类
public class Test73 {
    public static void main(String[] args) throws Exception{
        ApplicationContext ac = new ClassPathXmlApplicationContext("classpath:spring_1_100/config_71_80/spring73.xml");
        UserService userService = (UserService) ac.getBean("userService");
        User user = new User();
        user.setManagerId(1l);
        user.setPassword("1239832");
        user.setUsername("zhangsan");
        user.setRealName("瞿贻晓");
        userService.save(user);
    }
}
  1. 开始测试
    在这里插入图片描述

程序抛出异常,同时数据没有插入到数据库
在这里插入图片描述
  我偿注释掉UserService上的@Transactional注解
在这里插入图片描述
在这里插入图片描述
  通过上面两个例子的对比,我相信大家对@Component注解的匹配有了一定的了解,他和Transactional注解的匹配规则不一样,@Transactional是匹配切面的规则,而@Component注解是匹配Scan的规则 。
  言规正传,我们继续分析ConditionalOnMissingClass和ConditionalOnClass注解的实现原理。

private boolean isConditionMatch(MetadataReader metadataReader) {
	if (this.conditionEvaluator == null) {
		this.conditionEvaluator = new ConditionEvaluator(getRegistry(), getEnvironment(), getResourceLoader());
	}
	return !this.conditionEvaluator.shouldSkip(metadataReader.getAnnotationMetadata());
}

  如果conditionEvaluator不存在,则直接new ConditionEvaluator()类,因此shouldSkip没有什么好说的,就是直接调用
ConditionEvaluator的shouldSkip方法。我们进入shouldSkip方法。

public boolean shouldSkip(AnnotatedTypeMetadata metadata) {
	return shouldSkip(metadata, null);
}

public boolean shouldSkip(AnnotatedTypeMetadata metadata, ConfigurationPhase phase) {
	if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) {
		return false;
	}
	if (phase == null) {
		//从调用的shouldSkip方法中来看,第一次调用phase == null ,因此第一次肯定会进入下面的代码
		//我们知道,配置了@ConditionalOnClass注解的实例的metadata肯定实现了AnnotationMetadata接口
		//并且配置了ConditionalOnClass注解的实例也肯定配置了@Component注解或注解配置了@Component注解
		if (metadata instanceof AnnotationMetadata &&
				ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) {
			//递归调用shouldSkip方法,不过此时phase不为空,如果还是空,则出现死循环
			return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION);
		}
		return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN);
	}
	
	List<Condition> conditions = new ArrayList<Condition>();
	//获取注解ConditionalOnMissingClass或ConditionalOnClass上配置的注解Conditional的value值。也就是OnClassCondition类
	for (String[] conditionClasses : getConditionClasses(metadata)) {
		for (String conditionClass : conditionClasses) {
			//实例化OnClassCondition类,并加入到conditions中
			Condition condition = getCondition(conditionClass, this.context.getClassLoader());
			conditions.add(condition);
		}
	}

	//将所有配置了@Order注解的OnClassCondition排序
	AnnotationAwareOrderComparator.sort(conditions);

	for (Condition condition : conditions) {
		ConfigurationPhase requiredPhase = null;
		if (condition instanceof ConfigurationCondition) {
			requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();
		}
		if (requiredPhase == null || requiredPhase == phase) {
			//调用OnClassCondition的matchs方法
			if (!condition.matches(this.context, metadata)) {
				return true;
			}
		}
	}
	return false;
}

  这个方法不难,在注释中己经说得很明确了,接下来,我们看OnClassCondition的matchs方法到底做了哪些事情。

public final boolean matches(ConditionContext context,
		AnnotatedTypeMetadata metadata) {
	String classOrMethodName = getClassOrMethodName(metadata);
	try {
		ConditionOutcome outcome = getMatchOutcome(context, metadata);
		logOutcome(classOrMethodName, outcome);
		recordEvaluation(context, classOrMethodName, outcome);
		return outcome.isMatch();
	}
	catch (NoClassDefFoundError ex) {
		throw new IllegalStateException(
				"Could not evaluate condition on " + classOrMethodName + " due to "
						+ ex.getMessage() + " not "
						+ "found. Make sure your own configuration does not rely on "
						+ "that class. This can also happen if you are "
						+ "@ComponentScanning a springframework package (e.g. if you "
						+ "put a @ComponentScan in the default package by mistake)",
				ex);
	}
	catch (RuntimeException ex) {
		throw new IllegalStateException(
				"Error processing condition on " + getName(metadata), ex);
	}
}

  最终方法返回值是调用outcome的isMatch方法,而isMatch方法实际上是返回了outcome的match属性,因此重点就落在了outcome对象的创建上,从上面代码中可以看到,outcome是由getMatchOutcome方法返回的,那我们跟进getMatchOutcome方法。

private enum MatchType {
	PRESENT {
		@Override
		public boolean matches(String className, ClassLoader classLoader) {
			return isPresent(className, classLoader);
		}
	},

	MISSING {
		@Override
		public boolean matches(String className, ClassLoader classLoader) {
			return !isPresent(className, classLoader);
		}

	};
	省略...

}
public ConditionOutcome getMatchOutcome(ConditionContext context,
		AnnotatedTypeMetadata metadata) {
	ClassLoader classLoader = context.getClassLoader();
	ConditionMessage matchMessage = ConditionMessage.empty();
	//获取实例上配置了ConditionalOnClass注解的所有属性值字符串
	List<String> onClasses = getCandidates(metadata, ConditionalOnClass.class);
	if (onClasses != null) {
		List<String> missing = getMatches(onClasses, MatchType.MISSING, classLoader);
		//如果编译环境中存在类不存在,则match为false
		if (!missing.isEmpty()) {
			return ConditionOutcome
					.noMatch(ConditionMessage.forCondition(ConditionalOnClass.class)
							.didNotFind("required class", "required classes")
							.items(Style.QUOTE, missing));
		}
		matchMessage = matchMessage.andCondition(ConditionalOnClass.class)
				.found("required class", "required classes").items(Style.QUOTE,
						getMatches(onClasses, MatchType.PRESENT, classLoader));
	}
	//获取ConditionalOnMissingClass注解中配置的所有值
	List<String> onMissingClasses = getCandidates(metadata,
			ConditionalOnMissingClass.class);
	if (onMissingClasses != null) {
		List<String> present = getMatches(onMissingClasses, MatchType.PRESENT,
				classLoader);
		//如果存在配置类在编译环境中存在,match为false并直接返回
		if (!present.isEmpty()) {
			return ConditionOutcome.noMatch(
					ConditionMessage.forCondition(ConditionalOnMissingClass.class)
							.found("unwanted class", "unwanted classes")
							.items(Style.QUOTE, present));
		}
		matchMessage = matchMessage.andCondition(ConditionalOnMissingClass.class)
				.didNotFind("unwanted class", "unwanted classes").items(Style.QUOTE,
						getMatches(onMissingClasses, MatchType.MISSING, classLoader));
	}
	//如果ConditionalOnMissingClass条件和ConditionalOnClass条件都为true,则直接返回ConditionOutcome的match为true
	return ConditionOutcome.match(matchMessage);
}

  在看代码的时候,需要注意一下。getMatches方法的第二个参数MatchType枚举,当ConditionalOnClass注解匹配是,是传入的是MatchType.MISSING,而ConditionalOnMissingClass注解匹配时传入的是MatchType.PRESENT枚举,这两个枚举的结果刚好相反。经过前面的分析,我相信大家对ConditionalOnClass注解及ConditionalOnMissingClass注解的使用己经有了深刻的理解了,下面继续接着ConditionalOnBean注解的分析。

ConditionalOnBean注解

  关于ConditionalOnBean注解,我们先来看看ConditionalOnBean 注解

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnBeanCondition.class)
public @interface ConditionalOnBean {
	// 容器中必需有该类型,当前被注解的类才会被注册到容器中
	Class<?>[] value() default {};
	String[] type() default {};
	//容器中的bean必需配置了该注解,当前被注解的类才会被加载到容器中
	Class<? extends Annotation>[] annotation() default {};
	//容器中必需有该name的bean,当前类才会被加载到容器中
	String[] name() default {};
	//查询bean的模式,ALL表示先从子容器中查找,如果找不到,则递归查找父类
	SearchStrategy search() default SearchStrategy.ALL;
}

  当看到了ConditionalOnBean的注释后,我相信大家对ConditionalOnBean的使用,没有太大问题了,即使没有太大问题,我们还是要一一的来测试,这样才能体现对知识理解的严谨性。下面我们来看一个最简单的例子。

  1. 创建普通bean
@Service
public class ConditionalOnBeanAnnocation {

}
  1. 创建被测试类
@ConditionalOnBean({ConditionalOnBeanAnnocation.class})
@Service
public class ConditionalOnBeanUser {
}
  1. 开始测试
@Autowired
private ConditionalOnBeanUser conditionalOnBeanUser;

@RequestMapping("conditionalOnBeanUserTest")
public String conditionalOnBeanUserTest() {
    System.out.println(conditionalOnBeanUser);
    return "Sucess";
}

测试结果
在这里插入图片描述
  从测试结果中我们可以看出,容器中己经存在了conditionalOnBeanUser的bean。我们修改一下。去掉ConditionalOnBeanAnnocation上的@Service注解。
代码启动报错。
在这里插入图片描述
  下面我们再来测试,假如配置两个类,一个存在于容器中,另一个不会注册到容器中。编写测试代码如下

@RequestMapping("conditionalOnBeanUserTest")
public String conditionalOnBeanUserTest() {
    System.out.println(conditionalOnBeanUser);
    ConditionalOnBeanAnnocation1 conditionalOnBeanAnnocation1 = SpringContextUtils.getBean(ConditionalOnBeanAnnocation1.class);
    System.out.println(conditionalOnBeanAnnocation1);
    return "Sucess";
}

测试结果:
在这里插入图片描述
  从上述测试结果,我们得出结论,对于ConditionalOnBean注解中配置的参数,只要任意一个类存在于容器中,则会被ConditionalOnBean注解修饰的类都会被实例化到容器中。

  接下来,我们来看另外一个实例,在@ConditionalOnBean(annotation = MyConditionalOnBeanTest.class)中配置注解,那配置注解是什么意思呢?也就是说,只要容器中有一个实例被MyConditionalOnBeanTest注解修饰,则被ConditionalOnBean修饰的bean才会注册到容器中。

  1. 创建MyConditionalOnBeanTest注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyConditionalOnBeanTest {

}
  1. 创建bean 并配置MyConditionalOnBeanTest注解
@MyConditionalOnBeanTest
@Service
public class MyConditionalOnBeanTestImpl {
}
  1. 测试
@ConditionalOnBean(annotation = MyConditionalOnBeanTest.class)
@Service
public class ConditionalOnBeanUser {

}

测试结果
在这里插入图片描述
当注释掉MyConditionalOnBeanTestImpl类的MyConditionalOnBeanTest注解,显然conditionalOnBeanUser没有被实例化到容器中。
在这里插入图片描述

ConditionalOnSingleCandidate注解

  接下来,我们继续看和ConditionalOnBean类似的注解ConditionalOnSingleCandidate,先来看看ConditionalOnSingleCandidate注解的内容。

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnBeanCondition.class)
public @interface ConditionalOnSingleCandidate {

	Class<?> value() default Object.class;

	String type() default "";

	SearchStrategy search() default SearchStrategy.ALL;
}

  ConditionalOnSingleCandidate和ConditionalOnBean相比,少了annotation和name属性,为什么会少这两个属性呢?目前我也不知道,到后面分析源码时,再来分析。
在这里插入图片描述
  毋庸置疑,ConditionalOnSingleCandidate肯定是要求容器中有某个bean,被修饰的bean才会注册到容器中,ConditionalOnSingleCandidate的属性不存在的bean,这里就不测试了,被修饰的bean肯定不会被注册到容器中。如

@Service
@ConditionalOnSingleCandidate(ConditionalOnSingleCandidateUserAnnocation0.class)
public class ConditionalOnSingleCandidateUser {

}

  如果ConditionalOnSingleCandidateUserAnnocation0在容器中不存在,则ConditionalOnSingleCandidateUser肯定不会注册到容器中。
  从ConditionalOnSingleCandidate的类名称,大致可以猜测,容器中必需有一个单例Bean,被修饰的bean才会注册到容器中,真的是如此吗?。根据猜测,我们来测试一下。

  1. 创建一个普通bean
@Service
public class ConditionalOnSingleCandidateUserAnnocation0 {

}

  1. 创建测试bean
@Service
@ConditionalOnSingleCandidate(ConditionalOnSingleCandidateUserAnnocation0.class)
public class ConditionalOnSingleCandidateUser {
}
  1. 测试结果
    在这里插入图片描述

  2. 我们将ConditionalOnSingleCandidateUserAnnocation0变成多例
    在这里插入图片描述
      显然,我们之前的猜测有问题,Single不是单例的意思,那么不是单例,会不会是单个呢?那我们来测试一下。

  3. 创建接口IConditionalOnSingleCandidateUserAnnocation

public interface IConditionalOnSingleCandidateUserAnnocation {

}
  1. 创建测试类ConditionalOnSingleCandidateUserAnnocation实现IConditionalOnSingleCandidateUserAnnocation接口
@Service("conditionalOnSingleCandidateUserAnnocation")
public class ConditionalOnSingleCandidateUserAnnocation implements IConditionalOnSingleCandidateUserAnnocation {
}
  1. 创建测试类ConditionalOnSingleCandidateUserAnnocation1也实现IConditionalOnSingleCandidateUserAnnocation接口。
@Service("conditionalOnSingleCandidateUserAnnocation1")
public class ConditionalOnSingleCandidateUserAnnocation1 implements IConditionalOnSingleCandidateUserAnnocation {

}
  1. 修改测试类ConditionalOnSingleCandidateUser的注解ConditionalOnSingleCandidate属性。
@Service
@ConditionalOnSingleCandidate(IConditionalOnSingleCandidateUserAnnocation.class)
public class ConditionalOnSingleCandidateUser {

}
  1. 开始测试
    在这里插入图片描述
      从测试结果中可以看出,ConditionalOnSingleCandidateUser注入失败。不过我们以前学过,假如配置@Primary注解,可以确定容器优先依赖注入哪个类,什么意思呢?假如ConditionalOnSingleCandidateUserAnnocation类上配置了注解@Primary,而ConditionalOnSingleCandidateUserAnnocation1类上没有配置@Primary注解,在TestController中,使用
      @Autowired
      private IConditionalOnSingleCandidateUserAnnocation iConditionalOnSingleCandidateUserAnnocation;

  则TestController的iConditionalOnSingleCandidateUserAnnocation属性注入的是ConditionalOnSingleCandidateUserAnnocation的bean。基于这种情况,我们来测试一下。我们在ConditionalOnSingleCandidateUserAnnocation类上加上@Primary注解

@Service("conditionalOnSingleCandidateUserAnnocation")
@Primary
public class ConditionalOnSingleCandidateUserAnnocation implements IConditionalOnSingleCandidateUserAnnocation {

}

  显然ConditionalOnSingleCandidateUser注入成功。
在这里插入图片描述
  如果ConditionalOnSingleCandidateUserAnnocation1类上也配置@Primary注解。

@Service("conditionalOnSingleCandidateUserAnnocation1")
@Primary
public class ConditionalOnSingleCandidateUserAnnocation1 implements IConditionalOnSingleCandidateUserAnnocation {

}

  测试结果
在这里插入图片描述
  容器注册ConditionalOnSingleCandidateUser的bean失败。通过上面的例子,我们得出一个结论,在Spring中

  只要@ConditionalOnSingleCandidate(IConditionalOnSingleCandidateUserAnnocation.class),ConditionalOnSingleCandidate的属性IConditionalOnSingleCandidateUserAnnocation在属性依赖注入时,
  @Autowired
  private IConditionalOnSingleCandidateUserAnnocation iConditionalOnSingleCandidateUserAnnocation;
无法唯一确定,则被ConditionalOnSingleCandidate修饰的bean将无法注入。从上例子中,

  • 当ConditionalOnSingleCandidateUserAnnocation和ConditionalOnSingleCandidateUserAnnocation1都没有配置@Primary注解时,@Autowired注入属性iConditionalOnSingleCandidateUserAnnocation无法唯一确定。因此ConditionalOnSingleCandidateUser注入失败。

  • 当ConditionalOnSingleCandidateUserAnnocation和ConditionalOnSingleCandidateUserAnnocation1有且只有一个配置了@Primary注解时,@Autowired能唯一确定被Primary修饰的bean将是属性依赖注入的bean,因此ConditionalOnSingleCandidateUser注册容器成功。

  • 但ConditionalOnSingleCandidateUserAnnocation和ConditionalOnSingleCandidateUserAnnocation1同时都被@Primary修饰时,Spring容器又不知道哪个被Autowired注入了,此时ConditionalOnSingleCandidateUser注册到容器中失败。

  我相信此时大家对ConditionalOnSingleCandidate注解的使用己经有了深刻的理解下,下面,我们再来看另外一个注解。ConditionalOnMissingBean的使用

ConditionalOnMissingBean注解

  从ConditionalOnMissingBean的字面意思理解,只要是ConditionalOnMissingBean的属性bean在容器中不存在,则当前被ConditionalOnMissingBean注解修饰的bean将注册到容器中,真是这样吗?我们通过例子来证实我们的猜测。
在这里插入图片描述
  从类结构来说,ConditionalOnMissingBean多了一个ignored和ignoredType属性。后面再来分析ignored属性的使用

  1. 创建普通bean
@Service
public class ConditionalOnMissingBeanAnnocation {
}

  1. 创建测试bean ConditionalOnMissingBeanUser
@Service
@ConditionalOnMissingBean(ConditionalOnMissingBeanAnnocation.class)
public class ConditionalOnMissingBeanUser {
}

  1. 开始测试
    在这里插入图片描述
      显然ConditionalOnMissingBeanUser的bean并没有注入到容器中。
  2. 当我们将ConditionalOnBeanAnnocation1从容器中移除
    在这里插入图片描述
      显然ConditionalOnMissingBeanUser己经注册到容器中。从测试结果中可以看出,当ConditionalOnMissingBean的属性中的bean存在于容器中,ConditionalOnMissingBeanUser 的bean将不会注册到容器中,因此被ConditionalOnMissingBean修饰的bean和ConditionalOnMissingBean的属性值的bean是存在你死我活的关系。下面再来看,假如ConditionalOnMissingBean注解中是一个属性数组时,会怎样呢?
  3. 再次创建一个普通类 ConditionalOnMissingBeanAnnocation1,没有注册到容器中。
@Service
@ConditionalOnMissingBean({ConditionalOnMissingBeanAnnocation.class,ConditionalOnMissingBeanAnnocation1.class})
public class ConditionalOnMissingBeanUser {

}

  测试结果
在这里插入图片描述
  从测试结果来看,当ConditionalOnMissingBeanAnnocation和ConditionalOnMissingBeanAnnocation1都不存在于容器中,ConditionalOnMissingBeanUser注册到容器成功。

  1. 假如ConditionalOnMissingBeanAnnocation和ConditionalOnMissingBeanAnnocation1有一个存在于容器中,会怎样呢?在ConditionalOnMissingBeanAnnocation类上添加@Service注解
    在这里插入图片描述

  从测试结果中,可以发现ConditionalOnMissingBeanUser注入失败。因此,只要ConditionalOnMissingBean中的属性任意一个存在容器中,则被ConditionalOnMissingBean修饰的bean将不会被注册到容器中。
  接下来,我们来看看其另外一个属性ignored,这个属性有什么用呢?不就是忽略嘛,忽略掉ConditionalOnMissingBean中的value属性。那怎样测试呢?

@Service
@ConditionalOnMissingBean(value={ConditionalOnMissingBeanAnnocation.class,ConditionalOnMissingBeanAnnocation1.class},
        ignored = ConditionalOnMissingBeanAnnocation.class)
public class ConditionalOnMissingBeanUser {

}

  ConditionalOnMissingBeanAnnocation不会被注册到容器中,但ConditionalOnMissingBeanAnnocation1会被注册到容器中,ignored中配置了ConditionalOnMissingBeanAnnocation属性。
  测试结果
在这里插入图片描述
  ConditionalOnMissingBeanUser注册到容器成功。
  我们测试了那么多例子,那关于ConditionalOnSingleCandidate,ConditionalOnMissingBean,ConditionalOnBean的源码又是如何实现的呢?
  根据之前ConditionalOnClass源码解析经验,最终判断是否注册到容器中,是由
在这里插入图片描述
  注解ConditionalOnClass上的Conditional配置的类确定。因此,我们进入OnBeanCondition类。正如所料。

public ConditionOutcome getMatchOutcome(ConditionContext context,
		AnnotatedTypeMetadata metadata) {
	ConditionMessage matchMessage = ConditionMessage.empty();
	//如果bean配置了ConditionalOnBean注解
	if (metadata.isAnnotated(ConditionalOnBean.class.getName())) {
		BeanSearchSpec spec = new BeanSearchSpec(context, metadata,
				ConditionalOnBean.class);
		//根据ConditionalOnBean配置的属性,从容器中获取beanNames
		List<String> matching =  getMatchingBeans(context, spec);
		//如果容器中只要存在一个bean,则被注解修饰的bean就不会被注册到容器中
		if (matching.isEmpty()) {
			return ConditionOutcome.noMatch(
					ConditionMessage.forCondition(ConditionalOnBean.class, spec)
							.didNotFind("any beans").atAll());
		}
		matchMessage = matchMessage.andCondition(ConditionalOnBean.class, spec)
				.found("bean", "beans").items(Style.QUOTE, matching);
	}
	//如果bean配置了ConditionalOnSingleCandidate注解
	if (metadata.isAnnotated(ConditionalOnSingleCandidate.class.getName())) {
		BeanSearchSpec spec = new SingleCandidateBeanSearchSpec(context, metadata,
				ConditionalOnSingleCandidate.class);
		//根据ConditionalOnSingleCandidate配置的属性,从容器中获取beanNames
		List<String> matching =  getMatchingBeans(context, spec);
		//如果容器中一个bean都不存在,则被ConditionalOnSingleCandidate注解修饰的bean将不会注册到容器中
		if (matching.isEmpty()) {
			return ConditionOutcome.noMatch(ConditionMessage
					.forCondition(ConditionalOnSingleCandidate.class, spec)
					.didNotFind("any beans").atAll());
		}
		//如果一个bean在容器中并不是唯一存在,同时也没有Primary注解修饰,
		//则被ConditionalOnSingleCandidate修饰的bean不注册到容器中
		else if (!hasSingleAutowireCandidate(context.getBeanFactory(), matching,
				spec.getStrategy() == SearchStrategy.ALL)) {
			return ConditionOutcome.noMatch(ConditionMessage
					.forCondition(ConditionalOnSingleCandidate.class, spec)
					.didNotFind("a primary bean from beans")
					.items(Style.QUOTE, matching));
		}
		matchMessage = matchMessage
				.andCondition(ConditionalOnSingleCandidate.class, spec)
				.found("a primary bean from beans").items(Style.QUOTE, matching);
	}
	//如果bean配置了ConditionalOnMissingBean注解
	if (metadata.isAnnotated(ConditionalOnMissingBean.class.getName())) {
		BeanSearchSpec spec = new BeanSearchSpec(context, metadata,
				ConditionalOnMissingBean.class);
		//根据ConditionalOnMissingBean配置的属性,从容器中获取beanNames
		List<String> matching = getMatchingBeans(context, spec);
		//只要有bean存在,则被注解ConditionalOnMissingBean修饰的bean将不会注册到容器中
		if (!matching.isEmpty()) {
			return ConditionOutcome.noMatch(ConditionMessage
					.forCondition(ConditionalOnMissingBean.class, spec)
					.found("bean", "beans").items(Style.QUOTE, matching));
		}
		matchMessage = matchMessage.andCondition(ConditionalOnMissingBean.class, spec)
				.didNotFind("any beans").atAll();
	}
	return ConditionOutcome.match(matchMessage);
}

  我相信理解了ConditionalOnMissingBean,ConditionalOnSingleCandidate,及ConditionalOnBean的使用之后,再来看源码,我相信很容易理解了,但是上述代码中需要注意一些方法,先来看getMatchingBeans方法

private List<String> getMatchingBeans(ConditionContext context,
		BeanSearchSpec beans) {
	ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
	//如果查找模式是parent或者祖先模式,则从父容器开始查起
	if (beans.getStrategy() == SearchStrategy.PARENTS
			|| beans.getStrategy() == SearchStrategy.ANCESTORS) {
		BeanFactory parent = beanFactory.getParentBeanFactory();
		Assert.isInstanceOf(ConfigurableListableBeanFactory.class, parent,
				"Unable to use SearchStrategy.PARENTS");
		beanFactory = (ConfigurableListableBeanFactory) parent;
	}
	//如果父容器为空,直接返回空集合
	if (beanFactory == null) {
		return Collections.emptyList();
	}
	List<String> beanNames = new ArrayList<String>();
	//如果查找模式只是在当前容器中查找,则不会到父容器中去查找
	boolean considerHierarchy = beans.getStrategy() != SearchStrategy.CURRENT;
	//根据类型查找
	for (String type : beans.getTypes()) {
		beanNames.addAll(getBeanNamesForType(beanFactory, type,
				context.getClassLoader(), considerHierarchy));
	}
	//移除掉忽略的bean
	for (String ignoredType : beans.getIgnoredTypes()) {
		beanNames.removeAll(getBeanNamesForType(beanFactory, ignoredType,
				context.getClassLoader(), considerHierarchy));
	}
	//注解上配置的注解是否在容器中的其他bean上配置了该注解,如果配置了,将bean名称加入到beanNames中
	for (String annotation : beans.getAnnotations()) {
		beanNames.addAll(Arrays.asList(getBeanNamesForAnnotation(beanFactory,
				annotation, context.getClassLoader(), considerHierarchy)));
	}
	//根据名称从容器中查找
	for (String beanName : beans.getNames()) {
		if (containsBean(beanFactory, beanName, considerHierarchy)) {
			beanNames.add(beanName);
		}
	}
	return beanNames;
}

  从上面示例中,我们知道ConditionalOnSingleCandidate注解修饰的类,如果在Spring容器中,不能唯一确定,被修饰的类不会注册到容器中,其实现源码如下。

private boolean hasSingleAutowireCandidate(
		ConfigurableListableBeanFactory beanFactory, List<String> beanNames,
		boolean considerHierarchy) {
	//如果只有一个bean或者存在多个bean,但是只有其中一个bean被@Primary注解修饰,则返回true
	return (beanNames.size() == 1
			|| getPrimaryBeans(beanFactory, beanNames, considerHierarchy)
					.size() == 1);
}

private List<String> getPrimaryBeans(ConfigurableListableBeanFactory beanFactory,
		List<String> beanNames, boolean considerHierarchy) {
	List<String> primaryBeans = new ArrayList<String>();
	for (String beanName : beanNames) {
		BeanDefinition beanDefinition = findBeanDefinition(beanFactory, beanName,
				considerHierarchy);
		if (beanDefinition != null && beanDefinition.isPrimary()) {
			primaryBeans.add(beanName);
		}
	}
	return primaryBeans;
}

  上述情况,还有一段源码需要注意一下,就是属性的types,names等属性的封装这一块。

BeanSearchSpec(ConditionContext context, AnnotatedTypeMetadata metadata,
		Class<?> annotationType) {
	this.annotationType = annotationType;
	MultiValueMap<String, Object> attributes = metadata
			.getAllAnnotationAttributes(annotationType.getName(), true);
	collect(attributes, "name", this.names);
	//注解中value和type配置的属性都放到了types属性中
	collect(attributes, "value", this.types);
	collect(attributes, "type", this.types);
	collect(attributes, "annotation", this.annotations);
	collect(attributes, "ignored", this.ignoredTypes);
	collect(attributes, "ignoredType", this.ignoredTypes);
	//获取从容器中查找策略
	this.strategy = (SearchStrategy) metadata
			.getAnnotationAttributes(annotationType.getName()).get("search");
	BeanTypeDeductionException deductionException = null;
	try {
		if (this.types.isEmpty() && this.names.isEmpty()) {
			//当配置的属性types和names为空时,通过addDeducedBeanType获取types
			addDeducedBeanType(context, metadata, this.types);
		}
	}
	catch (BeanTypeDeductionException ex) {
		deductionException = ex;
	}
	validate(deductionException);
}

  上述代码其他的还好理解,无非将注解配置的属性value数组,type数组等封装到相应的属性中,但是有一种情况难以理解,那就是当我们在注解中什么都不配置时,这个时候需要通过addDeducedBeanType方法来获取types参数。那我们来看看一个例子。

@Configuration
public class ConditionalOnMissingBeanConfig {

    @Bean
    @ConditionalOnMissingBean
    public ConditionalOnMissingBeanDo conditionalOnMissingBeanDo(){
        System.out.println("第一次实例化ConditionalOnMissingBeanDo");
        return new ConditionalOnMissingBeanDo();
    }

    class ConditionalOnMissingBeanDo{

    }
}

在这里插入图片描述
  显然己经进来了,而创建ConditionalOnMissingBeanDo的bean上配置了ConditionalOnMissingBean注解。
测试结果
在这里插入图片描述

private void addDeducedBeanType(ConditionContext context,
		AnnotatedTypeMetadata metadata, final List<String> beanTypes) {
	if (metadata instanceof MethodMetadata
			&& metadata.isAnnotated(Bean.class.getName())) {
		addDeducedBeanTypeForBeanMethod(context, (MethodMetadata) metadata,
				beanTypes);
	}
}
private void addDeducedBeanTypeForBeanMethod(ConditionContext context,
		MethodMetadata metadata, final List<String> beanTypes) {
	try {
		Class<?> returnType = ClassUtils.forName(metadata.getReturnTypeName(),
				context.getClassLoader());
		beanTypes.add(returnType.getName());
	}
	catch (Throwable ex) {
		throw new BeanTypeDeductionException(metadata.getDeclaringClassName(),
				metadata.getMethodName(), ex);
	}
}

  我相信看了例子以后,再来理解上述源码就简单得多了,当注解配置在方法上,并且没有设置注解属性时,则取方法的返回值类型作为注解属性的types的值。那这么设置的应用场景是什么呢?我们接着上面的例子继续扩展。
在这里插入图片描述
  当两个方法都没有配置ConditionalOnMissingBean注解时,此时Spring容器实例化了两个ConditionalOnMissingBeanDo bean到容器中,当方法上配置了ConditionalOnMissingBean注解时。

在这里插入图片描述
  容器只实例化了一次ConditionalOnMissingBeanDo bean对象。有人在想,这种有没有应用场景呢?我们来看一下mybatis中SqlSessionFactory对这个注解的使用。
在这里插入图片描述

  对于ConditionalOnBean,ConditionalOnSingleCandidate,ConditionalOnMissingBean注解的使用及源码解析,下面,我们来看另外一个注解EnableConfigurationProperties的使用及源码解析。

EnableConfigurationProperties注解

  关于EnableConfigurationProperties注解怎样使用呢?这个我也不知道,但是我们发现MybatisAutoConfiguration也这样使用,那么我们也学着这么用。

@org.springframework.context.annotation.Configuration
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
@ConditionalOnBean(DataSource.class)
@EnableConfigurationProperties(MybatisProperties.class)
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class MybatisAutoConfiguration {

...
}
  1. 创建配置类
@ConfigurationProperties(prefix = "xxx.test")
public class EnableConfigurationPropertiesTest {


    private String username;
    private String password;
    ... 省略
}

  1. 在配置文件中配置相关属性

在这里插入图片描述

  1. 创建测试类
@Service
@EnableConfigurationProperties(EnableConfigurationPropertiesTest.class)
public class EnableConfigurationPropertiesBean {


}
  1. 开始测试
@Autowired
private EnableConfigurationPropertiesTest enableConfigurationPropertiesTest;

@RequestMapping("enableConfigurationPropertiesTest")
public String enableConfigurationPropertiesTest() {
    System.out.println(JSON.toJSONString(enableConfigurationPropertiesTest));
    return "Sucess";
}

  1. 测试结果
    在这里插入图片描述

  2. 当我们注释掉EnableConfigurationPropertiesBean的EnableConfigurationProperties注解时
    在这里插入图片描述

  项目启动报错。这个注解的使用非常广泛,比如Spring Boot中的redis,rabbitmq,mysql等,都是通过这个注解的使用来对参数初始化的。
  既然这么好用,那么实现原理是什么呢?先来看看EnableConfigurationProperties的源码。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(EnableConfigurationPropertiesImportSelector.class)
public @interface EnableConfigurationProperties {
	Class<?>[] value() default {};
}

  上述代码中有一个Import注解里配置了EnableConfigurationPropertiesImportSelector,这个属性很重要,后面再来看在源码中的使用。

  先通过全局搜索,看EnableConfigurationProperties.class在哪里使用,果不其然,在EnableConfigurationPropertiesImportSelector中用到了

在这里插入图片描述

  那么我们在EnableConfigurationPropertiesImportSelector的selectImports中打一个断点。看调用栈中,从哪里开始调用,下面的代码就是selectImports方法的入口代码。对于invokeBeanFactoryPostProcessors(beanFactory);方法调用,我们再熟悉不过了,就是激活各种BeanFactory处理器,那么在其内部如何实现呢?
在这里插入图片描述
  我们中转到我们和EnableConfigurationProperties注解相关的代码。

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
		throws IOException {

	// Recursively process any member (nested) classes first
	processMemberClasses(configClass, sourceClass);

	// Process any @PropertySource annotations
	for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
			sourceClass.getMetadata(), PropertySources.class,
			org.springframework.context.annotation.PropertySource.class)) {
		if (this.environment instanceof ConfigurableEnvironment) {
			processPropertySource(propertySource);
		}
		else {
			logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
					"]. Reason: Environment must implement ConfigurableEnvironment");
		}
	}

	// Process any @ComponentScan annotations
	Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
			sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
	if (!componentScans.isEmpty() &&
			!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
		for (AnnotationAttributes componentScan : componentScans) {
			// The config class is annotated with @ComponentScan -> perform the scan immediately
			Set<BeanDefinitionHolder> scannedBeanDefinitions =
					this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
			// Check the set of scanned definitions for any further config classes and parse recursively if needed
			for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
				if (ConfigurationClassUtils.checkConfigurationClassCandidate(
						holder.getBeanDefinition(), this.metadataReaderFactory)) {
					parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());
				}
			}
		}
	}

	// Process any @Import annotations
	processImports(configClass, sourceClass, getImports(sourceClass), true);

	// Process any @ImportResource annotations
	if (sourceClass.getMetadata().isAnnotated(ImportResource.class.getName())) {
		AnnotationAttributes importResource =
				AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
		String[] resources = importResource.getStringArray("locations");
		Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
		for (String resource : resources) {
			String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
			configClass.addImportedResource(resolvedResource, readerClass);
		}
	}

	// Process individual @Bean methods
	Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
	for (MethodMetadata methodMetadata : beanMethods) {
		configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
	}

	// Process default methods on interfaces
	processInterfaces(configClass, sourceClass);

	// Process superclass, if any
	if (sourceClass.getMetadata().hasSuperClass()) {
		String superclass = sourceClass.getMetadata().getSuperClassName();
		if (!superclass.startsWith("java") && !this.knownSuperclasses.containsKey(superclass)) {
			this.knownSuperclasses.put(superclass, configClass);
			// Superclass found, return its annotation metadata and recurse
			return sourceClass.getSuperClass();
		}
	}

	// No superclass -> processing is complete
	return null;
}

  上面中每一行代码都很重要,但是我们只关心processImports方法,而processImports这一行方法中,有一个非常关键的方法调用。getImports(sourceClass)调用,那么这个方法调用的返回值是下一个方法运行不可缺少的参数。那我们看看这个方法的实现

private Set getImports(SourceClass sourceClass) throws IOException {
	Set imports = new LinkedHashSet();
	Set visited = new LinkedHashSet();
	collectImports(sourceClass, imports, visited);
	return imports;
}

private void collectImports(SourceClass sourceClass, Set imports, Set visited)
		throws IOException {

	if (visited.add(sourceClass)) {
		for (SourceClass annotation : sourceClass.getAnnotations()) {
			String annName = annotation.getMetadata().getClassName();
			if (!annName.startsWith("java") && !annName.equals(Import.class.getName())) {
				//递归调用collectImports方法
				collectImports(annotation, imports, visited);
			}
		}
		imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
	}
}

  getImports方法的意图是什么呢?我相信仔细看一下代码就明白,无非就是获取Bean上的注解的注解中是否配置了Import注解,如果配置了,则获取Import的value值加入到imports中。因此,EnableConfigurationProperties注解上配置了Import注解,并且属性值为EnableConfigurationPropertiesImportSelector。从getImport方法中,得知importCandidates的值是EnableConfigurationPropertiesImportSelector对象。

  下面来看processImports方法到底做了哪些事情呢?

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
		Collection<SourceClass> importCandidates, boolean checkForCircularImports) {

	if (importCandidates.isEmpty()) {
		return;
	}

	if (checkForCircularImports && isChainedImportOnStack(configClass)) {
		this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
	}
	else {
		this.importStack.push(configClass);
		try {
			for (SourceClass candidate : importCandidates) {
				if (candidate.isAssignable(ImportSelector.class)) {
					// Candidate class is an ImportSelector -> delegate to it to determine imports
					Class<?> candidateClass = candidate.loadClass();
					//实例化EnableConfigurationPropertiesImportSelector对象
					ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
					ParserStrategyUtils.invokeAwareMethods(
							selector, this.environment, this.resourceLoader, this.registry);
					if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
						this.deferredImportSelectors.add(
								new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
					}
					else {
						//调用EnableConfigurationPropertiesImportSelector的selectImports方法
						String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
						Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
						processImports(configClass, currentSourceClass, importSourceClasses, false);
					}
				}
				else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
					// Candidate class is an ImportBeanDefinitionRegistrar ->
					// delegate to it to register additional bean definitions
					Class<?> candidateClass = candidate.loadClass();
					ImportBeanDefinitionRegistrar registrar =
							BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
					ParserStrategyUtils.invokeAwareMethods(
							registrar, this.environment, this.resourceLoader, this.registry);
					configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
				}
				else {
					// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
					// process it as an @Configuration class
					this.importStack.registerImport(
							currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
					processConfigurationClass(candidate.asConfigClass(configClass));
				}
			}
		}
		catch (BeanDefinitionStoreException ex) {
			throw ex;
		}
		catch (Throwable ex) {
			throw new BeanDefinitionStoreException(
					"Failed to process import candidates for configuration class [" +
					configClass.getMetadata().getClassName() + "]", ex);
		}
		finally {
			this.importStack.pop();
		}
	}
}

  上述代码中,我们先来看EnableConfigurationPropertiesImportSelector的selectImports方法具体实现。

public String[] selectImports(AnnotationMetadata metadata) {
	MultiValueMap<String, Object> attributes = metadata.getAllAnnotationAttributes(
			EnableConfigurationProperties.class.getName(), false);
	Object[] type = attributes == null ? null
			: (Object[]) attributes.getFirst("value");
	if (type == null || type.length == 0) {
		return new String[] {
				ConfigurationPropertiesBindingPostProcessorRegistrar.class
						.getName() };
	}
	
	//如果bean的EnableConfigurationProperties注解上配置有值
	return new String[] { ConfigurationPropertiesBeanRegistrar.class.getName(),
			ConfigurationPropertiesBindingPostProcessorRegistrar.class.getName() };
}

  selectImports的方法实现很简单,如果bean的EnableConfigurationProperties注解上配置有值,则返回{ ConfigurationPropertiesBeanRegistrar.class.getName(),
ConfigurationPropertiesBindingPostProcessorRegistrar.class.getName() };数组。
  接下来,我们继续看,返回了ConfigurationPropertiesBeanRegistrar和ConfigurationPropertiesBindingPostProcessorRegistrar的类名称后,又做哪些事情呢?我们看到,当调用selectImports获得返回值后,将返回值名称实例化成bean集合,之后继续递归调用processImports方法,最终将调用addImportBeanDefinitionRegistrar方法将ConfigurationPropertiesBeanRegistrar和ConfigurationPropertiesBindingPostProcessorRegistrar的实例以key的形式存储到importBeanDefinitionRegistrars Map中。

public void addImportBeanDefinitionRegistrar(ImportBeanDefinitionRegistrar registrar, AnnotationMetadata importingClassMetadata) {
	this.importBeanDefinitionRegistrars.put(registrar, importingClassMetadata);
}

  这个有什么用,我们也先记着。后面再来获取这个值。我们既然获取了ConfigurationPropertiesBeanRegistrar和ConfigurationPropertiesBindingPostProcessorRegistrar对象,那在这个对象中打一个断点看看。
在这里插入图片描述
  从上图中,我们看到在Spring启动的某个方法中会调用以上方法,最终调用了我们的ConfigurationPropertiesBeanRegistrar的registerBeanDefinitions方法。
  既然是遍历所有的bean,调用其loadBeanDefinitionsForConfigurationClass方法。我们进入loadBeanDefinitionsForConfigurationClass方法看看。

private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass,
		TrackedConditionEvaluator trackedConditionEvaluator) {

	if (trackedConditionEvaluator.shouldSkip(configClass)) {
		String beanName = configClass.getBeanName();
		if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
			this.registry.removeBeanDefinition(beanName);
		}
		this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
		return;
	}

	if (configClass.isImported()) {
		registerBeanDefinitionForImportedConfigurationClass(configClass);
	}
	for (BeanMethod beanMethod : configClass.getBeanMethods()) {
		loadBeanDefinitionsForBeanMethod(beanMethod);
	}
	loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
	loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}

  看到上面加粗的代码没有,上面加粗的代码中getImportBeanDefinitionRegistrars不就是我们之前千辛万苦分析出来的importBeanDefinitionRegistrars的值嘛。而这个值,我们之前分析是通过调用EnableConfigurationPropertiesImportSelector的selectImports方法返回的ConfigurationPropertiesBeanRegistrar和ConfigurationPropertiesBindingPostProcessorRegistrar类对象。我们继续跟进loadBeanDefinitionsFromRegistrars方法。

private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
	for (Map.Entry<ImportBeanDefinitionRegistrar, AnnotationMetadata> entry : registrars.entrySet()) {
		entry.getKey().registerBeanDefinitions(entry.getValue(), this.registry);
	}
}

  loadBeanDefinitionsFromRegistrars的实现很简单,无非遍历map,调用key的registerBeanDefinitions方法,也就是调用ConfigurationPropertiesBeanRegistrar和ConfigurationPropertiesBindingPostProcessorRegistrar对象的registerBeanDefinitions方法。我们进入到ConfigurationPropertiesBeanRegistrar的registerBeanDefinitions方法中。

public static class ConfigurationPropertiesBeanRegistrar
		implements ImportBeanDefinitionRegistrar {

	@Override
	public void registerBeanDefinitions(AnnotationMetadata metadata,
			BeanDefinitionRegistry registry) {
		MultiValueMap<String, Object> attributes = metadata
				.getAllAnnotationAttributes(
						EnableConfigurationProperties.class.getName(), false);
		List<Class<?>> types = collectClasses(attributes.get("value"));
		for (Class<?> type : types) {
			String prefix = extractPrefix(type);
			String name = (StringUtils.hasText(prefix) ? prefix + "-" + type.getName()
					: type.getName());
			//如果容器中不存在该beanDefinition
			if (!registry.containsBeanDefinition(name)) {
				registerBeanDefinition(registry, type, name);
			}
		}
	}
	
	private String extractPrefix(Class<?> type) {
		ConfigurationProperties annotation = AnnotationUtils.findAnnotation(type,
				ConfigurationProperties.class);
		if (annotation != null) {
			return annotation.prefix();
		}
		return "";
	}
	...
	private void registerBeanDefinition(BeanDefinitionRegistry registry,
			Class<?> type, String name) {
		BeanDefinitionBuilder builder = BeanDefinitionBuilder
				.genericBeanDefinition(type);
		AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
		registry.registerBeanDefinition(name, beanDefinition);

		ConfigurationProperties properties = AnnotationUtils.findAnnotation(type,
				ConfigurationProperties.class);
		Assert.notNull(properties,
				"No " + ConfigurationProperties.class.getSimpleName()
						+ " annotation found on  '" + type.getName() + "'.");
	}
}

  上述代码的实现逻辑也非常简单,获取到bean上配置的EnableConfigurationProperties注解的所有值,并注册beanDefinition。
  Bean己经注册好了,那么Bean的值又是如何填充的呢?
  在茫茫代码中去找到哪里注入,这也太难了,不知道读者有没有学到我的一招杀手锏,那就是结果索因法,比如警察找坏人,只需要到坏人家中等待,等坏人回来,直接抓住即可,但人的世界很复杂,在代码的世界,这招却是屡试不爽,因此对于这种无从查起的代码,用结果索因,无疑是最快,最好的选择。

  我们在EnableConfigurationPropertiesTest的setUsername中打一个断点,立即看到了调用的堆栈信息。
在这里插入图片描述
  从调用栈中找到,在postProcessBeforeInitialization方法中,有几行关键的代码。

public Object postProcessBeforeInitialization(Object bean, String beanName)
		throws BeansException {
	ConfigurationProperties annotation = AnnotationUtils
			.findAnnotation(bean.getClass(), ConfigurationProperties.class);
	if (annotation != null) {
		postProcessBeforeInitialization(bean, beanName, annotation);
	}
	annotation = this.beans.findFactoryAnnotation(beanName,
			ConfigurationProperties.class);
	if (annotation != null) {
		postProcessBeforeInitialization(bean, beanName, annotation);
	}
	return bean;
}

  从上述方法中可以看到,只要bean在实例化时,会调用postProcessBeforeInitialization方法,当bean配置了ConfigurationProperties注解时,就会调用postProcessBeforeInitialization方法,而为什么bean的初始化过程会调用postProcessBeforeInitialization方法呢?我们来看一张图。
在这里插入图片描述
  在bean的生命周期中,肯定会调用postProcessBeforeInitialization方法,这个就是Spring写死在代码中的,没有什么原因。在调用栈中,有一个重要的方法doBindPropertiesToTarget 绑定属性到目标对象中,接下来,我们来分析里面的关键代码。

private void doBindPropertiesToTarget() throws BindException {
	RelaxedDataBinder dataBinder = (this.targetName != null
			? new RelaxedDataBinder(this.target, this.targetName)
			: new RelaxedDataBinder(this.target));
	if (this.validator != null
			&& this.validator.supports(dataBinder.getTarget().getClass())) {
		dataBinder.setValidator(this.validator);
	}
	if (this.conversionService != null) {
		dataBinder.setConversionService(this.conversionService);
	}
	dataBinder.setAutoGrowCollectionLimit(Integer.MAX_VALUE);
	dataBinder.setIgnoreNestedProperties(this.ignoreNestedProperties);
	dataBinder.setIgnoreInvalidFields(this.ignoreInvalidFields);
	dataBinder.setIgnoreUnknownFields(this.ignoreUnknownFields);
	customizeBinder(dataBinder);
	if (this.applicationContext != null) {
		ResourceEditorRegistrar resourceEditorRegistrar = new ResourceEditorRegistrar(
				this.applicationContext, this.applicationContext.getEnvironment());
		resourceEditorRegistrar.registerCustomEditors(dataBinder);
	}
	Iterable<String> relaxedTargetNames = getRelaxedTargetNames();
	Set<String> names = getNames(relaxedTargetNames);
	PropertyValues propertyValues = getPropertySourcesPropertyValues(names,
			relaxedTargetNames);
	dataBinder.bind(propertyValues);
	if (this.validator != null) {
		dataBinder.validate();
	}
	checkForBindingErrors(dataBinder);
}

  通过getRelaxedTargetNames()方法返回了一系列的前缀,这个是什么意思呢?通过getPropertySourcesPropertyValues方法内部的代码可以得知,在application.yml中,只要配置了
  xxx.test
  xxx_test
  xxxTest
  xxxtest
  XXX.TEST
  XXX_TEST
  XXXTEST
  的任意一种,都可以属性绑定。
在这里插入图片描述
  既然如何,我们还是来测试一把。
在这里插入图片描述
  上述代码中有一个比较关键的方法,即是getPropertySourcesPropertyValues()方法,这个方法中获取的propertyValues,而数据绑定就是用propertyValues来绑定的。

private PropertyValues getPropertySourcesPropertyValues(Set<String> names,
		Iterable<String> relaxedTargetNames) {
	PropertyNamePatternsMatcher includes = getPropertyNamePatternsMatcher(names,
			relaxedTargetNames);
	return new PropertySourcesPropertyValues(this.propertySources, names, includes,
			this.resolvePlaceholders);
}

PropertySourcesPropertyValues(PropertySources propertySources,
		Collection<String> nonEnumerableFallbackNames,
		PropertyNamePatternsMatcher includes, boolean resolvePlaceholders) {
	Assert.notNull(propertySources, "PropertySources must not be null");
	Assert.notNull(includes, "Includes must not be null");
	this.propertySources = propertySources;
	this.nonEnumerableFallbackNames = nonEnumerableFallbackNames;
	this.includes = includes;
	this.resolvePlaceholders = resolvePlaceholders;
	PropertySourcesPropertyResolver resolver = new PropertySourcesPropertyResolver(
			propertySources);
	for (PropertySource<?> source : propertySources) {
		processPropertySource(source, resolver);
	}
}

  会遍历所有的propertySources,而propertySources的数据来源就是环境数据,命令行数据,以及我们配置的application.yml 或application-dev.yml数据,最终都会保存到propertySources中,目前在【Spring源码深度解析(郝佳)-学习-Spring Boot体系原理】这篇博客对于Spring Boot环境数据的注册做了详细分析,但是那篇博客还有一些内容依赖于这篇博客,所以,没有写完,不过后面会发出来的。既然不能看原理,那就看一下数据吧。
在这里插入图片描述

private void processPropertySource(PropertySource<?> source,
		PropertySourcesPropertyResolver resolver) {
	if (source instanceof CompositePropertySource) {
		processCompositePropertySource((CompositePropertySource) source, resolver);
	}
	else if (source instanceof EnumerablePropertySource) {
		processEnumerablePropertySource((EnumerablePropertySource<?>) source,
				resolver, this.includes);
	}
	else {
		processNonEnumerablePropertySource(source, resolver);
	}
}

private void processEnumerablePropertySource(EnumerablePropertySource<?> source,
		PropertySourcesPropertyResolver resolver,
		PropertyNamePatternsMatcher includes) {
	if (source.getPropertyNames().length > 0) {
		for (String propertyName : source.getPropertyNames()) {
			if (includes.matches(propertyName)) {
				Object value = getEnumerableProperty(source, resolver, propertyName);
				putIfAbsent(propertyName, value, source);
			}
		}
	}
}

private Object getEnumerableProperty(EnumerablePropertySource<?> source,
		PropertySourcesPropertyResolver resolver, String propertyName) {
	try {
		if (this.resolvePlaceholders) {
			return resolver.getProperty(propertyName, Object.class);
		}
	}
	catch (RuntimeException ex) {
	
	}
	return source.getProperty(propertyName);
}


@Override
public <T> T getProperty(String key, Class<T> targetValueType) {
	return getProperty(key, targetValueType, true);
}


protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {
	if (this.propertySources != null) {
		for (PropertySource<?> propertySource : this.propertySources) {
			if (logger.isTraceEnabled()) {
				logger.trace("Searching for key '" + key + "' in PropertySource '" +
						propertySource.getName() + "'");
			}
			Object value = propertySource.getProperty(key);
			if (value != null) {
				if (resolveNestedPlaceholders && value instanceof String) {
					value = resolveNestedPlaceholders((String) value);
				}
				logKeyFound(key, propertySource, value);
				return convertValueIfNecessary(value, targetValueType);
			}
		}
	}
	if (logger.isDebugEnabled()) {
		logger.debug("Could not find key '" + key + "' in any property source");
	}
	return null;
}

在这里插入图片描述

  从调用栈结果来看,最终调用了getProperty方法,返回的XXX_TEST.username的值。有了username和password的PropertyValues,最后无非是通过反射调用set方法填充属性值而已。

在这里插入图片描述
  不过上述中有一点还是需要注意的是,关于实体中的变量名问题,变量应该怎样命名。我们将username改成USERNAME
在这里插入图片描述
  当我们将username改成userName时

在这里插入图片描述
  发现属性注入失败。
  但是我们想要驼峰的命名规则,那该怎么办呢?我们新增一个属性homeLocation, 在配置文件中配置home-location: 湖南省,测试结果如下。显然,属性注入进去。
在这里插入图片描述
  如果我们在配置文件中配置homeLocation属性,测试结果会怎样呢?
在这里插入图片描述

AutoConfigureAfter注解

  接下来,我们来看看AutoConfigureAfter的使用,这个注解是什么意思呢?假如B bean中配置了@AutoConfigureAfter(A.class)注解,那么在实例化B 类之前先实例化A。既然如此,还是来测试一把,看看。
创建三个类AutoConfigureAfterA和AutoConfigureAfterB和AutoConfigureAfterC类,AutoConfigureAfterB类需要先等AutoConfigureAfterA和AutoConfigureAfterC实例化后再实例化。

@Service
public class AutoConfigureAfterA {

    public AutoConfigureAfterA() {
        System.out.println("A实例化");
    }
}


@Service
public class AutoConfigureAfterC {

    public AutoConfigureAfterC() {
        System.out.println("C实例化");
    }
}


@Service
@AutoConfigureAfter({AutoConfigureAfterA.class, AutoConfigureAfterC.class})
@Import({AutoConfigureAfterA.class, AutoConfigureAfterC.class})
public class AutoConfigureAfterB {

    public AutoConfigureAfterB() {
        System.out.println("B实例化");
    }
}

  启动项目,发现没有效果
在这里插入图片描述
  将@Serivce注解改成@Configuration

//@Service
@Configuration
public class AutoConfigureAfterA {

    public AutoConfigureAfterA() {
        System.out.println("A实例化");
    }
}

//@Service
@Configuration
public class AutoConfigureAfterC {

    public AutoConfigureAfterC() {
        System.out.println("C实例化");
    }
}


//@Service
@Configuration
@AutoConfigureAfter({AutoConfigureAfterA.class, AutoConfigureAfterC.class})
@Import({AutoConfigureAfterA.class, AutoConfigureAfterC.class})
public class AutoConfigureAfterB {

    public AutoConfigureAfterB() {
        System.out.println("B实例化");
    }
}

  依然没有效果
在这里插入图片描述
  有classpath下添加META-INF,并在其下面添加spring.factories文件,文件内容为

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.springbootstudy.service.AutoConfigureAfterB

  启动项目,发现神奇的效果出现了。
在这里插入图片描述

  从测试结果来看AutoConfigureAfterA和AutoConfigureAfterC在AutoConfigureAfterB前面被先实例化。假如此时将AutoConfigureAfterA,AutoConfigureAfterC,AutoConfigureAfterB的@Configuration改成@Service注解会怎样呢?
在这里插入图片描述
  从上面的测试中,我们得出结论,要想AutoConfigureAfterA,AutoConfigureAfterC在AutoConfigureAfterB前实例化必需满足两个条件。

  • 被修饰的类必需配置@Configuration注解
  • 被AutoConfigureAfter修饰的类必需在META-INF的spring.factories文件中配置org.springframework.boot.autoconfigure.EnableAutoConfiguration=被AutoConfigureAfter注解修饰的类

  如果spring.factories中配置AutoConfigureAfter的属性类,如AutoConfigureAfterA和AutoConfigureAfterC,没有效果

AutoConfigureBefore注解

  和AutoConfigureAfter相对应的注解是AutoConfigureBefore,那AutoConfigureBefore又该如何使用呢?我相信读者看到这里,肯定知道如何测试了,因为测试方法和AutoConfigureAfter一样。在测试之前,我们来猜测一下效果,配置了AutoConfigureBefore的类一定比AutoConfigureBefore的属性类要后实例化吗?我们的猜测是如此,那还是来测试一把。

  1. 创建AutoConfigure配置类,AutoConfigureBeforeA和AutoConfigureBeforeC
@Configuration
public class AutoConfigureBeforeA {
    public AutoConfigureBeforeA() {
        System.out.println("BeforeA实例化");
    }
}

@Configuration
public class AutoConfigureBeforeC {
    public AutoConfigureBeforeC() {
        System.out.println("BeforeC实例化");
    }
}
  1. 创建测试类
@Configuration
@AutoConfigureBefore({AutoConfigureBeforeA.class, AutoConfigureBeforeC.class})
@Import({AutoConfigureBeforeA.class, AutoConfigureBeforeC.class})
public class AutoConfigureBeforeB {

    public AutoConfigureBeforeB() {
        System.out.println("BeforeB实例化");
    }
}
  1. META-INF目录下的配置文件spring.factories中添加
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=
    com.example.springbootstudy.service.AutoConfigureAfterB,
    com.example.springbootstudy.service.AutoConfigureBeforeB

  2. 开始测试
    在这里插入图片描述
      从测试结果中可以看出,AutoConfigureBeforeB比AutoConfigureBeforeA和AutoConfigureBeforeC后实例化。那么也就是说AutoConfigureBefore和AutoConfigureAfter的配置效果一样。我看网上有人说
      @AutoConfigureBefore(AAAA.class)
      public class CCCC {
      }
      说明 CCCC 将会在 AAAA 之前加载
      从我的测试结果中来看,我觉得单纯这么说是不对的,从测试效果来看AutoConfigureBefore注解和AutoConfigureAfter注解配置效果目前是一样的。但是肯定有一定区别,那有什么区别呢?我们修改META-INF目录下的配置文件spring.factories,在文件中继续添加AutoConfigureBeforeA和AutoConfigureBeforeC类。
    在这里插入图片描述
      因此,要想AutoConfigureBeforeB在AutoConfigureBeforeA和AutoConfigureBeforeC之前实例化,必需在META-INF目录下的spring.factories配置文件中配置AutoConfigureBeforeB,AutoConfigureBeforeA和AutoConfigureBeforeC类。如果没有配置AutoConfigureBeforeA和AutoConfigureBeforeC类的话,AutoConfigureBefore注解的效果AutoConfigureAfter注解的效果一样。

  那如果AutoConfigureBefore和AutoConfigureAfter结合起来使用,此时我想让AutoConfigureAfterB在AutoConfigureBeforeC后再实例化,此时在AutoConfigureAfterB的注解AutoConfigureAfter再添加属性AutoConfigureBeforeC。

@Configuration
@AutoConfigureAfter({AutoConfigureAfterA.class, AutoConfigureAfterC.class,AutoConfigureBeforeC.class})
public class AutoConfigureAfterB {
    public AutoConfigureAfterB() {
        System.out.println("B实例化");
    }
}

  测试结果
在这里插入图片描述
  我们都测试了一遍,那么AutoConfigureBefore和AutoConfigureAfter注解源码是怎样实现的呢?对于上面奇怪的测试现像,今天花了一天的时间研究了一下,发现整个过程还是有点复杂的,我尽量写得通俗一点吧。在解读源码的过程中,有一种非常好用的方法,叫结果索因法,因为顺着找的话,可能性太多,无法通读整个Spring源码,那么我们在代码经过的地方,打个断点,根据调用栈,就能分析出源码来龙去脉了。在这里,我们依然用结果索因来分析。
在这里插入图片描述

  首先,全局搜索AutoConfigureBefore.class在哪里用到过,发现只有AutoConfigurationSorter用到了,看getInPriorityOrder方法像是排序的方法,那我们就在这个方法中打一个断点。通过调用栈,我们发现,最终是在refreshContext()方法调用了refresh()方法。
在这里插入图片描述

  而refresh()方法中的invokeBeanFactoryPostProcessors()方法调用了我们的getInPriorityOrder()方法。我们知道,只要Spring容器启动,则一定会调用refresh()方法。而invokeBeanFactoryPostProcessors()这个方法主要是调用容器中的各个处理器,好像上面的分析,和我们的主题没有什么关系,你先别急,AutoConfigureBefore和AutoConfigureAfter注解实现还是比较隐晦的,需要将前期工作做好,后面分析得到的结果才是水到渠成。
  我们来看中途有一个方法selectImports是不是和我们之前的ConditionalOnBean注解的源码分析很像了,即使很像,但是好像还是和我们的AutoConfigureBefore,AutoConfigureAfter注解没有太大关系。
在这里插入图片描述
  既然如此,我们回头看看AutoConfigurationSorter类。
在这里插入图片描述
  细心的读者肯定发现了类里有面AutoConfigureBefore,AutoConfigureAfter,AutoConfigureOrder这些东西,只是无从下手而已,Configure的初始化先后顺序肯定和这个类有关系,这次好你通过结果索因,不太好找问题了,但是也让我们发现的蛛丝马迹。那好,反着不行,那我们顺着来。
  我们先来到ConfigurationClassPostProcessor的processConfigBeanDefinitions方法,发现其parser.parse(candidates);的candidates参数值竟然是Spring boot 项目的启动类SpringBootStudyApplication。
在这里插入图片描述
  因为现在问题无从查起,那我们就一步步来,先将我们不懂的东西弄明白,通过地毯式的搜索,最终那些隐藏的东西都会被挖出来。那我们一步步的弄明白那些不懂的东西,先不去找我们要找的东西。
  言归正传,继续问十万个为什么?

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
	List<BeanDefinitionHolder> configCandidates = new ArrayList<BeanDefinitionHolder>();
	String[] candidateNames = registry.getBeanDefinitionNames();
	
	for (String beanName : candidateNames) {
		BeanDefinition beanDef = registry.getBeanDefinition(beanName);
		if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
				ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
			if (logger.isDebugEnabled()) {
				logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
			}
		}
		else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
			configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
		}
	}

	if (configCandidates.isEmpty()) {
		return;
	}

	Collections.sort(configCandidates, new Comparator<BeanDefinitionHolder>() {
		@Override
		public int compare(BeanDefinitionHolder bd1, BeanDefinitionHolder bd2) {
			int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
			int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
			return (i1 < i2) ? -1 : (i1 > i2) ? 1 : 0;
		}
	});

	SingletonBeanRegistry sbr = null;
	if (registry instanceof SingletonBeanRegistry) {
		sbr = (SingletonBeanRegistry) registry;
		if (!this.localBeanNameGeneratorSet && sbr.containsSingleton(CONFIGURATION_BEAN_NAME_GENERATOR)) {
			BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
			this.componentScanBeanNameGenerator = generator;
			this.importBeanNameGenerator = generator;
		}
	}

	ConfigurationClassParser parser = new ConfigurationClassParser(
			this.metadataReaderFactory, this.problemReporter, this.environment,
			this.resourceLoader, this.componentScanBeanNameGenerator, registry);

	Set<BeanDefinitionHolder> candidates = new LinkedHashSet<BeanDefinitionHolder>(configCandidates);
	Set<ConfigurationClass> alreadyParsed = new HashSet<ConfigurationClass>(configCandidates.size());
	do {
		parser.parse(candidates);
		parser.validate();
		
		Set<ConfigurationClass> configClasses = new LinkedHashSet<ConfigurationClass>(parser.getConfigurationClasses());
		configClasses.removeAll(alreadyParsed);
		
		// Read the model and create bean definitions based on its content
		if (this.reader == null) {
			this.reader = new ConfigurationClassBeanDefinitionReader(
					registry, this.sourceExtractor, this.resourceLoader, this.environment,
					this.importBeanNameGenerator, parser.getImportRegistry());
		}
		//对配置的xml中的bean 进行解析
		this.reader.loadBeanDefinitions(configClasses);
		...
	}while(...);
}

  上述过程中代码一大堆,看着都晕,但是从入口中可以看到,先获取所有的beanDefinitionNames,那么我们看看beanDefinitionNames是什么呢?
在这里插入图片描述
  我们发现有beanDefinitionNames有8个,先弄明白这8个值是哪里注册的。
  随便找一个org.springframework.context.annotation.internalConfigurationAnnotationProcessor,通过全局搜索,发现是AnnotationConfigUtils的一个常量CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME。
在这里插入图片描述
  而这个常量在AnnotationConfigUtils类的registerAnnotationConfigProcessors方法用到过。我们在registerAnnotationConfigProcessors方法用到过的地方打一个断点。
在这里插入图片描述
  发现竟然是在createApplicationContext方法中初始化时注册下面的beanDefinition的
org.springframework.context.annotation.internalConfigurationAnnotationProcessor->ConfigurationClassPostProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor->AutowiredAnnotationBeanPostProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor->RequiredAnnotationBeanPostProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor->CommonAnnotationBeanPostProcessor
org.springframework.context.event.internalEventListenerProcessor->EventListenerMethodProcessor
org.springframework.context.event.internalEventListenerFactory->DefaultEventListenerFactory。
  我们进入createApplicationContext()方法看看。

protected ConfigurableApplicationContext createApplicationContext() {
	Class<?> contextClass = this.applicationContextClass;
	if (contextClass == null) {
		try {
			contextClass = Class.forName(this.webEnvironment
					? "org.springframework."
			+ "boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext" : "org.springframework.context."
			+ "annotation.AnnotationConfigApplicationContext");
		}
		catch (ClassNotFoundException ex) {
			throw new IllegalStateException(
					"Unable create a default ApplicationContext, "
							+ "please specify an ApplicationContextClass",
					ex);
		}
	}
	return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
}

  实例化AnnotationConfigEmbeddedWebApplicationContext还是AnnotationConfigApplicationContext取决于webEnvironment,而webEnvironment取决于当前环境中是否有{ “javax.servlet.Servlet”,
“org.springframework.web.context.ConfigurableWebApplicationContext” },如果有,则webEnvironment为true,Spring Boot 项目,当然是有Servlet的,因此最终实例化AnnotationConfigEmbeddedWebApplicationContext时,注册了internalConfigurationAnnotationProcessor… 的beanDefinitions。
  接下来,我们来看springBootStudyApplication的beanDefinition是什么时候注册的。
  先来看项目启动代码。

@SpringBootApplication
public class SpringBootStudyApplication {

    public static void main(String[] args) {
        SpringApplication springApplication = new SpringApplication(SpringBootStudyApplication.class);
        Map<String, Object> defaultProperties = new HashMap<>();
        
        defaultProperties.put("pageNum", 1);
        defaultProperties.put("pageSize", 20);
        springApplication.setDefaultProperties(defaultProperties);

        springApplication.setBannerMode(Banner.Mode.CONSOLE);
        springApplication.run(args);
        //SpringApplication.run(SpringBootStudyApplication.class, args);
    }
}

public SpringApplication(Object... sources) {
	initialize(sources);
}

private void initialize(Object[] sources) {
	if (sources != null && sources.length > 0) {
		this.sources.addAll(Arrays.asList(sources));
	}
	this.webEnvironment = deduceWebEnvironment();
	setInitializers((Collection) getSpringFactoriesInstances(
			ApplicationContextInitializer.class));
	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
	this.mainApplicationClass = deduceMainApplicationClass();
}

  我们知道,此时SpringApplication的sources属性是SpringBootStudyApplication.class,先留在这里。
  我们在AnnotatedBeanDefinitionReader的registerBean方法中打一个断点,发现了SpringBootStudyApplication来注册beanDefintion.
在这里插入图片描述
  根据调用栈,我们找到了load方法,在Spring boot main方法启动时,调用了
在这里插入图片描述
  prepareContext()方法,而在prepareContext()方法中,load了this。因此Spring boot 启动时,自身也被注册到了beanDefinitions中。
  按照同样的方法,我们也能找到org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory的beanDefinition是在哪里注册,并且对应的beanClass是SharedMetadataReaderFactoryBean。
  通过上述分析,我们找到了8个beanDefintion的注册来源,以及对应的BeanClass,如下

org.springframework.context.annotation.internalConfigurationAnnotationProcessor->ConfigurationClassPostProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor->AutowiredAnnotationBeanPostProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor->RequiredAnnotationBeanPostProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor->CommonAnnotationBeanPostProcessor
org.springframework.context.event.internalEventListenerProcessor->EventListenerMethodProcessor
org.springframework.context.event.internalEventListenerFactory->DefaultEventListenerFactory
SpringBootStudyApplication->SpringBootStudyApplication.class
org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory->SharedMetadataReaderFactoryBean.class
  得到了上述结论以后,我们再回头看processConfigBeanDefinitions方法,其中有一个checkConfigurationClassCandidate过滤方法很重要,下面我们来看看checkConfigurationClassCandidate方法的实现。

public static boolean checkConfigurationClassCandidate(BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
	String className = beanDef.getBeanClassName();
	if (className == null || beanDef.getFactoryMethodName() != null) {
		return false;
	}

	AnnotationMetadata metadata;
	if (beanDef instanceof AnnotatedBeanDefinition &&
			className.equals(((AnnotatedBeanDefinition) beanDef).getMetadata().getClassName())) {
		metadata = ((AnnotatedBeanDefinition) beanDef).getMetadata();
	}
	else if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass()) {
		Class<?> beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass();
		metadata = new StandardAnnotationMetadata(beanClass, true);
	}
	else {
		try {
			MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className);
			metadata = metadataReader.getAnnotationMetadata();
		}
		catch (IOException ex) {
			if (logger.isDebugEnabled()) {
				logger.debug("Could not find class file for introspecting configuration annotations: " + className, ex);
			}
			return false;
		}
	}
	//如果beanClass的注解元数据或祖先注解元数据配置了Configuration注解
	if (isFullConfigurationCandidate(metadata)) {
		beanDef.setAttribute("org.springframework.context.annotation.ConfigurationClassPostProcessor.configurationClass", full);
	}
	//如果beanClass的注解元数据或祖先注解元数据有Component,ComponentScan,Import,ImportResource,或者Bean注解
	else if (isLiteConfigurationCandidate(metadata)) {
		beanDef.setAttribute("beanDef.setAttribute("org.springframework.context.annotation.ConfigurationClassPostProcessor.configurationClass", "lite");
	}
	else {
		return false;
	}

	Map<String, Object> orderAttributes = metadata.getAnnotationAttributes(Order.class.getName());
	if (orderAttributes != null) {
		beanDef.setAttribute(ORDER_ATTRIBUTE, orderAttributes.get(AnnotationUtils.VALUE));
	}

	return true;
}

  从上述方法中得知,除了SpringBootStudyApplication类上配置了SpringBootApplication注解,而SpringBootApplication上又配置了SpringBootConfiguration注解,SpringBootConfiguration注解上又配置了Configuration注解。

@SpringBootApplication
public class SpringBootStudyApplication {

}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
		@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {

}

  而其他的BeanDefinition对应的BeanClass上,边注解都没有一个,那肯定经过checkConfigurationClassCandidate方法后,candidates就只剩SpringBootStudyApplication了。
  我们分析了那么多,现在我们进入parse方法内部看看,看内部做了哪些事情。

public void parse(Set<BeanDefinitionHolder> configCandidates) {
	this.deferredImportSelectors = new LinkedList<DeferredImportSelectorHolder>();

	for (BeanDefinitionHolder holder : configCandidates) {
		BeanDefinition bd = holder.getBeanDefinition();
		try {
			//Spring boot 启动类肯定是实现了AnnotatedBeanDefinition接口
			if (bd instanceof AnnotatedBeanDefinition) {
				parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
			}
			else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
				parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
			}
			else {
				parse(bd.getBeanClassName(), holder.getBeanName());
			}
		}
		catch (BeanDefinitionStoreException ex) {
			throw ex;
		}
		catch (Throwable ex) {
			throw new BeanDefinitionStoreException(
					"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
		}
	}
	//处理延迟导入的bean,后面的源码再来分析
	processDeferredImportSelectors();
}

  因为SpringBootStudyApplication是通过
在这里插入图片描述

  AnnotatedGenericBeanDefinition注册的beanDefinition,而AnnotatedGenericBeanDefinition的类结构如下。
在这里插入图片描述
  因此上面代码肯定是走parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());这一行,我们进入parse方法。

protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
	processConfigurationClass(new ConfigurationClass(metadata, beanName));
}

protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
	//主要对配置了Conditional注解的bean是否跳过加载的判断
	if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
		return;
	}
	ConfigurationClass existingClass = this.configurationClasses.get(configClass);
	//假如己经存在了ConfigurationClass
	if (existingClass != null) {
		if (configClass.isImported()) {
			if (existingClass.isImported()) {
				existingClass.mergeImportedBy(configClass);
			}
			return;
		}
		else {
			//否则发现显式 bean 定义,可能替换导入。 让我们移除旧的并使用新的。
			this.configurationClasses.remove(configClass);
			for (Iterator it = this.knownSuperclasses.values().iterator(); it.hasNext();) {
				if (configClass.equals(it.next())) {
					it.remove();
				}
			}
		}
	}
	// 递归处理配置类及其超类层次结构。 
	SourceClass sourceClass = asSourceClass(configClass);
	do {
		sourceClass = doProcessConfigurationClass(configClass, sourceClass);
	}
	//如果@Configuration注解所在类有父类,则此时要处理父类中的注解信息
	while (sourceClass != null);
	this.configurationClasses.put(configClass, configClass);
}

  上述过程中,可能看起来有点晕,不过一定要弄清楚,configClass是下面代码的ImportByB,而importedBy属性值才是ImportByC

@Configuration
protected static class ImportByB {

}
@Configuration
@Import(ImportByB.class)
protected static class ImportByC {

}

  这里我们需要注意的一点是,configurationClasses是一个LinkedHashMap对象,而Spring初始化bean时,越早被加入到configurationClasses集合中的对象,越先被初始化,当上面existingClass.isImported()代码,existingClass被其他的bean Import后,此时,只能mergeImportedBy操作,而不能移除configClass或者调整configClass在configurationClasses位置。接下来,我们来看doProcessConfigurationClass方法,递归调用配置类及超类的层次结构 。当调用完配置类及超类的结构后,那就将当前类加入到configurationClasses集合中,也就是说父类及配置类配置的bean要先比当前类先实例化。

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
		throws IOException {

	// 首先递归处理任何成员(嵌套)类
	processMemberClasses(configClass, sourceClass);

	//PropertySource注解处理 
	for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
			sourceClass.getMetadata(), PropertySources.class,
			org.springframework.context.annotation.PropertySource.class)) {
		if (this.environment instanceof ConfigurableEnvironment) {
			//PropertySource注解处理 
			processPropertySource(propertySource);
		}
		else {
			logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
					"]. Reason: Environment must implement ConfigurableEnvironment");
		}
	}

	//ComponentScans注解处理
	Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
			sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
	if (!componentScans.isEmpty() &&
			!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
		for (AnnotationAttributes componentScan : componentScans) {
			//根据ComponentScan注解配置的路径扫描路径下的所有class文件
			Set<BeanDefinitionHolder> scannedBeanDefinitions =
					this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
			for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
				if (ConfigurationClassUtils.checkConfigurationClassCandidate(
						holder.getBeanDefinition(), this.metadataReaderFactory)) {
					parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());
				}
			}
		}
	}

	//对Import注解处理
	processImports(configClass, sourceClass, getImports(sourceClass), true);
	//对ImportResource注解处理
	if (sourceClass.getMetadata().isAnnotated(ImportResource.class.getName())) {
		AnnotationAttributes importResource =
				AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
		String[] resources = importResource.getStringArray("locations");
		Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
		for (String resource : resources) {
			//对resouce 的路径中的${xxx}变量的处理
			String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
			configClass.addImportedResource(resolvedResource, readerClass);
		}
	}

	//对方法中配置@Bean注解的处理
	Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
	for (MethodMetadata methodMetadata : beanMethods) {
		configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
	}

	//处理接口中的默认方法
	processInterfaces(configClass, sourceClass);

	//处理父类
	if (sourceClass.getMetadata().hasSuperClass()) {
		String superclass = sourceClass.getMetadata().getSuperClassName();
		if (!superclass.startsWith("java") && !this.knownSuperclasses.containsKey(superclass)) {
			this.knownSuperclasses.put(superclass, configClass);
			//返回父类
			return sourceClass.getSuperClass();
		}
	}
	return null;
}

  首先对类成员信息处理,这个看上去是那样的亲切,那这一行代码的意思是什么呢?先看代码,再来看示例。

private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
	//如果成员类中有配置了Configuration或Component,或ComponentScan或ImportResource注解,并且不是自身
	for (SourceClass memberClass : sourceClass.getMemberClasses()) {
		if (ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata()) &&
				!memberClass.getMetadata().getClassName().equals(configClass.getMetadata().getClassName())) {
			if (this.importStack.contains(configClass)) {
				this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
			}
			else {
				this.importStack.push(configClass);
				try {
					//递归处理配置类及其超类层次结构。 
					processConfigurationClass(memberClass.asConfigClass(configClass));
				}
				finally {
					this.importStack.pop();
				}
			}
		}
	}
}


public Collection<SourceClass> getMemberClasses() throws IOException {
	Object sourceToProcess = this.source;
	if (sourceToProcess instanceof Class) {
		Class<?> sourceClass = (Class<?>) sourceToProcess;
		try {
			//使用反射来获取类成员信息
			Class<?>[] declaredClasses = sourceClass.getDeclaredClasses();
			List<SourceClass> members = new ArrayList<SourceClass>(declaredClasses.length);
			for (Class<?> declaredClass : declaredClasses) {
				members.add(asSourceClass(declaredClass));
			}
			return members;
		}
		catch (NoClassDefFoundError err) {
			//getDeclaredClasses() 由于不可解析的依赖关系而失败 -> 回退到下面的 ASM
			//如果反射失败,则使用ASM来解析
			sourceToProcess = metadataReaderFactory.getMetadataReader(sourceClass.getName());
		}
	}

	// 基于 ASM 的解析 - 对于不可解析的类也是安全的
	MetadataReader sourceReader = (MetadataReader) sourceToProcess;
	String[] memberClassNames = sourceReader.getClassMetadata().getMemberClassNames();
	List<SourceClass> members = new ArrayList<SourceClass>(memberClassNames.length);
	for (String memberClassName : memberClassNames) {
		try {
			members.add(asSourceClass(memberClassName));
		}
		catch (IOException ex) {
			// 如果它不可解析,让我们跳过它 - 我们只是在寻找候选人
			if (logger.isDebugEnabled()) {
				logger.debug("Failed to resolve member class [" + memberClassName +
						"] - not considering it as a configuration class candidate");
			}
		}
	}
	return members;
}

  我们来看个例子。ImportByA有内部类ImportByB和ImportByC

@Configuration
public class ImportByA {

    @Configuration
    protected static class ImportByB {

        @Bean
        public ImportByBB importByBB(){
            return new ImportByBB();
        }

        protected static class ImportByBB {

        }
    }

    @Configuration
    @Import(ImportByB.class)
    protected static class ImportByC {

    }
}

  测试结果
在这里插入图片描述
  从上图中可以看出,类成员信息中,我们可以得到
com.example.springbootstudy.service.ImportByA$ImportByC
com.example.springbootstudy.service.ImportByA$ImportByB,再由isConfigurationCandidate方法,判断成员类是否有Configuration或Component,或ComponentScan或Import或ImportResource注解,如果有,则调用 processConfigurationClass 递归初始化成员类及配置类。

PropertySource注解

  接下来,第二步,我们先看PropertySource注解的使用,再来看源码。

  1. 创建配置类
@Component
@PropertySource({"classpath:config/bean1.properties","classpath:config/bean.properties"})
public class PropertySourceA {

}
  1. 创建配置文件bean.properties和bean1.properties
    bean.properties文件中配置bean.message=11111111111
    bean1.properties配置文件中配置bean.message=2222222222222

  2. 开始测试

在这里插入图片描述
  当bean.properties和bean1.properties配置相同的文件内容,bean1.properties会覆盖bean.properties文件内容。
  为什么返回的是bean.properties中配置的内容呢?修改PropertySource注解上文件名的位置,将bean1.properties文件放到前面,bean.properties文件放到后面。
在这里插入图片描述
  最后发现,bean.properties配置覆盖了bean1.properties配置内容,所以从上面的测试中得到,PropertySource注解中配置的数组属性文件,数组后面的文件内容会覆盖掉数组前面的内容。既然看了测试用例,接下来,我们来看看源码如何实现。

private void processPropertySource(AnnotationAttributes propertySource) throws IOException {
	String name = propertySource.getString("name");
	if (!StringUtils.hasLength(name)) {
		name = null;
	}
	String encoding = propertySource.getString("encoding");
	if (!StringUtils.hasLength(encoding)) {
		encoding = null;
	}
	String[] locations = propertySource.getStringArray("value");
	Assert.isTrue(locations.length > 0, "At least one @PropertySource(value) location is required");
	boolean ignoreResourceNotFound = propertySource.getBoolean("ignoreResourceNotFound");

	Class<? extends PropertySourceFactory> factoryClass = propertySource.getClass("factory");
	PropertySourceFactory factory = (factoryClass == PropertySourceFactory.class ?
			new DefaultPropertySourceFactory() : BeanUtils.instantiateClass(factoryClass));

	for (String location : locations) {
		try {
			String resolvedLocation = this.environment.resolveRequiredPlaceholders(location);
			Resource resource = this.resourceLoader.getResource(resolvedLocation);
			addPropertySource(factory.createPropertySource(name, new EncodedResource(resource, encoding)));
		}
		catch (IllegalArgumentException ex) {
			if (ignoreResourceNotFound) {
				if (logger.isInfoEnabled()) {
					logger.info("Properties location [" + location + "] not resolvable: " + ex.getMessage());
				}
			}
			else {
				throw ex;
			}
		}
		catch (IOException ex) {
			if (ignoreResourceNotFound &&
					(ex instanceof FileNotFoundException || ex instanceof UnknownHostException)) {
				if (logger.isInfoEnabled()) {
					logger.info("Properties location [" + location + "] not resolvable: " + ex.getMessage());
				}
			}
			else {
				throw ex;
			}
		}
	}
}

  对于上述方法,有一个就是ignoreResourceNotFound属性,如果为true,即使文件不存在,也不会抛出异常,否则抛出异常,上述方法原理很简单,就是遍历所有的locations文件,然后将其加入到环境变量中去,而加入环境变量位置就有讲究了,请看下面代码。

private void addPropertySource(PropertySource<?> propertySource) {
	String name = propertySource.getName();
	MutablePropertySources propertySources = ((ConfigurableEnvironment) this.environment).getPropertySources();
	if (propertySources.contains(name) && this.propertySourceNames.contains(name)) {
		//如果propertySources存在名字相同的resouce,那就将其封装成CompositePropertySource对象,并将新的resource放在CompositePropertySource的propertySources属性的第一个位置
		PropertySource<?> existing = propertySources.get(name);
		PropertySource<?> newSource = (propertySource instanceof ResourcePropertySource ?
				((ResourcePropertySource) propertySource).withResourceName() : propertySource);
		if (existing instanceof CompositePropertySource) {
			((CompositePropertySource) existing).addFirstPropertySource(newSource);
		}
		else {
			if (existing instanceof ResourcePropertySource) {
				existing = ((ResourcePropertySource) existing).withResourceName();
			}
			CompositePropertySource composite = new CompositePropertySource(name);
			composite.addPropertySource(newSource);
			composite.addPropertySource(existing);
			propertySources.replace(name, composite);
		}
	}
	else {
		if (this.propertySourceNames.isEmpty()) {
			propertySources.addLast(propertySource);
		}
		else {
			// 下面就是加入当前propertySource到propertySources的指定位置
			String firstProcessed = this.propertySourceNames.get(this.propertySourceNames.size() - 1);
			propertySources.addBefore(firstProcessed, propertySource);
		}
	}
	this.propertySourceNames.add(name);
}

  上面的将当前propertySource加入到propertySources的指定位置有什么用呢?在之前的博客中,Spring boot 启动时设置环境变量也说到过。Spring 在getProperty方法时,先遍历propertySources中的每一个propertySource,再从propertySource去获取property的值,只要找到了就返回,因此如果同一个变量配置在不同的propertySource中,越在propertySources集合前面的propertySource,优先被找到。

  1. 创建测试类
@Component
@PropertySource(value = {"classpath:config/bean2.properties","classpath:config/bean1.properties","classpath:config/bean.properties"},ignoreResourceNotFound =true )
public class PropertySourceA {
}

【测试结果】
在这里插入图片描述
  从上面来看配置在PropertySource注解中的value数组属性,越在数组后面的配置文件,在propertySources的位置越靠前,因此,相同的属性bean.message,最终取到的是bean.properties中配置的内容。
  关于PropertySource注解的使用,及实现原理,我相信此时大家有了深刻的理解了,下面再来看ComponentScan注解的使用。

ComponentScan注解

  关于ComponentScan注解我们经常使用,SpringBootApplication注解上也有ComponentScan注解。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
		@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
... 省略
}

  从SpringBootApplication注解上配置的ComponentScan注解来看,其中我们需要注意的是配置了excludeFilters属性里的AutoConfigurationExcludeFilter过虑器。最终解析得到componentScan的AnnotationAttributes的内容如下图所示。
在这里插入图片描述
  我们讲了这么多,至始至终都没有讲到AutoConfigureBefore,和AutoConfigureAfter相关的内容,但是不急,我们继续看,相信会看到你想关心的内容。在ComponentScan注解处理,比较重要的是下面这一行代码,
  Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
  根据componentScan配置的扫描路径,获取所有的beanDefinition,我们进入parse方法看看。

public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
	Assert.state(this.environment != null, "Environment must not be null");
	Assert.state(this.resourceLoader != null, "ResourceLoader must not be null");

	ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
			componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);

	Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");
	boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass);
	scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator :
			BeanUtils.instantiateClass(generatorClass));

	ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy");
	if (scopedProxyMode != ScopedProxyMode.DEFAULT) {
		scanner.setScopedProxyMode(scopedProxyMode);
	}
	else {
		Class<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver");
		scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass));
	}

	scanner.setResourcePattern(componentScan.getString("resourcePattern"));

	for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) {
		for (TypeFilter typeFilter : typeFiltersFor(filter)) {
			scanner.addIncludeFilter(typeFilter);
		}
	}
	for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {
		for (TypeFilter typeFilter : typeFiltersFor(filter)) {
			scanner.addExcludeFilter(typeFilter);
		}
	}

	boolean lazyInit = componentScan.getBoolean("lazyInit");
	if (lazyInit) {
		scanner.getBeanDefinitionDefaults().setLazyInit(true);
	}

	Set<String> basePackages = new LinkedHashSet<String>();
	String[] basePackagesArray = componentScan.getStringArray("basePackages");
	for (String pkg : basePackagesArray) {
		String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
				",; \t\n");
		basePackages.addAll(Arrays.asList(tokenized));
	}
	for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
		basePackages.add(ClassUtils.getPackageName(clazz));
	}
	if (basePackages.isEmpty()) {
		basePackages.add(ClassUtils.getPackageName(declaringClass));
	}

	scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
		@Override
		protected boolean matchClassName(String className) {
			return declaringClass.equals(className);
		}
	});
	return scanner.doScan(StringUtils.toStringArray(basePackages));
}

  上述代码的实现也没有过多好说明的,就是从注解中取出属性,然后设置到scanner对象中,需要注意的时,此时将SpringBootApplication配置的ComponentScan注解上的excludeFilters属性TypeExcludeFilter和AutoConfigurationExcludeFilter加入到了scanner的excludeFilters属性中。这个结论先留在这里。我们继续看scanner的doScan()方法。

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
	Assert.notEmpty(basePackages, "At least one base package must be specified");
	// 创建一个集合,存入扫描到的Bean 定义的封装类
	Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
	//遍历扫描所给定的包
	for (String basePackage : basePackages) {
		// 类路径的Bean定义扫描 ClassPathBeanDefinitionScanner 主要通过 findCandidateComponents() 方法调用其父类 ClassPathScanningCandidateComponentProvider
		// 来扫描获取给定包及其子包的类
		Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
		// 遍历扫描得到的Bean
		for (BeanDefinition candidate : candidates) {
			// 获取Bean定义类中的@Scope注解的值,即获取Bean的作用域
			ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
			// 为Bean设置注解配置的作用域
			candidate.setScope(scopeMetadata.getScopeName());
			// 设置我们的beanName,为Bean生成名称
			String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
			//设置Bean的自动依赖注入装配属性等
			if (candidate instanceof AbstractBeanDefinition) {
				postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
			}
			//处理jsr250相关的组件,如果扫描到的Bean是Spring的注解的Bean,则处理其通用的注解
			if (candidate instanceof AnnotatedBeanDefinition) {
				AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
			}
			//把我们解析出来的组件bean定义注册到Spring IoC容器中,根据Bean名称检查指定的Bean是否需要在容器注册,或者是否是容器中
			// 有冲突。
			if (checkCandidate(beanName, candidate)) {
				BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
				// 根据注解中的配置的作用域,为Bean的应用的代理模式
				definitionHolder =
						AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
				beanDefinitions.add(definitionHolder);
				//注册到Spring IoC容器中,向容器注册扫描到的Bean
				registerBeanDefinition(definitionHolder, this.registry);
			}
		}
	}
	return beanDefinitions;
}

  上述方法是通过注解注入bean的关键代码,非常重要,我们之前的博客也讲过,但是今天我们只关心findCandidateComponents方法,找到符合条件的BeDefinitions。

public Set<BeanDefinition> findCandidateComponents(String basePackage) {
	Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();
	try {
		String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
				resolveBasePackage(basePackage) + '/' + this.resourcePattern;
		Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
		boolean traceEnabled = logger.isTraceEnabled();
		boolean debugEnabled = logger.isDebugEnabled();
		for (Resource resource : resources) {
			if (traceEnabled) {
				logger.trace("Scanning " + resource);
			}
			if (resource.isReadable()) {
				try {
					// 为指定资源获取元数据读取器,元数据读取器通过汇编(ASM) 读取资源的元信息
					MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
					// 如果扫描的类符合容器配置的过滤规则
					if (isCandidateComponent(metadataReader)) {
						// 通过汇编(ASM) 读取资源字节码中的Bean定义的元信息
						ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
						sbd.setResource(resource);
						sbd.setSource(resource);
						if (isCandidateComponent(sbd)) {
							if (debugEnabled) {
								logger.debug("Identified candidate component class: " + resource);
							}
							candidates.add(sbd);
						}
						else {
							if (debugEnabled) {
								logger.debug("Ignored because not a concrete top-level class: " + resource);
							}
						}
					}
					else {
						if (traceEnabled) {
							logger.trace("Ignored because not matching any filter: " + resource);
						}
					}
				}
				catch (Throwable ex) {
					throw new BeanDefinitionStoreException(
							"Failed to read candidate component class: " + resource, ex);
				}
			}
			else {
				if (traceEnabled) {
					logger.trace("Ignored because not readable: " + resource);
				}
			}
		}
	}
	catch (IOException ex) {
		throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
	}
	return candidates;
}

  上述代码中,本身其他的代码也非常重要,但是今天的主角是isCandidateComponent方法,而这个方法主要判断当前注解是否需创建beanDefinition,接下来,我们来看isCandidateComponent方法的内部实现。

protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
	for (TypeFilter tf : this.excludeFilters) {
		//如果被TypeFilter匹配到,则不创建beanDefinition注册到容器中
		if (tf.match(metadataReader, this.metadataReaderFactory)) {
			return false;
		}
	}
	for (TypeFilter tf : this.includeFilters) {
		if (tf.match(metadataReader, this.metadataReaderFactory)) {
			return isConditionMatch(metadataReader);
		}
	}
	return false;
}

  上面这个方法非常简单,但需要注意的是,excludeFilters内容是什么?对于SpringBootApplication注解而言,不就是TypeExcludeFilter.class和AutoConfigurationExcludeFilter.class嘛。最终调用了Filter的match方法判断当前类是否创建BeanDefinition,因为我们在spring.factories中配置的内容如下,
org.springframework.boot.autoconfigure.EnableAutoConfiguration=
com.example.springbootstudy.service.AutoConfigureAfterB,
com.example.springbootstudy.service.AutoConfigureBeforeB,
com.example.springbootstudy.service.AutoConfigureBeforeA,
com.example.springbootstudy.service.AutoConfigureBeforeC

  因此从名字就猜测EnableAutoConfiguration注解和AutoConfigurationExcludeFilter过滤器有关系。我们进入这个过滤器

public class AutoConfigurationExcludeFilter implements TypeFilter, BeanClassLoaderAware {

	private ClassLoader beanClassLoader;

	private volatile List<String> autoConfigurations;

	@Override
	public void setBeanClassLoader(ClassLoader beanClassLoader) {
		this.beanClassLoader = beanClassLoader;
	}

	@Override
	public boolean match(MetadataReader metadataReader,
			MetadataReaderFactory metadataReaderFactory) throws IOException {
		// 配置了Configuration注解及是
		//spring.factories文件中org.springframework.boot.autoconfigure.EnableAutoConfiguration的value属性值
		return isConfiguration(metadataReader) && isAutoConfiguration(metadataReader);
	}

	private boolean isConfiguration(MetadataReader metadataReader) {
		return metadataReader.getAnnotationMetadata()
				.isAnnotated(Configuration.class.getName());
	}

	private boolean isAutoConfiguration(MetadataReader metadataReader) {
		return getAutoConfigurations()
				.contains(metadataReader.getClassMetadata().getClassName());
	}

	protected List<String> getAutoConfigurations() {
		if (this.autoConfigurations == null) {
			//加载当前classpath下的所有META-INF/spring.factories配置文件,中的
			//org.springframework.boot.autoconfigure.EnableAutoConfiguration=xxx,配置的内容
			this.autoConfigurations = SpringFactoriesLoader.loadFactoryNames(
					EnableAutoConfiguration.class, this.beanClassLoader);
		}
		return this.autoConfigurations;
	}
}

  我们分析了那么多,其实就是围绕SpringBootApplication的ComponentScan注解中的excludeFilters的class来分析,如果被excludeFilters中配置的class的match方法匹配掉,则先不加入到beanDefinition中去,这也是为什么,我们之前测试AutoConfigureAfter和AutoConfigureBefore注解时,将bean上面配置@Service注解时,注解配置无效,而必需配置了Configuration注解才生效,凡是配置在META-INF/spring.factories下,且是org.springframework.boot.autoconfigure.EnableAutoConfiguration配置类,并且配置了@Configuration注解的bean,都不会被SpringBootApplication注解扫描进去,都会延后初始化。下面来看一个现象。
在这里插入图片描述

  从图片来看,证实了我们的结论,事实上加入到configurationClasses集合中的实例的顺序决定了bean的创建顺序,因此,因为在META-INF/spring.factories中配置EnableAutoConfiguration对象,同时也配置了Configuration注解的bean,因为没有在Spring启动时扫描资源的时候,没有及时被加入到configurationClasses集合中,所以就被延后初始化化了,而在bean的加载创建过期,我们粗略的觉得,当所有的beanDefinition初始化完成,Spring会遍历configurationClasses,一个个的创建bean,但是这些都只是粗略的认为,事实是比这个复杂得多,假如B bean 依赖于A bean, 那么在B bean在实例化的时候,可能会先创建A bean。这个时候又涉及到循环依赖问题,循环依赖又包括构造器循环依赖和get set 属性循环依赖,等等,实际上整个过程很复杂,其实之前的博客也对这些情况做了分析,这里就不再赘述。接下来我们要分析是Import注解,看看Import注解

Import注解

  我们在分析Import注解前,看来看一个实例。
在这里插入图片描述
  加上Import注解。
在这里插入图片描述
  从测试效果中可以看出,没有配置@Import({AutoConfigureBeforeA.class, AutoConfigureBeforeC.class})时,AutoConfigureBeforeB 比AutoConfigureBeforeA和AutoConfigureBeforeC先实例化,而配置了@Import({AutoConfigureBeforeA.class, AutoConfigureBeforeC.class})注解后,AutoConfigureBeforeA和AutoConfigureBeforeC比AutoConfigureBeforeB先实例化。通过上面的测试效果,我们来理解源码。

private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
	Set<SourceClass> imports = new LinkedHashSet<SourceClass>();
	Set<SourceClass> visited = new LinkedHashSet<SourceClass>();
	collectImports(sourceClass, imports, visited);
	return imports;
}

private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)
		throws IOException {
	//解析过的注解不再做解析
	if (visited.add(sourceClass)) {
		for (SourceClass annotation : sourceClass.getAnnotations()) {
			String annName = annotation.getMetadata().getClassName();
			if (!annName.startsWith("java") && !annName.equals(Import.class.getName())) {
				collectImports(annotation, imports, visited);
			}
		}
		imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
	}
}

  上面就是获取bean的Import注解的value值,或者递归获取bean上所有非Import注解的上配置了Import注解的value值,这个是什么意思呢?来看个例子

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({AutoConfigureBeforeA.class, AutoConfigureBeforeC.class})
public @interface MyImport {

}

@Configuration
@AutoConfigureBefore({AutoConfigureBeforeA.class, AutoConfigureBeforeC.class})
@MyImport
public class AutoConfigureBeforeB {

    public AutoConfigureBeforeB() {
        System.out.println("BeforeB实例化");
    }
}

  显然AutoConfigureBeforeB并没有配置@Import({AutoConfigureBeforeA.class, AutoConfigureBeforeC.class}),而是MyImport注解中配置了@Import({AutoConfigureBeforeA.class, AutoConfigureBeforeC.class}),测试结果
在这里插入图片描述
  显然在AutoConfigureBeforeB并没有直接配置Import注解,而是MyImport配置了Import注解,从而实现了AutoConfigureBeforeA和AutoConfigureBeforeC比AutoConfigureBeforeB先实例化。换句话来说, bean先找当前Bean中是否配置了Import注解,如果没有配置,则看bean的注解的注解是否配置Import注解,如果还没有,则递归的找注解的注解的注解…是否配置了Import注解,如果有,则取出其值,并返回。
  前面我们只看到了Import标签对实例化顺序的影响,接下来,我们来看Import标签的另外一个特性。

public class ImportA {
}

@Service
@Import(ImportA.class)
public class ImportB {
}

  ImportA 上并没有配置注解,但是在ImportB上有注解@Import(ImportA.class),此时ImportA会被注入到容器中吗?看测试结果
在这里插入图片描述
  从测试结果中,我们看到了Import注解的另外一个特性,至少我们知道了另外一种方式将bean注入到容器中。
  接下来我们来看看源码是如何实现。

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
		Collection<SourceClass> importCandidates, boolean checkForCircularImports) {

	if (importCandidates.isEmpty()) {
		return;
	}

	if (checkForCircularImports && isChainedImportOnStack(configClass)) {
		this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
	}
	else {
		this.importStack.push(configClass);
		try {
			for (SourceClass candidate : importCandidates) {
				if (candidate.isAssignable(ImportSelector.class)) {
					// Candidate class is an ImportSelector -> delegate to it to determine imports
					Class<?> candidateClass = candidate.loadClass();
					ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
					ParserStrategyUtils.invokeAwareMethods(
							selector, this.environment, this.resourceLoader, this.registry);
					if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
						this.deferredImportSelectors.add(
								new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
					}
					else {
						String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
						Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
						processImports(configClass, currentSourceClass, importSourceClasses, false);
					}
				}
				else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
					// Candidate class is an ImportBeanDefinitionRegistrar ->
					// delegate to it to register additional bean definitions
					Class<?> candidateClass = candidate.loadClass();
					ImportBeanDefinitionRegistrar registrar =
							BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
					ParserStrategyUtils.invokeAwareMethods(
							registrar, this.environment, this.resourceLoader, this.registry);
					configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
				}
				else {
					// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
					// process it as an @Configuration class
					this.importStack.registerImport(
							currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
					//递归处理Configuration Class 
					processConfigurationClass(candidate.asConfigClass(configClass));
				}
			}
		}
		catch (BeanDefinitionStoreException ex) {
			throw ex;
		}
		catch (Throwable ex) {
			throw new BeanDefinitionStoreException(
					"Failed to process import candidates for configuration class [" +
					configClass.getMetadata().getClassName() + "]", ex);
		}
		finally {
			this.importStack.pop();
		}
	}
}

  需要注意的一点是,此时candidate是@Import({AutoConfigureBeforeA.class, AutoConfigureBeforeC.class}) 配置的属性AutoConfigureBeforeA和AutoConfigureBeforeC,显然一般的类和ImportSelector及ImportBeanDefinitionRegistrar类无关,因此,还是走processConfigurationClass方法。而processConfigurationClass这个方法是递归调用方法,又会去看AutoConfigureBeforeA和AutoConfigureBeforeB类中是否有成员方法配置了Bean注解,或者配置了@ComponentScan等,如果没有,则直接将AutoConfigureBeforeA和AutoConfigureBeforeB加入到configurationClasses属性中,但可确定的一点是AutoConfigureBeforeA和AutoConfigureBeforeC一定会比AutoConfigureBeforeB先加入到configurationClasses属性中,也就出现了为什么配置了Import注解后,AutoConfigureBefore注解变得无效。接下来,我们来看ImportResource注解

ImportResource注解

  注解和AutoConfigureAfter及AutoConfigureBefore注解没有太大关系,但是遇到了,我们还是来分析一下吧, 在分析源码之前,我们还是先来看一个例子。

  1. config目录下创建配置文件spring_importsource_test.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">


    <bean id="importResourceTestBean" class="com.example.springbootstudy.service.ImportResourceTestBean"></bean>


</beans>
  1. 创建普通类
public class ImportResourceTestBean {
}


  1. 创建配置类
@ImportResource("classpath:config/spring_importsource_test.xml")
@Service
public class ImportResourceA {
}
  1. 创建测试方法
@RequestMapping("importResourceTestBeanTest")
public String importResourceTestBeanTest() {
    ImportResourceTestBean importResourceTestBean = SpringContextUtils.getBean(ImportResourceTestBean.class);
    System.out.println(importResourceTestBean);
    return "Sucess";
}
  1. 测试结果
    在这里插入图片描述

  打印出我们配置文件中配置的bean ImportResourceTestBean ,那源码如何实现的呢?也就是doProcessConfigurationClass方法中的下面一段代码实现

if (sourceClass.getMetadata().isAnnotated(ImportResource.class.getName())) {
	AnnotationAttributes importResource =
			AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
	String[] resources = importResource.getStringArray("locations");
	Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
	for (String resource : resources) {
		String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
		configClass.addImportedResource(resolvedResource, readerClass);
	}
}
public void addImportedResource(String importedResource, Class<? extends BeanDefinitionReader> readerClass) {
	this.importedResources.put(importedResource, readerClass);
}

  对于addImportedResource方法,内部并没有什么复杂的逻辑,只是将ImportResource注解中配置的locations值取出并存储到importedResources属性中,而这个属性什么时候用呢?我们找到
importedResources获取的地方

public Map<String, Class<? extends BeanDefinitionReader>> getImportedResources() {
	return this.importedResources;
}

  通过idea很容易找到getImportedResources方法有且只有loadBeanDefinitionsForConfigurationClass方法调用。接下来,我们来看loadBeanDefinitionsForConfigurationClass方法。

private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass,
		TrackedConditionEvaluator trackedConditionEvaluator) {

	if (trackedConditionEvaluator.shouldSkip(configClass)) {
		String beanName = configClass.getBeanName();
		if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
			this.registry.removeBeanDefinition(beanName);
		}
		this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
		return;
	}

	if (configClass.isImported()) {
		registerBeanDefinitionForImportedConfigurationClass(configClass);
	}
	for (BeanMethod beanMethod : configClass.getBeanMethods()) {
		loadBeanDefinitionsForBeanMethod(beanMethod);
	}
	//解析所有配置的xml文件,注册为beanDefinition
	loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
	loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}

  先不急着看xml解析的实现,带着疑问,我们找loadBeanDefinitionsForConfigurationClass这个方法是在哪里调用呢?发现最终来自于processConfigBeanDefinitions方法在parser.parse(candidates);之后调用了loadBeanDefinitions方法。

public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
	TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
	for (ConfigurationClass configClass : configurationModel) {
		loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
	}
}

  接下来,我们来看看loadBeanDefinitionsFromImportedResources方法的内部实现。

private void loadBeanDefinitionsFromImportedResources(
		Map<String, Class<? extends BeanDefinitionReader>> importedResources) {
	Map<Class<?>, BeanDefinitionReader> readerInstanceCache = new HashMap<Class<?>, BeanDefinitionReader>();

	for (Map.Entry<String, Class<? extends BeanDefinitionReader>> entry : importedResources.entrySet()) {
		String resource = entry.getKey();
		Class<? extends BeanDefinitionReader> readerClass = entry.getValue();

		//获取resource的读取器
		if (BeanDefinitionReader.class == readerClass) {
			if (StringUtils.endsWithIgnoreCase(resource, ".groovy")) {
				//如果文件后缀是groovy,使用GroovyBeanDefinitionReader解析器
				readerClass = GroovyBeanDefinitionReader.class;
			}
			else {
				//使用xml文件解析器
				readerClass = XmlBeanDefinitionReader.class;
			}
		}

		BeanDefinitionReader reader = readerInstanceCache.get(readerClass);
		//使用缓存提高性能
		if (reader == null) {
			try {
				reader = readerClass.getConstructor(BeanDefinitionRegistry.class).newInstance(this.registry);
				
				if (reader instanceof AbstractBeanDefinitionReader) {
					AbstractBeanDefinitionReader abdr = ((AbstractBeanDefinitionReader) reader);
					abdr.setResourceLoader(this.resourceLoader);
					abdr.setEnvironment(this.environment);
				}
				readerInstanceCache.put(readerClass, reader);
			}
			catch (Throwable ex) {
				throw new IllegalStateException(
						"Could not instantiate BeanDefinitionReader class [" + readerClass.getName() + "]");
			}
		}
		//加载xml中配置的bean,解析成beanDefinition并注册到容器中
		reader.loadBeanDefinitions(resource);
	}
}

  对于source的处理也是非常简单的,根据后缀名找到resource的解析器,然后直接读取,只是为了提高性能,可能使用了缓存。而loadBeanDefinitions这个是Spring容器的基础,bean从配置文件中加载,之前很多的博客都是围绕着这个代码来分析,这里也不再赘述。到这里,我们对ImportResource注解的使用及源码实现告一段落。接下来,我们来分析@Bean注解的使用及源码

Bean注解

  关于Bean注解注解的使用,我相信基本上没有人不会使用吧,这里就不举例了。直接看源码

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 {
			// 尝试通过 ASM 读取类文件以获得确定性声明顺序... 
			// 不幸的是,JVM 的标准反射以任意顺序 
			// 返回方法,即使在同一 JVM 上同一应用程序的不同运行之间也是如此。
			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<MethodMetadata>(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) {
			logger.debug("Failed to read class file via ASM for determining @Bean method order", ex);
			// No worries, let's continue with the reflection metadata we started with...
		}
	}
	return beanMethods;
}

  关于StandardAnnotationMetadata的使用,也是非常简单

public static void main(String[] args) throws IOException {
    AnnotationMetadata reflectReader = new StandardAnnotationMetadata(BeanConfig.class);
    System.out.println(reflectReader.getAnnotationTypes());
}

  网上有关StandardAnnotationMetadata的解释是
SimpleAnnotationMetadataReadingVisitor与StandardAnnotationMetadata的主要区别在于,SimpleAnnotationMetadataReadingVisitor是基于asm的实现,StandardAnnotationMetadata是基于反射的实现,那我们在使用时,应该要怎么选呢?

  由于基于反射是要先加类加载到jvm中的,因此我的判断是,如果当前类没有加载到jvm中,就使用SimpleAnnotationMetadataReadingVisitor,如果类已经加载到jvm中了,两者皆可使用。

  事实上,在spring包扫描阶段,读取类上的注解时,使用的都是SimpleAnnotationMetadataReadingVisitor,因为此时类并没有加载到jvm,如果使用StandardAnnotationMetadata读取,就会导致类提前加载。类提前加载有什么问题呢?java类是按需加载的,有的类可能在整个jvm生命周期内都没用到,如果全都加载了,就白白浪费内存了。
【总结】
  本文介绍了 AnnotationMetadata两种实现方案,一种基于 Java 反射,另一种基于 ASM 框架。

  两种实现方案适用于不同场景。StandardAnnotationMetadata 基于 Java 反射,需要加载类文件。而 AnnotationMetadataReadingVisitor基于 ASM 框架无需提前加载类,所以适用于 Spring 应用扫描指定范围内模式注解时使用。
  因此Spring默认是用SimpleAnnotationMetadataReadingVisitor来获取bean的信息的。因此就有bean加载的无序性。那我们来看一个示例。

@Configuration
public class BeanConfig {

    @Bean
    public BeanB beanB(){
        return new BeanB();
    }

    @Bean
    public BeanA beanA(){
        return new BeanA();
    }

}

在这里插入图片描述

@Configuration
public class BeanConfig {

    @Bean
    public BeanA beanA(){
        return new BeanA();
    }
        
    @Bean
    public BeanB beanB(){
        return new BeanB();
    }
}

在这里插入图片描述
  因此,有个时候,我们用Configuration配置bean时,Bean在代码中写的顺序影响到Bean实例化的顺序。目前我也没有找到好的办法解决这个问题。
  接下来,我们继续看如何处理接口中定义的默认方法,并且方法配置了bean。在看源码之前 ,还是先来看一个示例。

public class ProcessInterfaceB {
}

public class ProcessInterfaceC {
}

public interface ProcessInterfaceA {
    @Bean
    default ProcessInterfaceB processInterfaceB() {
        return new ProcessInterfaceB();
    }
}

@Configuration
public interface ProcessInterfaceAA extends ProcessInterfaceA {
    @Bean
    default ProcessInterfaceC processInterfaceC() {
        return new ProcessInterfaceC();
    }
}

@RequestMapping("processInterfaceCTest")
public String processInterfaceCTest() {
    ProcessInterfaceB processInterfaceB = SpringContextUtils.getBean(ProcessInterfaceB.class);
    ProcessInterfaceC processInterfaceC = SpringContextUtils.getBean(ProcessInterfaceC.class);
    System.out.println(processInterfaceB);
    System.out.println(processInterfaceC);
    return "Sucess";
}

  首先ProcessInterfaceB和ProcessInterfaceC是普通方法。ProcessInterfaceA和ProcessInterfaceAA是接口,分别在ProcessInterfaceA和ProcessInterfaceAA接口中创建了默认方法processInterfaceB和processInterfaceC,并且每个方法上都配置了Bean注解。同时在ProcessInterfaceAA类中配置Configuration注解。开始测试
在这里插入图片描述
  容器中并没有注入ProcessInterfaceB和ProcessInterfaceC,是不是我们配置不对呢?我们修改一下,去掉ProcessInterfaceAA上的注解Configuration,添加新类ProcessInterfaceConfiguration并配置Configuration注解,并实现ProcessInterfaceAA接口。

@Configuration
public class ProcessInterfaceConfiguration implements ProcessInterfaceAA {

}

  测试结果
在这里插入图片描述
  我们发现一个神奇的现象,就是ProcessInterfaceA接口中的默认方法ProcessInterfaceB也被实例化。接口中的接口的方法bean方法也被实例化了,聪明的读者肯定会想,Spring又用了递归,真是如此吗?我们来看看源码。

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()) {
                configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
            }
        }
        processInterfaces(configClass, ifc);
    }
}

  从源码上来看,是获取sourceClass类的所有接口,递归调用接口的接口中是否配置了@Bean注解的默认方法,如果有,则加入到BeanDefinition中。接下来看Configuration注解所配置类的父类,又是如何处理呢?

public class SuperClassConfigA {
}
public class SuperClassConfigParent {
    @Bean
    public SuperClassConfigA superClassConfigA(){
        return new SuperClassConfigA();
    }
}
@Configuration
public class SuperClassConfig extends SuperClassConfigParent{

}
@RequestMapping("superClassConfigTest")
public String superClassConfigTest() {
    SuperClassConfigA superClassConfigA = SpringContextUtils.getBean(SuperClassConfigA.class);
    System.out.println(superClassConfigA);
    return "Sucess";
}

  创建了普通类SuperClassConfigA,创建普通类SuperClassConfigParent,但是其内部创建了superClassConfigA()方法,注册SuperClassConfigA的bean,创建SuperClassConfig类继承SuperClassConfigParent,并配置了Configuration注解。源码很简单。

//如果配置的Configuration注解的类有父类
if (sourceClass.getMetadata().hasSuperClass()) {
    String superclass = sourceClass.getMetadata().getSuperClassName();
    if (!superclass.startsWith("java") && !this.knownSuperclasses.containsKey(superclass)) {
    	//避免重复实例化
        this.knownSuperclasses.put(superclass, configClass);
        //返回父类
        return sourceClass.getSuperClass();
    }
}


//如果返回的父类不为空,则再次递归调用父类的doProcessConfigurationClass方法
SourceClass sourceClass = asSourceClass(configClass);
do {
    sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}
while (sourceClass != null);

  上述代码需要注意的一点是knownSuperclasses的使用,因为SuperClassConfigParent可能被其他的类也继承,但是为了保证SuperClassConfigA只被注册一次,因此需要knownSuperclasses来缓存己经被扫描的父类,还是看个例子吧。

@Configuration
public class SuperClassConfig extends SuperClassConfigParent{

}
@Configuration
public class SuperClassConfig1 extends SuperClassConfigParent{

}

  SuperClassConfig和SuperClassConfig1都继承SuperClassConfigParent,而SuperClassConfig只被实例化一次,打个断点看看。

在这里插入图片描述
  我们分析了整个Configuration注解的扫描过程,但是到目前为止,只知道了AutoConfigureBefore和AutoConfigureAfter注解因为配置到META-INF/spring.factories的org.springframework.boot.autoconfigure.EnableAutoConfiguration属性中被延后实例化。AutoConfigureBefore和AutoConfigureAfter注解配置引起的实例化顺序问题,目前还没有看到在哪里实现。但是我们分析明白了一些问题,就是配置@Service,@Component注解,这些注解再配置AutoConfigureBefore无效,AutoConfigureBefore只对@Configuration注解有效。而且Configuration注解配置的bean还必需配置在META-INF/spring.factories的org.springframework.boot.autoconfigure.EnableAutoConfiguration属性中,否则也是无效,那么现在就基于bean配置了Configuration注解,同时bean也配置到org.springframework.boot.autoconfigure.EnableAutoConfiguration属性中来分析。不知道细心的读者有没有注意到processDeferredImportSelectors这个方法。Deferred单词是延迟的意思,是不是在这个方法中呢?我们进入这个方法。

private void processDeferredImportSelectors() {
    List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
    this.deferredImportSelectors = null;
    Collections.sort(deferredImports, DEFERRED_IMPORT_COMPARATOR);
    for (DeferredImportSelectorHolder deferredImport : deferredImports) {
        ConfigurationClass configClass = deferredImport.getConfigurationClass();
        try {
            String[] imports = deferredImport.getImportSelector().selectImports(configClass.getMetadata());
            processImports(configClass, asSourceClass(configClass), asSourceClasses(imports), false);
        }
        catch (BeanDefinitionStoreException ex) {
            throw ex;
        }
        catch (Throwable ex) {
            throw new BeanDefinitionStoreException(
                    "Failed to process import candidates for configuration class [" +
                    configClass.getMetadata().getClassName() + "]", ex);
        }
    }
}

在这里插入图片描述
  从测试结果来看,我们知道了deferredImport.getImportSelector()就是EnableAutoConfigurationImportSelector,并且来源于deferredImportSelectors属性。那EnableAutoConfigurationImportSelector又是在何时注入的呢?为了寻找答案,我们不妨再回头看SpringBootApplication注解。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
        @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

}

  在SpringBootApplication注解中,我们发现了另外一个注解EnableAutoConfiguration,这个注解和我们的目标EnableAutoConfigurationImportSelector很像,那再来看看EnableAutoConfiguration注解。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

}

  在这个注解中,我们看到了EnableAutoConfigurationImportSelector类被Import了,根据之前Import注解的特性,EnableAutoConfigurationImportSelector肯定会被注册到容器中。那何时加入到deferredImportSelectors属性中的呢?我们再回到processImports的源码来看,其中candidate.isAssignable(ImportSelector.class)后面几行代码就是处理ImportSelector及其子类的。如下图所示
在这里插入图片描述
  既然我们知道了deferredImport.getImportSelector()返回的是EnableAutoConfigurationImportSelector对象,那么我们来看看selectImports内部是如何实现

public String[] selectImports(AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) {
        return NO_IMPORTS;
    }
    try {
        AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
                .loadMetadata(this.beanClassLoader);
        AnnotationAttributes attributes = getAttributes(annotationMetadata);
        //加载org.springframework.boot.autoconfigure.EnableAutoConfiguration内容
        List<String> configurations = getCandidateConfigurations(annotationMetadata,
                attributes);
        //去重
        configurations = removeDuplicates(configurations);
        //排序
        configurations = sort(configurations, autoConfigurationMetadata);
        Set<String> exclusions = getExclusions(annotationMetadata, attributes);
        checkExcludedClasses(configurations, exclusions);
        configurations.removeAll(exclusions);
        //过虑掉配置在spring.factories文件中的类,但是当前编译环境中并不存在的类
        configurations = filter(configurations, autoConfigurationMetadata);
        //发送配置类导入的事件消息
        fireAutoConfigurationImportEvents(configurations, exclusions);
        return configurations.toArray(new String[configurations.size()]);
    }
    catch (IOException ex) {
        throw new IllegalStateException(ex);
    }
}

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
		AnnotationAttributes attributes) {
	//加载META-INF/spring.factories下的
	//org.springframework.boot.autoconfigure.EnableAutoConfiguration内容
	List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
			getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
	Assert.notEmpty(configurations,
			"No auto configuration classes found in META-INF/spring.factories. If you "
					+ "are using a custom packaging, make sure that file is correct.");
	return configurations;
}

  我们千辛万苦终于看到了sort方法,这也是AutoConfigureBefore和AutoConfigureAfter注解排序的关键代码。我们进入这个方法看看。

private List<String> sort(List<String> configurations,
        AutoConfigurationMetadata autoConfigurationMetadata) throws IOException {
    configurations = new AutoConfigurationSorter(getMetadataReaderFactory(),
            autoConfigurationMetadata).getInPriorityOrder(configurations);
    return configurations;
}

AutoConfigurationSorter(MetadataReaderFactory metadataReaderFactory,
        AutoConfigurationMetadata autoConfigurationMetadata) {
    Assert.notNull(metadataReaderFactory, "MetadataReaderFactory must not be null");
    this.metadataReaderFactory = metadataReaderFactory;
    this.autoConfigurationMetadata = autoConfigurationMetadata;
}

  上面代码只是对AutoConfigurationSorter做初始化,但最终是调用getInPriorityOrder方法来获取configuration的排序的。

public List<String> getInPriorityOrder(Collection<String> classNames) {
    final AutoConfigurationClasses classes = new AutoConfigurationClasses(
            this.metadataReaderFactory, this.autoConfigurationMetadata, classNames);
    List<String> orderedClassNames = new ArrayList<String>(classNames);
    //按字母顺序排序
    Collections.sort(orderedClassNames);
    //按AutoConfigureOrder注解排序
    Collections.sort(orderedClassNames, new Comparator<String>() {
        @Override
        public int compare(String o1, String o2) {
        	//先根据AutoConfigureOrder排序
            int i1 = classes.get(o1).getOrder();
            int i2 = classes.get(o2).getOrder();
            return (i1 < i2) ? -1 : (i1 > i2) ? 1 : 0;
        }
    });
    //然后尊重@AutoConfigureBefore @AutoConfigureAfter
    orderedClassNames = sortByAnnotation(classes, orderedClassNames);
    return orderedClassNames;
}

  上面有一个重要的类AutoConfigurationClasses,下面我们来看看这个类的结构

private static class AutoConfigurationClasses {

    private final Map<String, AutoConfigurationClass> classes = new HashMap<String, AutoConfigurationClass>();

    AutoConfigurationClasses(MetadataReaderFactory metadataReaderFactory,
            AutoConfigurationMetadata autoConfigurationMetadata,
            Collection<String> classNames) {
        for (String className : classNames) {
            this.classes.put(className, new AutoConfigurationClass(className,
                    metadataReaderFactory, autoConfigurationMetadata));
        }
    }
    public AutoConfigurationClass get(String className) {
        return this.classes.get(className);
    }
  
}

  最终将configurations转化为了AutoConfigurationClass 集合中,并存储到classes集合中,而AutoConfigurationClass这个类源码又是怎样的呢?因为比较重要,所以,我将整个类的源码都贴出来了。

private static class AutoConfigurationClass {

    private final String className;

    private final MetadataReaderFactory metadataReaderFactory;

    private final AutoConfigurationMetadata autoConfigurationMetadata;

    private AnnotationMetadata annotationMetadata;

    private final Set<String> before;

    private final Set<String> after;

    AutoConfigurationClass(String className,
            MetadataReaderFactory metadataReaderFactory,
            AutoConfigurationMetadata autoConfigurationMetadata) {
        this.className = className;
        this.metadataReaderFactory = metadataReaderFactory;
        this.autoConfigurationMetadata = autoConfigurationMetadata;
        //读取AutoConfigureBefore注解
        this.before = readBefore();
        //读取AutoConfigureAfter注解
        this.after = readAfter();
    }

    public Set<String> getBefore() {
        return this.before;
    }

    public Set<String> getAfter() {
        return this.after;
    }

    private int getOrder() {
        if (this.autoConfigurationMetadata.wasProcessed(this.className)) {
            return this.autoConfigurationMetadata.getInteger(this.className,
                    "AutoConfigureOrder", Integer.MAX_VALUE );
        }
        Map<String, Object> attributes = getAnnotationMetadata()
                .getAnnotationAttributes(AutoConfigureOrder.class.getName());
        return (attributes == null ?Integer.MAX_VALUE
                : (Integer) attributes.get("value"));
    }

    private Set<String> readBefore() {
        if (this.autoConfigurationMetadata.wasProcessed(this.className)) {
            return this.autoConfigurationMetadata.getSet(this.className,
                    "AutoConfigureBefore", Collections.<String>emptySet());
        }
        return getAnnotationValue(AutoConfigureBefore.class);
    }

    private Set<String> readAfter() {
        if (this.autoConfigurationMetadata.wasProcessed(this.className)) {
            return this.autoConfigurationMetadata.getSet(this.className,
                    "AutoConfigureAfter", Collections.<String>emptySet());
        }
        return getAnnotationValue(AutoConfigureAfter.class);
    }

    private Set<String> getAnnotationValue(Class<?> annotation) {
        Map<String, Object> attributes = getAnnotationMetadata()
                .getAnnotationAttributes(annotation.getName(), true);
        if (attributes == null) {
            return Collections.emptySet();
        }
        Set<String> value = new LinkedHashSet<String>();
        Collections.addAll(value, (String[]) attributes.get("value"));
        Collections.addAll(value, (String[]) attributes.get("name"));
        return value;
    }

    private AnnotationMetadata getAnnotationMetadata() {
        if (this.annotationMetadata == null) {
            try {
                MetadataReader metadataReader = this.metadataReaderFactory
                        .getMetadataReader(this.className);
                this.annotationMetadata = metadataReader.getAnnotationMetadata();
            }
            catch (IOException ex) {
                throw new IllegalStateException(
                        "Unable to read meta-data for class " + this.className, ex);
            }
        }
        return this.annotationMetadata;
    }
}

  AutoConfigurationClass这个类非常重要,但是内部代码其实不理解,就是读取AutoConfigureOrder,AutoConfigureBefore和AutoConfigureAfter注解,并保存到AutoConfigurationClass的属性中。
  接下来,我们来看是如何通过注解来排序的

private List<String> sortByAnnotation(AutoConfigurationClasses classes,
        List<String> classNames) {
    //侍排序类集合
    List<String> toSort = new ArrayList<String>(classNames);
    //己经排好序集合
    Set<String> sorted = new LinkedHashSet<String>();
    //正在排序的类集合
    Set<String> processing = new LinkedHashSet<String>();
    //只要待排序的类还存在,则循环不会结束
    while (!toSort.isEmpty()) {
    	//开始排序
        doSortByAfterAnnotation(classes, toSort, sorted, processing, null);
    }
    return new ArrayList<String>(sorted);
}

private void doSortByAfterAnnotation(AutoConfigurationClasses classes,
        List<String> toSort, Set<String> sorted, Set<String> processing,
        String current) {
    if (current == null) {
    	//如果current为空,则从侍排序的队列中取出第0个元素
        current = toSort.remove(0);
    }
    processing.add(current);
    for (String after : classes.getClassesRequestedAfter(current)) {
        Assert.state(!processing.contains(after),
                "AutoConfigure cycle detected between " + current + " and " + after);
        if (!sorted.contains(after) && toSort.contains(after)) {
            doSortByAfterAnnotation(classes, toSort, sorted, processing, after);
        }
    }
    processing.remove(current);
    sorted.add(current);
}

public Set<String> getClassesRequestedAfter(String className) {
    Set<String> rtn = new LinkedHashSet<String>();
    rtn.addAll(get(className).getAfter());
    for (Map.Entry<String, AutoConfigurationClass> entry : this.classes
            .entrySet()) {
        if (entry.getValue().getBefore().contains(className)) {
            rtn.add(entry.getKey());
        }
    }
    return rtn;
}

  其实AutoConfigureBefore和AutoConfigureAfter注解的排序就在上面的几个方法里,但是很多人可能第一眼就看晕了,虽然我们知道getAfter()方法就是获取当前类的AutoConfigureAfter注解配置的类,getBefore()方法就是AutoConfigureBefore注解配置的类,我们来看个例子说明一下。

@Configuration
@AutoConfigureAfter({AutoConfigureAfterA.class, AutoConfigureAfterC.class})
public class AutoConfigureAfterB {

    public AutoConfigureAfterB() {
        System.out.println("AutoConfigureAfterB实例化");
    }
}

@Configuration
@AutoConfigureBefore({AutoConfigureBeforeA.class, AutoConfigureBeforeC.class,AutoConfigureAfterB.class})
public class AutoConfigureBeforeB {

    public AutoConfigureBeforeB() {
        System.out.println("AutoConfigureBeforeB实例化");
    }
}

  类的配置如上所示,AutoConfigureBeforeA,AutoConfigureBeforeC,AutoConfigureAfterA,AutoConfigureAfterC都是普通配置@Configuration的类,在调用AutoConfigureAfterB的 getClassesRequestedAfter方法时,调用getAfter()方法,获得AutoConfigureAfterA,AutoConfigureAfterC类,再循环遍历所有的classes中的所有类,看其他类的AutoConfigureBefore注解中是否配置了AutoConfigureAfterB类,显然,我们的AutoConfigureBeforeB类的AutoConfigureBefore注解中配置了AutoConfigureAfterB类,因此获得AutoConfigureAfterB必需在AutoConfigureAfterA,AutoConfigureAfterC,AutoConfigureBeforeB类之后实例化。其实上面的配置下面的配置效果等同

@Configuration
@AutoConfigureAfter({AutoConfigureAfterA.class, AutoConfigureAfterC.class,AutoConfigureBeforeB.class})
public class AutoConfigureAfterB {
    public AutoConfigureAfterB() {
        System.out.println("AutoConfigureAfterB实例化");
    }
}

@Configuration
@AutoConfigureBefore({AutoConfigureBeforeA.class, AutoConfigureBeforeC.class})
public class AutoConfigureBeforeB {
    public AutoConfigureBeforeB() {
        System.out.println("AutoConfigureBeforeB实例化");
    }
}

  上面测试代码的区别,在于将AutoConfigureAfterB配置在AutoConfigureBeforeB的AutoConfigureBefore注解中,还是将AutoConfigureBeforeB配置到AutoConfigureAfterB的AutoConfigureAfter注解中,其实两者的效果一样。再来回顾一下,AutoConfigureAfterB上配置的AutoConfigureAfter类一定比AutoConfigureAfterB先实例化,而AutoConfigureBeforeB配置的AutoConfigureBefore注解类一定比AutoConfigureBeforeB要后实例化。AutoConfigureAfter和AutoConfigureBefore这两个注解的功能非常容易弄混了。大家一定要小心。
  因为越先创建beanDefinition加入到容器中,就越先被实例化,因此当获取比当前类AutoConfigureAfterB先实例化的类
AutoConfigureAfterA和AutoConfigureAfterC后,再去看有没有比AutoConfigureAfterA和AutoConfigureAfterC是否配置了AutoConfigureAfter注解或被其他类配置在AutoConfigureBefore注解,有,则获取比AutoConfigureAfterA要先实例化的类,以此类推,递归调用,从而实现了AutoConfigureBefore和AutoConfigureAfter注解功能。
  可能有人会想,什么时候会出现循环依赖呢?测试用例很简单,我们来看一个例子。

@Configuration
@AutoConfigureAfter(AutoConfigureCycleC.class)
public class AutoConfigureCycleA {
}

@Configuration
@AutoConfigureAfter(AutoConfigureCycleA.class)
public class AutoConfigureCycleB {
}


@Configuration
@AutoConfigureAfter(AutoConfigureCycleB.class)
public class AutoConfigureCycleC {
}

  AutoConfigureCycleA说AutoConfigureCycleC在我前面实例化,AutoConfigureCycleB说AutoConfigureCycleA在我前面实例化,AutoConfigureCycleC又说AutoConfigureCycleB在我前面实例化,明显是一个鸡生蛋,和蛋生鸡的问题,因此,Spring不知道你要什么效果,直接抛出异常好了。
在这里插入图片描述

总结

    AutoConfigureBefore,AutoConfigureAfter注解主要是维护配置类的beanDefinition在容器中保存的顺序,保存在集合前面的BeanDefinition在bean实例化时,就越先被实例化,这也是一个粗略的认为,因为当有Import注解或有依赖时,实例化的顺序就会改变。所以AutoConfigureAfter,AutoConfigureBefore的作用并不是绝对的。

  其他的注解还好,只是AutoConfigureBefore和AutoConfigureAfter注解的源码解析这一块,中途可能穿插了很多的其他的内容,如Spring boot的启动,像PropertySource,ComponentScan,ImportResource注解,扫描到接口,父类时的处理,因为不说明整个过程,那我们得到的知识点也是零碎的,不完整的,所以,就一路分析下来,也可能有些问题,我写得不够深刻,或者有误,如蒙不弃,大家在我的博客下方留言,有问题,我一定会去修正,因为Spring 代码太博大精深了,对于里面的注解,也不可能页面具到,我希望读者通过这篇博客能学习到注解相关的知识,更重要的是,学会分析Spring源码的方法,如果此博客对你有帮助,或有没有帮助,给我一个回馈也好的。我在写另外一篇关于Spring Boot博客,目前还没有写完,可能这篇博客或多或少有Spring Boot 启动,配置相关的影子,但是不急, 有兴趣,等我下一篇博客写好了,再来看看。

参考文章

Spring探秘之组合注解的处理

https://www.jianshu.com/p/0097572f34e8

spring-core:元数据之AnnotationMetadata.md

https://github.com/alex2chen/spring-boot-cloud-note/blob/master/spring-core%EF%BC%9A%E5%85%83%E6%95%B0%E6%8D%AE%E4%B9%8BAnnotationMetadata.md

本文相关源码github地址
https://github.com/quyixiao/spring-boot-study

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值