Spring配置类
天黑请闭眼丶风 人气:0spring配置类解析源码解析
上一篇分析spring的启动过程中,会把BeanDefinitionRegistryPostProcessor接口类型的子类添加到启动的过程中,其中它的一个子类ConfigurationClassPostProcessor
是用来处理配置类。下面来分析spring如何处理配置类。
那么首先要知道什么是配置类?先简单来说常见的配置类就是添加了@Configuration、@ComponentScan等注解的类。后续分析源码的过程中在详细介绍。
配置类解析源码分析
上一篇分析过spring启动的过程会添加一些类用于后续bean的生命周期使用,启动过程中会执行ConfigurationClassPostProcessor
的processConfigBeanDefinitions
方法。展示部分重要的代码
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { List<BeanDefinitionHolder> configCandidates = new ArrayList<>(); //拿到启动过程中注入的类的名称 String[] candidateNames = registry.getBeanDefinitionNames(); for (String beanName : candidateNames) { BeanDefinition beanDef = registry.getBeanDefinition(beanName); if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) { 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 // 通过@Order可以排序,升序排序,order越下越靠前 configCandidates.sort((bd1, bd2) -> { int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition()); int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition()); return Integer.compare(i1, i2); }); // 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()); // 递归解析配置类,有可能通过解析一个配置类,得到了其他的配置类,比如扫描和Import do { StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse"); //配置类解析 parser.parse(candidates); parser.validate(); // 从解析器parse得到配置类,移除已经解析过的,剩下的就是新增的 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()); } // 把parser.parse(candidates);解析到的但是未生成BeanDefinition的配置 生成对应的BeanDefinition this.reader.loadBeanDefinitions(configClasses); alreadyParsed.addAll(configClasses); processConfig.tag("classCount", () -> String.valueOf(configClasses.size())).end(); // candidates中存的是BeanDefinition,configClasses中存的是ConfigurationClass candidates.clear(); //loadBeanDefinitions方法会增加很多BeanDefinition 如果发现BeanDefinition增加了,则有可能增加了配置类 //这个if对新增的BeanDefinition做处理 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()); } //遍历新增的BeanDefinition for (String candidateName : newCandidateNames) { if (!oldCandidateNames.contains(candidateName)) { BeanDefinition bd = registry.getBeanDefinition(candidateName); //判断新增的BeanDefinition是不是一个配置类 if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) && !alreadyParsedClasses.contains(bd.getBeanClassName())) { //如果是配置类 添加到这个集合当中 交给do while 循环解析配置类 candidates.add(new BeanDefinitionHolder(bd, candidateName)); } } } candidateNames = newCandidateNames; } } while (!candidates.isEmpty()); }
整个流程就是 判断哪些类是配置类,根据@order注解排序,构建一个配置类的解析器,利用do while 解析配置类。先来看看spring是如何判断哪些类是配置类的。
判断配置类
判断配置类的方法是ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)
public static boolean checkConfigurationClassCandidate( BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) { String className = beanDef.getBeanClassName(); if (className == null || beanDef.getFactoryMethodName() != null) { return false; } // AnnotationMetadata表示某个类的注解信息,但是并一定要加载这个类 AnnotationMetadata metadata; // 如果AnnotatedBeanDefinition,则直接取AnnotationMetadata if (beanDef instanceof AnnotatedBeanDefinition && className.equals(((AnnotatedBeanDefinition) beanDef).getMetadata().getClassName())) { // Can reuse the pre-parsed metadata from the given BeanDefinition... metadata = ((AnnotatedBeanDefinition) beanDef).getMetadata(); } // 如果是AbstractBeanDefinition,则解析beanClass得到AnnotationMetadata else if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass()) { // Check already loaded Class if present... // since we possibly can't even load the class file for this Class. Class<?> beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass(); 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; } } //如果该类有@Configuration注解 表示是一个配置类 Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName()); // 存在@Configuration,并且proxyBeanMethods不为false(为true或为null)时,就是Full配置类 if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) { beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL); // 存在@Configuration,并且proxyBeanMethods为false时,是lite配置类 // 或者不存在@Configuration,但是只要存在@Component、@ComponentScan、@Import、@ImportResource四个中的一个,就是lite配置类 // 或者不存在@Configuration,只要存在@Bean注解了的方法,就是lite配置类 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; }
首先就是拿到这个类的BeanDefinition的注解,根据注解判断有没有@Configuration,如果有该注解并且其属性proxyBeanMethods=ture,默认也是true,那么就是full配置类,如果为fasle那么就是lite配置类。这两种配置类跟代理有关可以先不用管。同时还有个或的判断isConfigurationCandidate(metadata)
,如果满足这个条件那么也是lite配置类。
public static boolean isConfigurationCandidate(AnnotationMetadata metadata) { //如果是一个接口类型,那么直接返回false 就不是一个配置类 if (metadata.isInterface()) { return false; } // 只要存在@Component、@ComponentScan、@Import、@ImportResource四个中的一个,就是lite配置类 for (String indicator : candidateIndicators) { if (metadata.isAnnotated(indicator)) { return true; } } // 只要存在@Bean注解了的方法,就是lite配置类 return hasBeanMethods(metadata); } static { candidateIndicators.add(Component.class.getName()); candidateIndicators.add(ComponentScan.class.getName()); candidateIndicators.add(Import.class.getName()); candidateIndicators.add(ImportResource.class.getName()); }
其中candidateIndicators是一个集合,包含了@Component、@ComponentScan、@Import、@ImportResource这几个注解。
因此根据上述代码可以知道什么是配置类
类上有@Component、@ComponentScan、@Import、@ImportResource、@Configuration任意一个注解的类,或者包含有@bean注解的类都是配置类。
解析配置类
拿到了配置类,排序后,构建一个配置类的解析器就开始解析。开头讲的do while 就是用来解析配置类的。核心代码在于parser.parse(candidates);
,这个方法中核心处理在于processConfigurationClass这个方法。
protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException { // 条件注解判断 if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) { return; } ConfigurationClass existingClass = this.configurationClasses.get(configClass); if (existingClass != null) { if (configClass.isImported()) { // OrderService导入了AccountService,UserService也导入了AccountService,就会符合这个条件 if (existingClass.isImported()) { existingClass.mergeImportedBy(configClass); } // Otherwise ignore new imported config class; existing non-imported class overrides it. 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); } } // Recursively process the configuration class and its superclass hierarchy. SourceClass sourceClass = asSourceClass(configClass, filter); //循环 解析了该类后 解析父类 do { //核心方法 真正解析类 --> 解析类上的各种配置注解并做出对应处理 sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter); } while (sourceClass != null); // ConfigurationClass重写了equals方法,只要两个ConfigurationClass对应的className相等就可以 this.configurationClasses.put(configClass, configClass); }
其中核心代码在于doProcessConfigurationClass(configClass, sourceClass, filter)
开始解析配置类,这里也有个do while ,这个do while 就是循环解决配置类的父类,如果配置类存在父类,那么就会执行解析的逻辑,如果没有就会推出循环。那么接着看如何解析配置类。
protected final SourceClass doProcessConfigurationClass( ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter) throws IOException { //对@Component注解做处理 if (configClass.getMetadata().isAnnotated(Component.class.getName())) { // 处理内部类 processMemberClasses(configClass, sourceClass, filter); } //对@PropertySource注解做处理 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注解做处理 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 // 底层调用doScan 进行扫描所有的bean得到BeanDefinition 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(); } // 检查扫描出来的BeanDefinition是不是配置类(full和lite) if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) { //如果是配置类递归执行 解析配置类的方法 parse(bdCand.getBeanClassName(), holder.getBeanName()); } } } } // getImports(sourceClass)会拿到所有导入的类 //对@Import注解做处理 processImports(configClass, sourceClass, getImports(sourceClass), filter, true); //对@ImportResource注解做处理 @ImportResource导入一个xml作为配置文件 这里暂时没有解析xml文件 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 // 解析配置类中的@Bean,但并没有真正处理@Bean,只是暂时找出来 Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass); for (MethodMetadata methodMetadata : beanMethods) { configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass)); } // Process default methods on interfaces // 解析配置类所实现的接口中的@Bean,但并没有真正处理@Bean,只是暂时找出来 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(); } } //如果没有父类 那么这个类就解析完了 return null; }
大致流程就是解析配置类上的注解,根据得到的注解,做出不同的处理。如何处理通过下面的流程图来分析。doProcessConfigurationClass方法流程图:
需要注意的是除了@ComponentScan的处理会把扫描到的类注册成BeanDefinition以外,其他的处理都是记录到相关属性,后续在把这些类拿出来做处理。
那么到此parser.parse(candidates);
方法就执行完成。
配置类的处理
解析完配置类后,会得到很多新的配置类,或者bean对象。通过this.reader.loadBeanDefinitions(configClasses);
方法创建这些新的类的BeanDefinition。该方法的底层核心方法是loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
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; } //将Importe和@component的内部类 生成BeanDefinition if (configClass.isImported()) { registerBeanDefinitionForImportedConfigurationClass(configClass); } //解析所有的@bean 生成对应的BeanDefinition for (BeanMethod beanMethod : configClass.getBeanMethods()) { loadBeanDefinitionsForBeanMethod(beanMethod); } //解析导入进来的xml文件 生成bean对应的BeanDefinition loadBeanDefinitionsFromImportedResources(configClass.getImportedResources()); //执行实现了ImportBeanDefinitionRegistrar接口的方法 loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars()); }
doProcessConfigurationClass(configClass, sourceClass, filter)
方法会把根据配置类上的注解做处理,比如吧Importe导入的类和@component的内部类,收集起来,所有@bean需要创建的bean封装成BeanMethod对象收集起来,ImportedResources导入的xml文件等都没有处理。那么loadBeanDefinitionsForConfigurationClass
这个方法就是处理这些类的,把这类的对应的BeanDefinition创建出来。
由于创建出很多BeanDefinition,那么还需要判断哪些BeanDefinition是配置类。如果是配置加入到集合当中。通过开头讲的do while去处理这个集合。
总结
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
一:在启动Spring时,需要传入一个配置类,例如上面传入AppConfig.class。ApplicationContext会根据AppConfig类封装为一个BeanDefinition,这种BeanDefinition我们把它称为配置类BeanDefinition。
二:ConfigurationClassPostProcessor中会把配置类BeanDefinition取出来
三:构造一个ConfigurationClassParser用来解析配置类BeanDefinition,并且会生成一个配置类对象ConfigurationClass
四:如果配置类上存在@Component注解,那么解析配置类中的内部类(这里有递归,如果内部类也是配置类的话)
五:如果配置类上存在@PropertySource注解,那么则解析该注解,并得到PropertySource对象,并添加到environment中去
六:如果配置类上存在@ComponentScan注解,那么则解析该注解,进行扫描,扫描得到一系列的BeanDefinition对象,然后判断这些BeanDefinition是不是也是配置类BeanDefinition(只要存在@Component注解就是配置类,所以基本上扫描出来的都是配置类),如果是则继续解析该配置类,(也有递归),并且会生成对应的ConfigurationClass
七:如果配置类上存在@Import注解,那么则判断Import的类的类型:
如果是ImportSelector,那么调用执行selectImports方法得到类名,然后在把这个类当做配置类进行解析 也是递归如果是ImportBeanDefinitionRegistrar,那么则生成一个ImportBeanDefinitionRegistrar实例对象,并添加到配置类对象中(ConfigurationClass)的importBeanDefinitionRegistrars属性中。
八:如果配置类上存在@ImportResource注解,那么则把导入进来的资源路径存在配置类对象中的importedResources属性中
九:如果配置类中存在@Bean的方法,那么则把这些方法封装为BeanMethod对象,并添加到配置类对象中的beanMethods属性中
十:如果配置类实现了某些接口,则看这些接口内是否定义了@Bean的默认方法
十一:如果配置类有父类,则把父类当做配置类进行解析
十二:AppConfig这个配置类会对应一个ConfigurationClass,同时在解析的过程中也会生成另外的一些ConfigurationClass,接下来就利用reader来进一步解析ConfigurationClass
- 如果ConfigurationClass是通过@Import注解导入进来的,则把这个类生成一个BeanDefinition,同时解析这个类上@Scope,@Lazy等注解信息,并注册BeanDefinition
- 如果ConfigurationClass中存在一些BeanMethod,也就是定义了一些@Bean,那么则解析这些@Bean,并生成对应的BeanDefinition,并注册
- 如果ConfigurationClass中导入了一些资源文件,比如xx.xml,那么则解析这些xx.xml文件,得到并注册BeanDefinition
- 如果ConfigurationClass中导入了一些ImportBeanDefinitionRegistrar,那么则执行对应的registerBeanDefinitions进行BeanDefinition的注册
扩展点
通过spring对配置类处理,可以了解一些知识点,
- @Import 可以导入一些类,并且这些以配置类的形式注册到spring容器中(当然也会生成Bean对象)导入的类还可以实现ImportSelector或ImportBeanDefinitionRegistrar这两个接口做出额外的扩展
- @ImportResource 导入xxx.xml配置文件
- @PropertySource 导入xxx.properties文件到环境变量中
Import使用demo
//定义一个类 public class Aaaa { public void test(){ System.out.println("-----------Aaaa----------"); } } //定义一个Import类 public class ImportDemo { @Bean public Aaaa aaaa(){ return new Aaaa(); } }
测试代码
@ComponentScan(basePackages = {"service","config","aop"}) @Configuration @Import(ImportDemo.class) public class AppConfig { } public class Test { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); Aaaa aaaa = (Aaaa)context.getBean("aaaa"); aaaa.test(); } }
这样可以把Aaaa这个类放入到spring容器中。 通过这种形式可以控制那些bean在那些条件下才导入到spring中 例如在某个注解上加上@Import(ImportDemo.class)。这样只有使用了该注解后才会导入这个bean。
加载全部内容