SpringBoot源码分析之BeanDefinitionLoader注册主Configuration的Java配置类
2018-03-27 18:00
1011 查看
在上一篇中我们分析了SpringBootApplication的启动过程,在启动过程中通过调用BeanDefinitionLoader的load()方法来加载sources资源,也就是Application.class类。为了直观起见,还是将之前的示例写出来。
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
applyInitializers(context);
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
context.getBeanFactory().registerSingleton("springApplicationArguments",
applicationArguments);
if (printedBanner != null) {
context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
}
// Load the sources 加载Application.class
Set<Object> sources = getSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[sources.size()]));//主要方法
listeners.contextLoaded(context);
}
Application.class作为sources传入到SpringApplication中,最终处理sources的是BeanDefinitionLoader的load()方法。下面开始分析BeanDefinitionLoader。
加载Resource。典型的就是加载application.xml Bean配置文件。
加载Package。以扫包的方式加载Bean定义。
@SpringBootApplication public class Application { /** * 启动Spring Boot应用 */ public static void main(String[] args) { SpringApplication.run(Application.class, args); } }SpringApplication.run()中的prepareContext() private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
applyInitializers(context);
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
context.getBeanFactory().registerSingleton("springApplicationArguments",
applicationArguments);
if (printedBanner != null) {
context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
}
// Load the sources 加载Application.class
Set<Object> sources = getSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[sources.size()]));//主要方法
listeners.contextLoaded(context);
}
protected void load(ApplicationContext context, Object[] sources) { if (logger.isDebugEnabled()) { logger.debug( "Loading source " + StringUtils.arrayToCommaDelimitedString(sources)); }//创建BeanDefinitionLoader BeanDefinitionLoader loader = createBeanDefinitionLoader( getBeanDefinitionRegistry(context), sources); if (this.beanNameGenerator != null) { loader.setBeanNameGenerator(this.beanNameGenerator); } if (this.resourceLoader != null) { loader.setResourceLoader(this.resourceLoader); } if (this.environment != null) { loader.setEnvironment(this.environment); } loader.load();//该方法中加载注册Application.class进applicationContext }
Application.class作为sources传入到SpringApplication中,最终处理sources的是BeanDefinitionLoader的load()方法。下面开始分析BeanDefinitionLoader。
BeanDefinitionLoader分析
BeanDefinitionLoader的构造方法如下:BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) { // Bean注册器和sources必须不为空 Assert.notNull(registry, "Registry must not be null"); Assert.notEmpty(sources, "Sources must not be empty"); this.sources = sources; // 注解形式的Bean定义读取器 this.annotatedReader = new AnnotatedBeanDefinitionReader(registry); // XML形式的Bean定义读取器 this.xmlReader = new XmlBeanDefinitionReader(registry); // Groovy形式的Bean定义读取器 if (isGroovyPresent()) { this.groovyReader = new GroovyBeanDefinitionReader(registry); } // 类路径扫描器 this.scanner = new ClassPathBeanDefinitionScanner(registry); // 扫描器添加排除过滤器 this.scanner.addExcludeFilter(new ClassExcludeFilter(sources)); }在构造方法中我们可以知道,准备了几种Bean定义读取器,可以通过不同的方式来读取定义的Bean,以及提供了包扫描器。下面我们看看load()方法,看支持对哪些形式的Bean的加载。
public int load() { int count = 0; for (Object source : this.sources) { count += load(source); } return count; } private int load(Object source) { Assert.notNull(source, "Source must not be null"); if (source instanceof Class<?>) { return load((Class<?>) source); } if (source instanceof Resource) { return load((Resource) source); } if (source instanceof Package) { return load((Package) source); } if (source instanceof CharSequence) { return load((CharSequence) source); } throw new IllegalArgumentException("Invalid source type " + source.getClass()); }从上面的方法很清楚看出,BeanDefinitionLoader可以从Class、Resource、Package和CharSequence中进行加载。对于我们上面的应用Application.class使用的是Class形式的加载。说明: 对于Groovy部分我们不做分析。
加载Class
加载Class的方法如下:private int load(Class<?> source) { if (isComponent(source)) { this.annotatedReader.register(source); return 1; } return 0; }首先通过isComponent方法进行判断,是否符合加载条件,然后使用注解形式的Bean定义读取器进行注册。isComponent方法主要判断source是否使用了@Component注解,另外,内部类不能加载。在上面的示例中Application.class使用了@SpringBootApplication注解,@SpringBootApplication注解包含了@Component注解。关于@SpringBootApplication注解后面再详细分析。
加载Resource
使用XML形式的Bean定义读取器进行加载。private int load(Resource source) { if (source.getFilename().endsWith(".groovy")) { if (this.groovyReader == null) { throw new BeanDefinitionStoreException( "Cannot load Groovy beans without Groovy on classpath"); } return this.groovyReader.loadBeanDefinitions(source); } return this.xmlReader.loadBeanDefinitions(source); }
加载Package
使用包扫描器进行加载。private int load(Package source) { return this.scanner.scan(source.getName()); }
加载CharSequence
这种形式实际上可以等同于sources为字符串,根据解析sources,进行匹配Class、Resource或Package进行加载。具体细节不分析了,原理一样。private int load(CharSequence source) { String resolvedSource = this.xmlReader.getEnvironment() .resolvePlaceholders(source.toString()); // 尝试使用Class加载 try { return load(ClassUtils.forName(resolvedSource, null)); } catch (IllegalArgumentException ex) { // swallow exception and continue } catch (ClassNotFoundException ex) { // swallow exception and continue } // 尝试使用Resource加载 Resource[] resources = findResources(resolvedSource); int loadCount = 0; boolean atLeastOneResourceExists = false; for (Resource resource : resources) { if (isLoadCandidate(resource)) { atLeastOneResourceExists = true; loadCount += load(resource); } } if (atLeastOneResourceExists) { return loadCount; } // 尝试使用Package加载 Package packageResource = findPackage(resolvedSource); if (packageResource != null) { return load(packageResource); } throw new IllegalArgumentException("Invalid source '" + resolvedSource + "'"); }BeanDefinitionLoader定义了不同形式的方式来对SpringApplication中的sources进行加载。加载Class。也就是我们使用的加载类注解的方式来加载Bean定义。
加载Resource。典型的就是加载application.xml Bean配置文件。
加载Package。以扫包的方式加载Bean定义。
AnnotatedBeanDefinitionReader分析
在上面我们分析过,当我们使用SpringApplication.run(Application.class, args)开始启动Spring Boot时,最终使用AnnotatedBeanDefinitionReader的register(Class<?>... annotatedClasses)方法进行注册Bean。在createContext()方法中context初始化时创建AnnotatedBeanDefinitionReader时已经调用过AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry), 见 https://blog.csdn.net/liaokailin/article/details/49048557AnnotatedBeanDefinitionReader构造方法
我们还是从构造方法开始分析,AnnotatedBeanDefinitionReader的构造方法如下:public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) { // registry和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(this.registry)中向Spring容器注入了注解配置时需要使用的后置处理器。代码如下:public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors( BeanDefinitionRegistry registry, 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<BeanDefinitionHolder>(4); // 判断注册器中是否存在Spring内置的ConfigurationClassPostProcessor类型的Bean
// 如果没有则添加到注册器中 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)); } // 判断注册器中是否存在Spring内置的AutowiredAnnotationBeanPostProcessor类型的Bean // 如果没有则添加到注册器中 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)); } // 省略了其他的Bean注册 return beanDefs; }在能够使用AnnotatedBeanDefinitionReader开始对注解类注册Bean之前,Spring加入了一系列的BeanPostProcessor处理器。至于有哪些处理器,我们目前不关心。
注册Bean
在构造方法之后,开始对注解类进行注册,在我们的示例中也就是对Application.class进行注册处理。注册方法如下:public void registerBean(Class<?> annotatedClass, String name, Class<? extends Annotation>... qualifiers) { // 包装注解类 AnnotatedGenericBeanDefinition abd =new AnnotatedGenericBeanDefinition(annotatedClass); // 判断是否应该跳过注册Bean if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) { return; } // 获取Scope元数据信息 ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd); abd.setScope(scopeMetadata.getScopeName()); // 生成Bean名称 String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry)); // 处理一些公共的注解 AnnotationConfigUtils.processCommonDefinitionAnnotations(abd); if (qualifiers != null) { for (Class<? extends Annotation> qualifier : qualifiers) { if (Primary.class == qualifier) { abd.setPrimary(true); } else if (Lazy.class == qualifier) { abd.setLazyInit(true); } else { abd.addQualifier(new AutowireCandidateQualifier(qualifier)); } } } // 创建Bean定义对象holder暂时保存对象 BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName); // 代理类 definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); // 注册到Spring中 BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry); }
条件跳过Bean注册
下面我们需要看一下ConditionEvaluator的shouldSkip方法,该方法处理了是否需要跳过注册。在该方法中首先会检测是否是属于@Conditional注解,@Conditional注解可以包含Condition接口集合,根据这个接口集合来判断是否符合Bean的注册。方法如下:public boolean shouldSkip(AnnotatedTypeMetadata metadata, ConfigurationPhase phase) { // 首先判断是否属于@Conditional类型注解 if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) { return false; } if (phase == null) { if (metadata instanceof AnnotationMetadata && ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) { return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION); } return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN); } // 从@conditional中获取Condition集合 List<Condition> conditions = new ArrayList<Condition>(); for (String[] conditionClasses : getConditionClasses(metadata)) { for (String conditionClass : conditionClasses) { Condition condition = getCondition(conditionClass, this.context.getClassLoader()); conditions.add(condition); } } // 进行排序 AnnotationAwareOrderComparator.sort(conditions); // 一旦发现有不匹配的Condition就表示需要跳过注册 for (Condition condition : conditions) { ConfigurationPhase requiredPhase = null; if (condition instanceof ConfigurationCondition) { requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase(); } if (requiredPhase == null || requiredPhase == phase) { if (!condition.matches(this.context, metadata)) { return true; } } } return false; }在上面的代码分析中,我们使用的Application.class对象,最后被处理成AnnotatedGenericBeanDefinition对象,然后被注册到Spring容器中去。参考于 https://my.oschina.net/xiaoqiyiye/blog/1624227
相关文章推荐
- Spring源码分析-BeanDefinition加载、解析和注册
- 【spring源码分析】BeanDefinitionRegistryPostProcessor接口可自定义bean加入IOC
- Spring源码阅读--BeanDefinition 在 IOC 容器中的注册
- 【Spring源码--IOC容器的实现】(四)BeanDefinition的注册
- spring源码(8)注册解析的BeanDefinition
- Spring源码解析--Spring配置文件解析BeanDefinitionParserDelegate(四)
- Spring Boot【原理分析】(3)——BeanDefinition
- Spring源码学习--Spring配置文件解析BeanDefinitionReader(二)
- Spring源码解析-BeanDefinition在IOC容器中的注册(三)
- Spring源码阅读之IoC容器初始化3 -- BeanDefinition在IoC容器中的注册
- Spring源码情操陶冶-AnnotationConfigBeanDefinitionParser注解配置解析器
- 7-spring源码3.2.18解读+spring技术内幕(关于BeanDefinition的注册)
- Spring源码学习--Spring配置文件解析BeanDefinitionDocumentReader(三)
- spring源码学习之四 BeanDefinitionParserDelegate分析
- Spring源码之旅(3)_BeanDefinition的解析与注册
- 理解spring的BeanDefinition和配置文件(原文标题:动态注册bean到spring容器)
- 【Spring源码--IOC容器的实现】(三)BeanDefinition的载入和解析【I】
- SpringBoot-Loader源码分析系列2:启动 new JarLauncher().launch(args)的.launch(args)部分
- 【Spring源码--IOC容器的实现】(三)BeanDefinition的载入和解析【II】
- SpringBoot-Loader源码分析系列1:启动&读取MANIFEST.MF文件