一 介绍
在spring的ioc中,对于bean的声明常见的有xml文件解析,BeanDefinitionRegistry注入,@Configuration注解的方式,在目前微服务大行其道,spring boot和spring cloud作为主流微服务架构开发的首选的时候,我们更多的是提倡基于注解形式的开发。
并且 spring boot社区推荐使用基于JavaConfig的配置形式来取代基于xml的配置形式
一 源码解读
1,使用
@Test
public void test() {
/**
* 将ConfigurationClassPostProcessor的BeanFactoryPostProcessor注入到容器中,进行解析
*/
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(TestConfiguration.class);
//使用@Bean标签加入的
TestBean testBean = (TestBean) applicationContext.getBean("testBean");
System.out.println("testBean=="+testBean);
//使用@Component注解注入的bean
TestComponent testComponent = (TestComponent)applicationContext.getBean("testComponent");
testComponent.testComponent();
//@Import Selector
Selector selector = (Selector)applicationContext.getBean("com.zcs.configuration.Selector");
selector.print();
//@Import normal class
// NormalImportClass normalImportClass = (NormalImportClass) applicationContext.getBean("com.zcs.configuration.NormalImportClass");
// normalImportClass.print();
}
首先我们使用AnnotationConfigApplicationContext spring的扩展类实例化一个ApplicationContext,AnnotationConfigApplicationContext 接收一个字节码类TestConfiguration,TestConfiguration类的具体内容如下:
/**
* @author: zhoucg
* @date: 2019-05-09
*/
@Configuration
@ComponentScan(basePackages = "com.zcs.configuration")
@ImportResource(value = {"classpath:/spring/spring-context.xml"})
@Import(value = {AutoConfigurationSelector.class,NormalImportClass.class})
public class TestConfiguration {
@Bean
public TestBean testBean(){
return new TestBean();
}
}
这里面定义了一些常见的注解类,@Configuration,@ComponentScan,@ImportResource,@Import,在下面的源码分析中,我们会一一分析这些注解类的作用,同时我们还会在源码中分析关于 @Conditional 等注解的源码解析
话不多说,首先进入到AnnotationConfigApplicationContext 类中大致的看一下提供了哪些公共的方法
主要的方式就是提供了一些无参和含有注解的Class类型的构造器,同时提供了设置环境变量的方法,进入到AnnotationConfigApplicationContext(Class<?>… annotatedClasses) 方法
/**
* Create a new AnnotationConfigApplicationContext, deriving bean definitions
* from the given annotated classes and automatically refreshing the context.
* @param annotatedClasses one or more annotated classes,
* e.g. {@link Configuration @Configuration} classes
*/
public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
//初始化bean的读取器和扫描器
this();
//注册bean配置文件
register(annotatedClasses);
refresh();
}
主要的作用就是首先初始化bean的读取器和扫描器,注册注解类annotatedClasses,执行refresh()方法(这个方法就是执行 AbstractApplicationContext 中的refresh()方法)
/**
* Create a new AnnotationConfigApplicationContext that needs to be populated
* through {@link #register} calls and then manually {@linkplain #refresh refreshed}.
*/
public AnnotationConfigApplicationContext() {
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
实例化AnnotatedBeanDefinitionReader reader 和 ClassPathBeanDefinitionScanner scanner,进入到AnnotatedBeanDefinitionReader 构造函数,最后会进入到AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) 构造函数中
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);
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
然后再进入到AnnotationConfigUtils 类的registerAnnotationConfigProcessors(BeanDefinitionRegistry registry)中,重点来了
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<>(4);
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));
}
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.
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));
}
// Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor.
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));
}
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));
}
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;
}
在上面的源码中,我们实际上会发现,该方法主要是向Spirng ioc容器中注入了这几个类
ConfigurationClassPostProcessor
AutowiredAnnotationBeanPostProcessor
CommonAnnotationBeanPostProcessor
PersistenceAnnotationBeanPostProcessor
EventListenerMethodProcessor
DefaultEventListenerFactory
其中,我们最需要关注的两个类就是 ConfigurationClassPostProcessor , AutowiredAnnotationBeanPostProcessor ,其中 ConfigurationClassPostProcessor的作用就是用于引导处理@Configuration注解的类
源码注释:
{@link BeanFactoryPostProcessor} used for bootstrapping processing of {@link Configuration @Configuration} classes.
2 ConfigurationClassPostProcessor解析
首先ConfigurationClassPostProcessor是一个BeanFactoryPostProcessor ,并且实现了BeanDefinitionRegistryPostProcessor ,它是一个具有向容器注入bean的功能,因此在refresh()中的invokeBeanFactoryPostProcessors(beanFactory);方法中,是会优先执行BeanDefinitionRegistryPostProcessor中的postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)方法,经过前期的处理,最后是会进入到ConfigurationClassPostProcessor类中的 processConfigBeanDefinitions(BeanDefinitionRegistry registry) 方法中
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
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));
}
}
// Return immediately if no @Configuration classes were found
if (configCandidates.isEmpty()) {
return;
}
// Sort by previously determined @Order value, if applicable
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(CONFIGURATION_BEAN_NAME_GENERATOR);
if (generator != null) {
this.componentScanBeanNameGenerator = generator;
this.importBeanNameGenerator = generator;
}
}
}
if (this.environment == null) {
this.environment = new StandardEnvironment();
}
// Parse each @Configuration class
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 {
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
// 读取模型并根据其内容创建bean定义
/**
* 4、遍历configCandidates,使用委托类ConfigurationClassBeanDefinitionReader注册解析好的BeanDefinition
*/
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();
}
}
最终,我们进入到了对于@Configuration核心的解析过程,(其实我们在看spring的源码中,我们会发现spring对于核心方法的实现内部是穿插了大量细节的实现,但是这些细节的实现,spring都是通过委托其他类的形式进行实现,这个大大的提高了spring的扩展性延申性)
在这个方法中,主要是做了几件事,
1,遍历目前的spring容器中,所有已解析的beandefintions,从beandefintions中找到含有@Configuration注解的类加入到configCandidates中,(实际上注解含有@Component,@ComponentScan,@Import,@ImportResource,@Configuration都可以)
2,根据@Order对configCandidates队列进行排序,
3,遍历不为空的configCandidates,然后委托ConfigurationClassParse进行解析配置,包含了@PropertySource注解解析,@ComponentScan注解解析,@Import 注解解析,@Bean注解解析,
4,遍历configCandidates,使用委托类ConfigurationClassBeanDefinitionReader注册解析好的beanDefinitioin
下面,我们会就主要的流程中的主要的问题,进行分析,spring的解析过程
对于上面的第一点,解析过程是委托给了ConfigurationClassUtils 类的checkConfigurationClassCandidate(BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) 方法来遍历当前spring容器中,所有已解析beandefinitions中符合条件的类加入到configCandidates中,
那么,这个时候是有这么一个问题,spring ioc中当前存在哪些已解析的beandefinions呢,
这个问题,其实很好理解,在解析之前,spring其实是只存在之前通过AnnotationConfigUtils.registerAnnotationConfigProcessors(BeanDefinitionRegistry registry)注入的几个配置类,以及通过 this.reader.register(annotatedClasses);注入到ioc容器的TestConfiguration 类
对于符合条件的beandefintions的判断,spring是通过ConfigurationClassUtils的isFullConfigurationCandidate和 isLiteConfigurationCandidate方法进行判断
对于上面的第三点,spring是首先委托ConfigurationClassParser 进行解析,进入到parse(Set configCandidates)方法,最后是会交由doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)进行解析
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException {
// Recursively process any member (nested) classes first
processMemberClasses(configClass, sourceClass);
/**
* https://www.cnblogs.com/whx7762/p/7885735.html
* 通过@PropertySource注解将properties配置文件中的值存储到Spring的Environment中,Environment接口提供方法去读取配置文件的值
*
*/
// 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.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
"]. Reason: Environment must implement ConfigurableEnvironment");
}
}
/**
* @ComponentScan 解析,ComponentScanAnnotationParser解析器
* 首先是解析@Conditional注解(判断该@ComponentScan注解是否继续解析)
*
*/
// 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) {
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
/**
* 继续检测@ComponentScan解析出来的bean是否为ConfigurationClass类型,然后再进行解析
*/
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
/**
* 测试
*/
BeanDefinitionRegistry beanDefinitionRegistry = this.registry;
String[] beanDefinitions = beanDefinitionRegistry.getBeanDefinitionNames();
for(String beanDefinition : beanDefinitions) {
logger.debug("[zhoucg Test] current haven load BeanDefinition:["+beanDefinition+"]");
}
/**
* 解析@Import,
*/
// Process any @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), true);
// Process any @ImportResource annotations
AnnotationAttributes importResource =
AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
if (importResource != null) {
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 != null && !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;
}
在讲解这个之前,spring关于注解**@Conditional的解析是在processConfigurationClass(ConfigurationClass configClass)方法中(这个就先不研究了)
到了这,我们会发现,spring是会优先解析@ComponentScan注解,然后判断注解中含有@Component,@Service,或者自定义的注解加入到ioc中(这一步也是极其繁琐,可以进入到ComponentScanAnnotationParser的parse()方法中研究),然后将解析到的beandefintions再进行parser解析,查看对应的配置类是否同样含有@Configruation,@Import等注解,
然后,执行@Import和@ImportResource注解,这两个解析操作时会涉及到ImportSelector** (这个就是spring boot自动装配的实现原理)的解析
然后是执行@Bean的解析
如果你坚持看到这,也许,你会有这么一个疑问,在ConfigurationClassPostProcessor解析的时候,会认为带@Component注解的类为Configuration的类,进行解析,但是在使用委托类ConfigurationClassParse进行解析的时候并不包含@Component的解析,这是为什么?
答案就是使用@Component注解类时,ioc会认为他是一个组件,当使用基于注释的配置和类路径扫描时,这些类被认为是自动检测的候选类。可以查看{@link org.springframework.stereotype.Component @Component} 代码注解
一 源码地址
spring关于@Configuration注解类的实现,可以说为后来的spring boot和spring cloud的开发提供了很完美的扩展。
源码github地址 | 源码说明 |
---|---|
https://github.com/zcswl7961/spring/blob/master/spring-mytest/src/test/java/com/zcs/config/SpringApplication.java | 基于spring5 |