序
使用xml进行spring的配置终究已经不是主流,在springboot流行的当今,基于注解的配置才是主流.既然已经对基于xml文件的容器进行了一定的了解,那么也该对基于注解的启动流程进行学习了
注解的前置工作
基于注解的配置,是不是有点springboot的味道了
@ComponentScan
public class Start {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Start.class);
System.out.println("============"+context.getBean(People.class)+"=============");
}
}
People 类
package com.zk.sixDay;
import org.springframework.stereotype.Service;
/**
* @author zhaokun
* @since 2021/1/14 15:40
*/
@Service
public class People {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "People{" +
"name='" + name + '\'' +
'}';
}
}
结果
注解的源码分析
AnnotationConfigApplicationContext的AnnotationConfigApplicationContext(Class<?>… componentClasses)
public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
//这里实例化了reader和scanner
this();
//这里将传入的class对象注册到BeanDenifition中
register(componentClasses);
//和xml一样,都会进入到refresh方法
refresh();
}
AnnotationConfigApplicationContext的AnnotationConfigApplicationContext()
public AnnotationConfigApplicationContext() {
//实例化reader,在这个过程中,完成了各种内置postprocessor的注册工作
this.reader = new AnnotatedBeanDefinitionReader(this);
//实例化scanner
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
AnnotatedBeanDefinitionReader()的构造方法
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);
//在此注册了一系列的BeanFactoryPostProcessor的实现类,其中包含一个很重要的类
//ConfigurationClassPostProcessor.class
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
至于ClassPathBeanDefinitionScanner类的实例化,在此不再赘述,因为在之前的博客<component-scan标签解析>中有对改类实例化的分析.
此时便会调用refresh()方法.
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
prepareRefresh();
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
prepareBeanFactory(beanFactory);
try {
postProcessBeanFactory(beanFactory);
invokeBeanFactoryPostProcessors(beanFactory);
registerBeanPostProcessors(beanFactory);
initMessageSource();
initApplicationEventMulticaster();
onRefresh();
registerListeners();
finishBeanFactoryInitialization(beanFactory);
finishRefresh();
....
}
xml文件的解析是在执行 obtainFreshBeanFactory() 时完成的.而在基于注解的启动时,该方法不能够型增任何其他的BeanDenition.此时将会执行到 invokeBeanFactoryPostProcessors(beanFactory) 中,而invokeBeanFactoryPostProcessors方法已经在之前的博客中介绍了它的作用,简单说下这里面就是对实现BeanFactoryPostProcessor接口的方法的优先执行,在此debug看下有哪些BeanDenifitions
在这时并没有People类被注册到beanDefinitionMap中
继续debug后,发现people类被注册进去了.因此才有了后面取得了People类,那么这里的People是怎么注册进去的呢,scanner并没有扫描,这时就涉及到一个很重要的类了 ConfigurationClassPostProcessor.class 类
注解的核心处理
ConfigurationClassPostProcessor是极为重要的一个类,概述它的作用就是对被扫描的类上面的注解进行解析,万一解析了其他类也符合条件变成BeanDenition,对于新的BeanDenition的类也会去解析它的注解,以此反复递归下去.最后把有关联的符合条件的类全部加载到容器中
ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
...
//核心方法
processConfigBeanDefinitions(registry);
}
然后看下ConfigurationClassPostProcessor的processConfigBeanDefinitions(BeanDefinitionRegistry registry)方法好长啊0.0!
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
String[] candidateNames = registry.getBeanDefinitionNames();
//获取容器中的BeanDefinition,对他们进行遍历处理
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
//判断下该类有没有被ConfigurationClassPostProcessor类处理过
if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
//这里去判断BeanDenition中是否含有@Configuration注解或者@Bean注解或者@Component,@ComponentScan
//@Import,@ImportResource这些注解,要是包含,就加入到候选处理的集合中
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
// 要是没有需要处理的类,则直接返回出去
if (configCandidates.isEmpty()) {
return;
}
// 对候选处理的类进行排序
configCandidates.sort((bd1, bd2) -> {
int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
return Integer.compare(i1, i2);
});
// Detect any custom bean name generation strategy supplied through the enclosing application context
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 parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
//这是一个循环
do {
//核心处理方法,在这里对@Configuration注解或者@Bean注解或者@Component,@ComponentScan
//@Import,@ImportResource这些注解进行处理的
parser.parse(candidates);
parser.validate();
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(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());
}
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
candidates.clear();
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) {
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();
}
}
因为ConfigurationClassPostProcessor类太复杂,本博客只是大体的说明下该类的功能,对于其中方法的详细解析,会另开一篇博客来分析的.其中的循环递归也会在那篇博客中分析到
结
基于注解的解析,首先在实例化时就将ConfigurationClassPostProcessor这个类注册到了容器中,而这个类又是后置处理器,会在实例化前先执行方法,在这个过程中会去解析已经注册的类的注解,进而去执行注解的功能,其中包含的注解总结如下:
@Configuration : 会被扫描到,并且默认会生成代理
@Bean : 会被扫描到生成工厂方法的bean对象
@Component : 会被扫描到
@ComponentScan : 会扫描包下的带有@Component的类和子类并加入到BeanDefinitions里,在对新扫描进入的类进行递归
@Import : 引入类
@ImportResource : 引入xml文件(或者其他文件)
@PropertyResource:引入properties文件