基本用法
关于@Configuration注解的使用,可以参考 spring的官网说明,这里不详细说明了。
前置概念
在分析@Configuration的源码之前,先了解几个源码中涉及到的处理方式。
什么是配置类
我们知道,被@Configuration修饰的类,充当了配置类的角色,实际上除了@Configuration可以指定配置类外,还有以下几种情况下,注入的类,也可以认为是配置类:
- 当一个类被@Configuration、@Component、@ComponentScan、@Import、@ImportResource修饰时
- 或者类中有@Bean注解修饰的方法时,Spring就认为这个类是一个配置类
ConfigurationClassPostProcessor
@Configuration注解的处理,是通过增强器ConfigurationClassPostProcessor实现的,该Processor是在PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(..)方法中注册进来并执行的。关于该方法的说明,前面的文章有过说明。
看一下该类的声明:
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor和,
PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {
// ......
}
通过该类的定义,可以发现,ConfigurationClassPostProcessor同时实现了BeanDefinitionRegistryPostProcessor和PriorityOrdered接口,这在下面的源码分析中会看到。
full和lite
前面说了除了@Configuration注入的类,是一个配置类外,还有其他两种方式,区别在于:
- @Configuration修饰的配置类,在源码中通过一个属性值full来标识,表示完整的配置类
- 其他两种方式修饰的配置类,在源码中,则是通过属性值为lite来标识,表示为一个精简版的配置类
源码分析
通过一个测试代码,来跟踪源码,了解@Configuration的实现原理
配置类:MyConfig.java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyConfig {
@Bean
public Person person() {
return new Person();
}
}
注入类:Person.java
public class Person {
public Person() {
}
}
测试主类:ApplicationSimpleConfiguration.java
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class ApplicationSimpleConfiguration {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
System.out.println(context.getBean("person"));
}
}
以上代码即为@Configuration的最基本使用,下面来分析源码
前面说过,@Configuration是由ConfigurationClassPostProcessor处理的,而这个Processor是在PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(..)中注册的,具体是在处理BeanDefinitionRegistryPostProcessor的逻辑块里,第一次调用invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);方法时候,会去处理,代码如下(简化处理):
# PostProcessorRegistrationDelegate.java
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
Set<String> processedBeans = new HashSet<>();
if (beanFactory instanceof BeanDefinitionRegistry) {
// ... 省略其他代码
// First, invoke ...
// 在此处找到ConfigurationClassPostProcessor, 并存储到数组中
// 关于ConfigurationClassPostProcessor什么时候存储起来的,下面会详细分析
// [1]
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
// 通过getBean, 实例化了ConfigurationClassPostProcessor到容器中
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
// 完成调用处理逻辑
// 本篇重点说明的地方,对于使用基于@Configuration注解和@bean注入的对象, 此时的processor类型为ConfigurationClassPostprocessor, 且在此处执行
// [2]
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
// ... 省略其他与本篇内容不相关的代码
} else {
// ... 省略其他与本篇内容不相关的代码
}
// ... 省略其他与本篇内容不相关的代码
}
上面贴出的源码中标注 [1] 和 [2] 是需要重点留意的。
先看 [1] 处的这行代码:
String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
这行代码会获取到名称为:org.springframework.context.annotation.internalConfigurationAnnotationProcessor
的增强器。
其实该字符串就是对应了前面说到的ConfigurationClassPostProcessor
,那这里获取到的这个字符串值,是在哪里加入进去的?
这个字符串定义在了抽象类AnnotationConfigUtils
中,具体代码如下:
public abstract class AnnotationConfigUtils {
// ... 省略其他代码
public static final String CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME = "org.springframework.context.annotation.internalConfigurationAnnotationProcessor";
public static final String AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME =
"org.springframework.context.annotation.internalAutowiredAnnotationProcessor";
public static final String COMMON_ANNOTATION_PROCESSOR_BEAN_NAME =
"org.springframework.context.annotation.internalCommonAnnotationProcessor";
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
BeanDefinitionRegistry registry, @Nullable Object source) {
DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
if (beanFactory != null) {
if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {
beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
}
if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {
beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
}
}
Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);
// 注册了一个ConfigurationClassPostProcessor, 支撑@Configuration相关的注解
// 其实就是将ConfigurationClassPostProcessor包装成BeanDefinition, 并注册到容器中
// [3]
if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
}
// 注册了一个AutowiredAnnotationBeanPostProcessor, 用来处理@Autowire,@Value注解的
if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
}
// Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor.
// 这里是支撑JSR-250规范的@Resource、@PostConstruct、@PreDestroy注解的
if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
}
// 这里是支持注解形式的jpa的BeanPostProcessor
if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition();
try {
def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME,
AnnotationConfigUtils.class.getClassLoader()));
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex);
}
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));
}
// 支撑spring-event相关注解的processor,对@EventListener的支撑
if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
}
// 支撑spring-event相关注解的processor,对@EventListener的支撑
if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
}
return beanDefs;
}
}
在上面的源码中,registerAnnotationConfigProcessors
方法中注册了很多PostProcessor,都是和注解相关的处理器,如本文所说的 @Configuration
,以及 @Autowire
、@Value
、@Resource
等注解。通过源码,也可以发现 @Autowire
和 @Resource
的底层实现分别基于 AutowiredAnnotationBeanPostProcessor
和CommonAnnotationBeanPostProcessor
。
这里插入一下:registerAnnotationConfigProcessors(..)
是在AnnotationConfigApplicationContext
的构造函数中调用的,具体链路如下(从上往下的调用顺序):
// 入口测试主类
public class ApplicationSimpleConfiguration {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
System.out.println(context.getBean("person"));
}
}
public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
// this中注册了上面的PostProcessor
this();
register(componentClasses);
refresh();
}
// this()进入
public AnnotationConfigApplicationContext() {
// 正如前面推测的那样, 基于注解方法使用的AnnotatedBeanDefinitionReader
// 基于xml方式的注入, 则是使用XmlBeanDefinitionReader进行处理的
this.reader = new AnnotatedBeanDefinitionReader(this);
// 将默认的过滤器 AnnotationTypeFilter 注册到 includeFilter 集合中,
// 过滤器指定的注解我们关注的主要是 Component, 当然也包括Named等
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
// 还是通过构造函数进入
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
Assert.notNull(environment, "Environment must not be null");
this.registry = registry;
this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
// 在这里注册了注解相关的几个PostProcessor
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
继续来看上面贴出的AnnotationConfigUtils
这个类的源码,注意贴出的源码上面标注的 [3] 处的代码,这就是本篇提到的ConfigurationClassPostProcessor
,在 [3] 中 if 块里,创建了一个 BeanDefinition
,class类型即为ConfigurationClassPostProcessor
,并且会注册到工厂的容器,所以在beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
获取的时候,是可以获取到这个增强器的。
至此,PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(..)
代码中 [1] 处的处理已经说完了。下面聊聊 [2] 处的代码,这里是主要的地方:
// [2]
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
进入调用逻辑,还是当前类中:
# PostProcessorRegistrationDelegate.java
private static void invokeBeanDefinitionRegistryPostProcessors(
Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) {
for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessBeanDefinitionRegistry(registry);
}
}
这里的postProcessors只包含了上文所说的 ConfigurationClassPostProcessor
一个对象,重点看ConfigurationClassPostProcessor
这个类的postProcessBeanDefinitionRegistry(registry)方法:
# ConfigurationClassPostProcessor.java
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
// 标识符, 用来标识防止重复处理, 不重要
int registryId = System.identityHashCode(registry);
if (this.registriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
}
if (this.factoriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanFactory already called on this post-processor against " + registry);
}
this.registriesPostProcessed.add(registryId);
// 在这里处理@configure
processConfigBeanDefinitions(registry);
}
核心就是最后一行代码,继续跟踪:
# ConfigurationClassPostProcessor.java
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
// 用来存储未被处理过的配置类
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
// 该数组这里一共有六个元素, 即前面所说的AnnotationConfigUtils中的5个注解支撑类, 以及配置类MyConfig
String[] candidateNames = registry.getBeanDefinitionNames();
// 从候选对象中, 找出所有的配置类, 如果是配置类, 则加入到配置类候选集合configCandidates中
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
// 如果已经被处理过, 打印一行日志即可, 否则需要检验是不是配置类
if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
//打印日志, 略
}
// !!!判断当前beanDefinition是不是一个配置类, 如果是, 收集起来, 并且设置beanDef的属性值
// check方法下面会单独说明
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
// 如果是一个需要处理的配置类, 加入列表
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
// 配置类候选集合为空, 说明没有找到配置类, 结束即可
if (configCandidates.isEmpty()) {
return;
}
// 如果存在排序, 则按照@Order标注的顺序对配置类进行排序
configCandidates.sort((bd1, bd2) -> {
int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
return Integer.compare(i1, i2);
});
// 这里主要是初始化一些工具类, 环境变量之类的
SingletonBeanRegistry sbr = null;
if (registry instanceof SingletonBeanRegistry) {
sbr = (SingletonBeanRegistry) registry;
if (!this.localBeanNameGeneratorSet) {
// 命名生成器, 不是很重要
BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(
AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);
if (generator != null) {
this.componentScanBeanNameGenerator = generator;
this.importBeanNameGenerator = generator;
}
}
}
if (this.environment == null) {
this.environment = new StandardEnvironment();
}
// 准备解析上面找出的所有的配置类
// 创建ConfigurationClassParser实例, 委托其进行配置类的解析操作
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
// 转换类型, 从ArrayList —> Set, 可以去重
// 这里实际上只有一个配置类, 即自定义的MyConfig类
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
// 解析过的, 就存储到该集合中
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
// 真正开始解析工作了, 可以发现这是一个循环。实际上,这里面包含了循环和递归, 后面会给出一张流程图,防止绕来绕去,绕晕了。
do {
// !!!委托给Parser来解析这些配置类!!!
// 这里主要是把配置类上的注解信息解析封装成ConfigurationClass对象
// 如果是配置类导入(import/componentScan等)的普通类(非配置类), 将会在这里生成beanDefinition并注册
parser.parse(candidates);
// 校验扫描出来的beanDefinitionu是否合法, 这里其实主要是校验
// 1.proxyBeanMethods=true的情况下配置类是否可以重写(非final, 需要生成cglib代理类)
// 2.@Bean修饰的方法是否可以重写(非final,需要生成cglib代理类)
parser.validate();
// 获取所有解析出来的配置类
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
// 在注册BeanDefinition前, 先移除掉configClasses中已经处理过的对象
configClasses.removeAll(alreadyParsed);
// 初始化一个ConfigurationClassBeanDefinitionReader
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
// !!!把封装好的ConfigurationClass对象委托给BeanDefinitionReader处理!!!
// 通过配置类加载注册beanDefinition, 以前的文章有过详细说明, 这里不说了
this.reader.loadBeanDefinitions(configClasses);
// 加入到alreadyParsed集合中, 表示处理完了, 避免重复注册
alreadyParsed.addAll(configClasses);
candidates.clear();
// 处理完ConfigurationClass后, 可能会注册新的配置类, 这里就是收集这些新注册的配置类的
if (registry.getBeanDefinitionCount() > candidateNames.length) {
String[] newCandidateNames = registry.getBeanDefinitionNames();
Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
Set<String> alreadyParsedClasses = new HashSet<>();
for (ConfigurationClass configurationClass : alreadyParsed) {
alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
}
for (String candidateName : newCandidateNames) {
// 只有新注册的beanDefinition才需要处理
if (!oldCandidateNames.contains(candidateName)) {
BeanDefinition bd = registry.getBeanDefinition(candidateName);
// 是否是配置类且未处理过
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
!alreadyParsedClasses.contains(bd.getBeanClassName())) {
candidates.add(new BeanDefinitionHolder(bd, candidateName));
}
}
}
candidateNames = newCandidateNames;
}
}
// 循环, 直到没有新增的配置类为止
while (!candidates.isEmpty());
// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
}
if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
// Clear cache in externally provided MetadataReaderFactory; this is a no-op
// for a shared cache since it'll be cleared by the ApplicationContext.
((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
}
}
上面的代码比较长,详细的说明,都写在了源码中,下面简单总结这个方法:
- 从容器中得到所有的候选类
- 从候选类中, 检测出所有的配置类
- 遍历所有筛选出来的的配置类,解析、校验、注册BeanDefinition到容器中
- 处理在执行过程中可能又引入的新的配置类 到此为止,以上所有的代码可以用一张流程图简单概括一下:
还剩下两个方法需要重点说明,对应图片中红色背景的两个方法。
先看第一个方法 checkConfigurationClassCandidate()
public static boolean checkConfigurationClassCandidate(
BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
String className = beanDef.getBeanClassName();
// [4]
// 如果指定了factoryMethodName, 也不算做配置类,下面会解释
if (className == null || beanDef.getFactoryMethodName() != null) {
return false;
}
// 这个AnnotationMetadata能拿到类上的所有注解的信息就可以了
// 最后beanDef对应类上的注解信息都会存储到这个metadata中
AnnotationMetadata metadata;
// 初始化metadata, 根据不同的BeanDefinition类型, 进入不同的分支
// 这里MyConfig会进入 if 分支
if (beanDef instanceof AnnotatedBeanDefinition &&
className.equals(((AnnotatedBeanDefinition) beanDef).getMetadata().getClassName())) {
// Can reuse the pre-parsed metadata from the given BeanDefinition...
// 复用
metadata = ((AnnotatedBeanDefinition) beanDef).getMetadata();
}
else if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass()) {
Class<?> beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass();
// A.isAssignableFrom(B.class), 即 A是不是B的父类或者相同类型
// 例如Object.class.isAssignableFrom(xxx.getClass())都是true, 因为Object是所有类的父类
// 如果是这4个类的派生子类, 则也不能算作配置类
if (BeanFactoryPostProcessor.class.isAssignableFrom(beanClass) ||
BeanPostProcessor.class.isAssignableFrom(beanClass) ||
AopInfrastructureBean.class.isAssignableFrom(beanClass) ||
EventListenerFactory.class.isAssignableFrom(beanClass)) {
return false;
}
metadata = AnnotationMetadata.introspect(beanClass);
}
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;
}
}
// 获取beanDef对应类上的@Configuration注解的属性:value和proxyBeanMethods
Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());
// 有@Configuration注解且注解的proxyBeanMethods=true(这个是默认值)
// 这里简单说明一下proxyBeanMethods:是@Configuration的属性, 默认true, 表示使用用CGlib代理@Bean修饰的方法, 否则, Spring不会代理配置类内部@Bean修饰的方法所返回的实例
// [5]
// 关于这里if和else的处理区别, 前面已经说明, 这里在简单描述一下:
// full:如果加了@Configuration,那么对应的BeanDefinition为full;
// lite:如果加了@Bean,@Component,@ComponentScan,@Import,@ImportResource这些注解,则为lite;
// 其他则为非配置类
// 这里该属性值的设置, 可以理解成是一个标志位, 每次判断是否为标志类前, 先确保该属性没有值方可, 否则就是处理过的配置类
if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
// 注意这里在beanDefinition中塞入了CONFIGURATION_CLASS_ATTRIBUTE这个属性
// 外面是通过判断这个是否有属性来确定某个beanDefinition是否是配置类
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
}
// 有@Configuration 或者 isConfigurationCandidate(metadata)
else if (config != null || isConfigurationCandidate(metadata)) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
}
else {
return false;
}
// It's a full or lite configuration candidate... Let's determine the order value, if any.
// 获取排序值
Integer order = getOrder(metadata);
if (order != null) {
beanDef.setAttribute(ORDER_ATTRIBUTE, order);
}
return true;
}
上面代码标注了 [4] 和 [5] 两处代码,下面详细分析:
[4] 处需要留意一下factoryMethodName的用法:
这里总结一下Spring中实例化对象的几种方式:
- 直接在xml配置文件中,通过bean标签创建对象;
- 通过注解如@Bean、@Service等创建对象;
- 通过FactoryBean工厂类创建对象
- 通过factory-method属性创建对象 关于后面两种用法,可以参考Spring的官方说明,点击链接地址即可查看Spring通过factory-method实例化对象的方式
[5] 处的proxyBeanMethods放在文末专门举例说明,这里先不写。
简单总结下[5]处的代码:
- 如果被@Configuration修饰, 且属性proxyBeanMethods的值为true, 则设置属性标志为full, 即完整版配置类
- 如果被@Configuration修饰, 或者符合isConfigurationCandidate过滤条件, 则设置属性标志为lite, 即前面说的精简版的配置类
- 都不符合,说明该类不是配置类, 直接结束
在[5]处有一个方法isConfigurationCandidate()用来判断是不是精简版的配置类,来看一下该方法:
abstract class ConfigurationClassUtils {
// 完整版和精简版配置类的属性值
public static final String CONFIGURATION_CLASS_FULL = "full";
public static final String CONFIGURATION_CLASS_LITE = "lite";
// 该集合存储了能够作为精简配置类的注解, 一共有4个
// 也就是说一个类上, 如果添加了以下任意一个注解, 则该类也是个配置类
private static final Set<String> candidateIndicators = new HashSet<>(8);
static {
candidateIndicators.add(Component.class.getName());
candidateIndicators.add(ComponentScan.class.getName());
candidateIndicators.add(Import.class.getName());
candidateIndicators.add(ImportResource.class.getName());
}
public static boolean isConfigurationCandidate(AnnotationMetadata metadata) {
// 如果是个接口或注解, 也不能算作配置类, 直接返回false
if (metadata.isInterface()) {
return false;
}
// 遍历匹配在上面static块中初始化的四个注解, 只要有一个, 则符合配置类要求, 直接返回true
for (String indicator : candidateIndicators) {
if (metadata.isAnnotated(indicator)) {
return true;
}
}
try {
// 如果不包含上面的四个注解, 再验证该类中是否包含@Bean注解修饰的方法,
// 如果包含, 则也是个配置类, 返回true
return metadata.hasAnnotatedMethods(Bean.class.getName());
}
catch (Throwable ex) {
// 打印日志, 略
return false;
}
}
}
至此流程图中的checkConfigurationClassCandidate()方法已经很详细的解释了, 我们也已经找到了所有定义了的配置类,下一步就要进行遍历解析了,对应图中的parse方法。
源码如下:
# ConfigurationClassParser.java
/**
* 解析配置类!!!
* 这里主要是把配置类上的注解信息解析封装成ConfigurationClass对象
* 如果是配置类导入(import/componentScan)的普通类(非配置类), 将会在这里生成beanDefinition并注册
* @param configCandidates 待解析的配置类集合
*/
public void parse(Set<BeanDefinitionHolder> configCandidates) {
// 遍历所有的配置类定义信息
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
// 此时的bd的实际类型为AnnotatedGenericBeanDefinition, 是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);
}
}
// 延迟处理, 本篇不说明
this.deferredImportSelectorHandler.process();
}
上面一共有三个分支, 根据配置类对应的BeanDefinition不同,进入不同的分支,这里实际进入的是第一个if块,继续跟踪:
# ConfigurationClassParser.java
protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
// 最后一个形参类型为:Predicate<String> filter, java8新语法, 表示过滤某些参数,
// 实参为常量DEFAULT_EXCLUSION_FILTER, 指定以java.lang.annotation
// 或者org.springframework.stereotype开头的类需要进行过滤
processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER);
}
再继续跟踪进去:
# ConfigurationClassParser.java
protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
// 这里是判断@Condition那些,看是否需要跳过
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
// 看一下这个配置类是否已经解析过了,configurationClasses是一个LinkedHashMap结构
// 这里相当于是通过配置类的类名去获取配置类的封装信息的
// 解析过
ConfigurationClass existingClass = this.configurationClasses.get(configClass);
if (existingClass != null) {
// 分两种情况
if (configClass.isImported()) {
if (existingClass.isImported()) {
// 如果已经解析过,会做一些处理
existingClass.mergeImportedBy(configClass);
}
return;
}
else {
// Explicit bean definition found, probably replacing an import.
// Let's remove the old one and go with the new one.
this.configurationClasses.remove(configClass);
this.knownSuperclasses.values().removeIf(configClass::equals);
}
}
// 这里把configClass又包装成了一个SourceClass,
// 这个filter的值默认是 DEFAULT_EXCLUSION_FILTER, 意思就是这两个包内的类在解析的时候会被排除
// 在本文前面的Predicate<String>中提到过
SourceClass sourceClass = asSourceClass(configClass, filter);
do {
// 处理配置类的逻辑, 配置类中的信息将会被封装到configClass
// 这里返回sourceClass覆盖上一次的sourceClass, 区别在于新的sourceClass值是旧的sourceClass的父类,
// 这里循环处理到当前配置类的最顶层父类才会结束
sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
} while (sourceClass != null);
// 把处理完成的配置类加入到该Map中
this.configurationClasses.put(configClass, configClass);
}
到这里,先总结一下parse()相关的处理流程,如下图:
再看 doProcessConfigurationClass
这个方法,源码如下:
# ConfigurationClassParser.java
protected final SourceClass doProcessConfigurationClass(
ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
throws IOException {
// [6]
// 注意因为@Configuration注解上实际上也声明了@Component, 所以这里的if是会进去的
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
// 如果配置类被@Component修饰,先处理内部类
processMemberClasses(configClass, sourceClass, filter);
}
// Process any @PropertySource annotations
// 这里是处理配置类上的@PropertySources注解的
// 简单来说就是把properties文件中的内容加载到内存中的Environment中了
// 我们不细看
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
if (this.environment instanceof ConfigurableEnvironment) {
processPropertySource(propertySource);
}
else {
// 打印日志, 略...
}
}
// 这里开始是处理类上的@ComponentScan注解的逻辑
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
// 循环处理每一个注解(可以用@ComponentScans包装多个注解-jdk<java8,或者直接打上多个@ComponentScan注解-jdk>=java8)
for (AnnotationAttributes componentScan : componentScans) {
// 委托给componentScanParser处理,这里处理完之后返回了一批已注册的BeanDefinition
// 这里的parse逻辑其实就是创建了一个扫描器并且进行扫描
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser
.parse(componentScan, sourceClass.getMetadata().getClassName());
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
// 如果扫描出来的类是配置类,需要走一遍解析配置类的逻辑
// 实际上是一个递归
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
// 处理类上的@Import注解
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
// 处理@ImportResource
// 用来导入spring的xml配置文件
AnnotationAttributes importResource =
AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
if (importResource != null) {
String[] resources = importResource.getStringArray("locations");
// 这个值默认是 BeanDefinitionReader.class
Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
for (String resource : resources) {
String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
// 把@ImportResource注解上的信息封装到configClass
configClass.addImportedResource(resolvedResource, readerClass);
}
}
// [7]
// 收集当前类里所有有@bean注解的方法
// 一般通过反射就能拿到@Bean注解修饰的方法, spring为了确定这些方法处理的顺序, 使用了asm字节码技术来获取方法在类中的声明顺序, 有兴趣可以了解一下
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
// 封装成BeanMethod并且也放入configClass
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
// 这里会找到所有接口中被@Bean修饰的非抽象的方法,也封装成BeanMethod放入configClass
// 因为java8之后接口也可以有默认方法(default修饰的方法), 所以这里也进行了处理
processInterfaces(configClass, sourceClass);
// Process superclass, if any
// 如果传入的参数sourceClass, 有父类的话,会返回父类的sourceClass,然后在外层循环中继续调用doProcessConfigurationClass()方法进行解析
// 并且把解析的信息封装到当前这个configClass
if (sourceClass.getMetadata().hasSuperClass()) {
String superclass = sourceClass.getMetadata().getSuperClassName();
if (superclass != null && !superclass.startsWith("java") &&
!this.knownSuperclasses.containsKey(superclass)) {
this.knownSuperclasses.put(superclass, configClass);
// Superclass found, return its annotation metadata and recurse
return sourceClass.getSuperClass();
}
}
// 没有父类了就处理完了
return null;
}
上面的一大段代码,源码中已经给了详细的说明,再总结一下:
- 处理内部的@Component,对应processMemberClasses()方法,下面会单独说明
- 处理@PropertySource
- 处理@ComponentScan
- 处理@Import
- 处理@ImportResource
- 处理@Bean修饰的方法
- 处理java8中接口里的默认实现方法
- 查找传入参数sourceClass的父类,并返回给doProcessConfigurationClass()方法 我们再给出doProcessConfigurationClass()方法的一次循环中的处理流程图:
接下来会说明processMemberClasses()和@Bean,分别对应源码中标注的 [6] 和 [7] 这两处的处理逻辑, 而其他的几个注解的处理,比较简单,以后如果有时间可以再聊聊。
由于@Import等这几个注解,会在处理的过程中,又引入了新的类,所以这个过程会涉及到很多的递归,下面来聊聊processMemberClasses()方法:
# ConfigurationClassParser.java
/**
* Register member (nested) classes that happen to be configuration classes themselves.
* 处理配置类上的@Component注解,实际上就是获取到配置类中的所有内部类,并且解析其中的配置类,即调用processConfigurationClass方法
*/
private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass,
Predicate<String> filter) throws IOException {
// 获取所有的内部类
Collection<SourceClass> memberClasses = sourceClass.getMemberClasses();
if (!memberClasses.isEmpty()) {
List<SourceClass> candidates = new ArrayList<>(memberClasses.size());
// 循环处理每个内部类
for (SourceClass memberClass : memberClasses) {
// 如果内部类也是一个配置类,且内部类与当前类不同
// 则加入待处理的配置类候选集合中
if (ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata()) &&
!memberClass.getMetadata().getClassName().equals(configClass.getMetadata().getClassName())) {
candidates.add(memberClass);
}
}
// 对待处理的配置类排序
OrderComparator.sort(candidates);
for (SourceClass candidate : candidates) {
if (this.importStack.contains(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
this.importStack.push(configClass);
try {
// 把每一个内部类也需要处理一下,这里其实又回到上层了,是一个递归
// 可以说, 处理配置类上的@Component注解,实际上就是获取到配置类中的所有内部类,并且解析其中的配置类,即调用processConfigurationClass方法。
processConfigurationClass(candidate.asConfigClass(configClass), filter);
}
finally {
this.importStack.pop();
}
}
}
}
}
总结上面的代码:
- 获取所有的内部类
- 判断如果也是一个配置类,则加入到配置类候选集合candidates中
- 对候选配置类排序
- 遍历,并调用processConfigurationClass()方法进行解析。该方法就是本篇前面描述过的方法,所以这里其实又是个递归处理。
再来说明[7]处对@Bean修饰方法的收集处理逻辑,代码如下:
# ConfigurationClassParser.java
private Set<MethodMetadata> retrieveBeanMethodMetadata(SourceClass sourceClass) {
// 获取所有被@Bean修饰的方法
AnnotationMetadata original = sourceClass.getMetadata();
Set<MethodMetadata> beanMethods = original.getAnnotatedMethods(Bean.class.getName());
if (beanMethods.size() > 1 && original instanceof StandardAnnotationMetadata) {
// Try reading the class file via ASM for deterministic declaration order...
// Unfortunately, the JVM's standard reflection returns methods in arbitrary
// order, even between different runs of the same application on the same JVM.
// 这里注释是说由于jvm返回的方法列表顺序不能保证,这里尝试使用asm字节码技术拿到方法在类中的声明顺序,以此来为这些被@Bean修饰的方法排序
try {
AnnotationMetadata asm =
this.metadataReaderFactory.getMetadataReader(original.getClassName()).getAnnotationMetadata();
Set<MethodMetadata> asmMethods = asm.getAnnotatedMethods(Bean.class.getName());
// 排序
if (asmMethods.size() >= beanMethods.size()) {
Set<MethodMetadata> selectedMethods = new LinkedHashSet<>(asmMethods.size());
for (MethodMetadata asmMethod : asmMethods) {
for (MethodMetadata beanMethod : beanMethods) {
if (beanMethod.getMethodName().equals(asmMethod.getMethodName())) {
selectedMethods.add(beanMethod);
break;
}
}
}
if (selectedMethods.size() == beanMethods.size()) {
// All reflection-detected methods found in ASM method set -> proceed
beanMethods = selectedMethods;
}
}
}
catch (IOException ex) {
// 抛出异常, 省略...
}
}
return beanMethods;
}
该方法了解即可,知道最终返回的是配置类中被@Bean修饰的方法集合即可。
总结
上文已经详细说明了配置类的收集、以及将其BeanDefinition信息注册到bean工厂的流程,图片如下:
本篇着重介绍了@Configuration
相关的配置类的收集过程,还有关于其动态代理的源码,本篇暂时不讲AOP相关的内容。这里提一下,其入口处是在PostProcessorRegistrationDelegate
类的invokeBeanFactoryPostProcessors()
方法中,具体是在处理BeanDefinitionRegistryPostProcessor
类型的调用处,会调用 enhanceConfigurationClasses(beanFactory)
方法,代码如下:
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
Set<String> processedBeans = new HashSet<>();
if (beanFactory instanceof BeanDefinitionRegistry) {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
BeanDefinitionRegistryPostProcessor registryProcessor = (BeanDefinitionRegistryPostProcessor) postProcessor;
registryProcessor.postProcessBeanDefinitionRegistry(registry);
registryProcessors.add(registryProcessor);
}
else {
regularPostProcessors.add(postProcessor);
}
}
List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
// First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
// Next, invoke the BeanDefinitionRegistryPostProcessors that implement
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
// Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.
boolean reiterate = true;
while (reiterate) {
reiterate = false;
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
reiterate = true;
}
}
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
}
// Now, invoke the postProcessBeanFactory callback of all processors handled so far.
// 这里其实会调用enhanceConfigurationClasses()方法,进行CGLIB的代理处理, 本文不说, 后续到AOP的时候, 再来详谈这块的内容
invoeBeanFactoryPostProcessors(registryProcessors, beanFactory);
invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
}
else {
// ....
}
// .......
}
文末
前面提到@Configuration注解的proxyBeanMethods属性, 这里在前面调试代码的基础上,做一下改动
新增一个对象类:Car.java
public class Car {
}
修改Person的构造函数
public class Person {
public Person(Car car) {
System.out.println("内部:" + car);
}
}
配置类做如下修改:MyConfig.java
@Configuration
public class MyConfig {
@Bean
public Person person() {
// @Bean方法调用另一个@Bean方法
return new Person(car());
}
@Bean
public Car car() {
return new Car();
}
}
测试类:
public class ApplicationSimpleConfiguration {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
System.out.println("car = " + context.getBean("car"));
System.out.println("person = " + context.getBean("person"));
}
}
观察打印结果:可以发现两个car是一致的
但如果修改配置类:删除@Configuration注解或者设置其属性proxyBeanMethods = false
@Configuration
public class MyConfig {
@Bean
public Person person() {
// @Bean方法调用另一个@Bean方法
return new Person(car());
}
@Bean
public Car car() {
return new Car();
}
}
观察结果: