spring boot实战(第九篇)Application创建源码分析
前言
通过前面的文章了解到在spring boot的启动时,利用的是编写的Application类,使用了注解@SpringBootApplication,本篇将阐述该Bean的加载过程。
[html] view plain copy
- @SpringBootApplication
- public class Application {
- public static void main(String[] args) {
- SpringApplication app = new SpringApplication(Application.class);
- app.addListeners(new MyApplicationStartedEventListener());
- app.run(args);
- }
- }
Application
上篇中讲述了上下文的创建,在run方法中接下来会执行
[html] view plain copy
- load(context, sources.toArray(new Object[sources.size()]));
这个的sources表示的为Application类,在创建SpringApplication时手动传递
[html] view plain copy
- SpringApplication app = new SpringApplication(Application.class);
load方法如下:
[html] view plain copy
- protected void load(ApplicationContext context, Object[] sources) {
- if (this.log.isDebugEnabled()) {
- this.log.debug("Loading source "
- + StringUtils.arrayToCommaDelimitedString(sources));
- }
- 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();
- }
调用loader.load();
[html] view plain copy
- 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());
- }
执行load((Class<?>) source)
[html] view plain copy
- private int load(Class<?> source) {
- if (isGroovyPresent()) {
- // Any GroovyLoaders added in beans{} DSL can contribute beans here
- if (GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
- GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source,
- GroovyBeanDefinitionSource.class);
- load(loader);
- }
- }
- if (isComponent(source)) {
- this.annotatedReader.register(source);
- return 1;
- }
- return 0;
- }
isComponent判断Application是否存在注解Compent
[html] view plain copy
- private boolean isComponent(Class<?> type) {
- // This has to be a bit of a guess. The only way to be sure that this type is
- // eligible is to make a bean definition out of it and try to instantiate it.
- if (AnnotationUtils.findAnnotation(type, Component.class) != null) {
- return true;
- }
- // Nested anonymous classes are not eligible for registration, nor are groovy
- // closures
- if (type.getName().matches(".*\\$_.*closure.*") || type.isAnonymousClass()
- || type.getConstructors() == null || type.getConstructors().length == 0) {
- return false;
- }
- return true;
- }
AnnotationUtils.findAnnotation(type, Component.class) 工具类获取执行类对应的注解信息,该工具类在自己编码代码时可用得到
由于Application使用注解@SpringBootApplication,其定义如下
[html] view plain copy
- @Target(ElementType.TYPE)
- @Retention(RetentionPolicy.RUNTIME)
- @Documented
- @Inherited
- @Configuration
- @EnableAutoConfiguration
- @ComponentScan
- public @interface SpringBootApplication {
- /**
- * Exclude specific auto-configuration classes such that they will never be applied.
- * @return the classes to exclude
- */
- Class<?>[] exclude() default {};
- }
发现不存在Compoment注解,是不是表明Application不是一个Component呢?其实不然,来看下@Configuration注解
[html] view plain copy
- @Target(ElementType.TYPE)
- @Retention(RetentionPolicy.RUNTIME)
- @Documented
- @Component
- public @interface Configuration {
- /**
- * Explicitly specify the name of the Spring bean definition associated
- * with this Configuration class. If left unspecified (the common case),
- * a bean name will be automatically generated.
- * <p>The custom name applies only if the Configuration class is picked up via
- * component scanning or supplied directly to a {@link AnnotationConfigApplicationContext}.
- * If the Configuration class is registered as a traditional XML bean definition,
- * the name/id of the bean element will take precedence.
- * @return the specified bean name, if any
- * @see org.springframework.beans.factory.support.DefaultBeanNameGenerator
- */
- String value() default "";
- }
发现Configuration注解上存在Component注解,表明Application为Component
接下来执行this.annotatedReader.register(source);
[html] view plain copy
- public void register(Class<?>... annotatedClasses) {
- for (Class<?> annotatedClass : annotatedClasses) {
- registerBean(annotatedClass);
- }
- }
调用registerBean注册Application对应的bean信息
[html] view plain copy
- public void registerBean(Class<?> annotatedClass, String name,
- @SuppressWarnings("unchecked") Class<? extends Annotation>... qualifiers) {
- AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
- if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
- return;
- }
- ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
- abd.setScope(scopeMetadata.getScopeName());
- 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.equals(qualifier)) {
- abd.setPrimary(true);
- }
- else if (Lazy.class.equals(qualifier)) {
- abd.setLazyInit(true);
- }
- else {
- abd.addQualifier(new AutowireCandidateQualifier(qualifier));
- }
- }
- }
- BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
- definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
- BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
- }
首先来看
[html] view plain copy
- if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
- return;
- }
判断是否需要跳过,其代码如下:
[html] view plain copy
- public boolean shouldSkip(AnnotatedTypeMetadata metadata, ConfigurationPhase phase) {
- 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);
- }
- 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);
- 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上是否存在Conditional注解,如果不满足Conditional对应条件则该bean不被创建;
Conditional注解
代码分析到这里可以先看看Conditional注解的使用,其定义为:
[html] view plain copy
- @Retention(RetentionPolicy.RUNTIME)
- @Target({ElementType.TYPE, ElementType.METHOD})
- public @interface Conditional {
- /**
- * All {@link Condition}s that must {@linkplain Condition#matches match}
- * in order for the component to be registered.
- */
- Class<? extends Condition>[] value();
- }
从源码可以看出,首先判断Application上是否存在Conditional,如果存在,则获取Conditional注解中的value数组值,对应的Class必须实现Condition接口:
[html] view plain copy
- public interface Condition {
- boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
- }
如果matches返回true 表明该bean需要被创建,否则表明该bean不需要被创建。
明白了该注解的用法后,来一个实际案例
[html] view plain copy
- package com.lkl.springboot.condition;
- import org.springframework.context.annotation.Conditional;
- import org.springframework.stereotype.Component;
- @Component("MyCondition")
- @Conditional(MyCondition.class)
- public class ConditionBean {
- }
创建ConditionBean,使用注解@Conditional(MyCondition.class)调用MyCondition类
[html] view plain copy
- /**
- * 自定义condition 修改返回值,查看bean是否创建
- *
- * @author liaokailin
- */
- public class MyCondition implements Condition {
- /**
- * 返回true 生成bean
- * 返回false 不生成bean
- */
- @Override
- public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
- Map<String, Object> map = metadata.getAnnotationAttributes(Component.class.getName());
- return "MyCondition".equals(map.get("value").toString());
- }
- }
MyCondition实现接口Condition,在matches方法中获取bean上注解Component信息,如果bean名称等于MyCondition返回true,否则返回false,bean不会被创建。
回到前面Application的分析,Application上不存在Conditional,因此shouldSkip返回false,代码继续执行
[html] view plain copy
- ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
处理Scope注解信息,默认是单例bean
执行
[html] view plain copy
- AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
处理一些常见注解信息
[html] view plain copy
- static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) {
- if (metadata.isAnnotated(Lazy.class.getName())) {
- abd.setLazyInit(attributesFor(metadata, Lazy.class).getBoolean("value"));
- }
- else if (abd.getMetadata() != metadata && abd.getMetadata().isAnnotated(Lazy.class.getName())) {
- abd.setLazyInit(attributesFor(abd.getMetadata(), Lazy.class).getBoolean("value"));
- }
- if (metadata.isAnnotated(Primary.class.getName())) {
- abd.setPrimary(true);
- }
- if (metadata.isAnnotated(DependsOn.class.getName())) {
- abd.setDependsOn(attributesFor(metadata, DependsOn.class).getStringArray("value"));
- }
- if (abd instanceof AbstractBeanDefinition) {
- AbstractBeanDefinition absBd = (AbstractBeanDefinition) abd;
- if (metadata.isAnnotated(Role.class.getName())) {
- absBd.setRole(attributesFor(metadata, Role.class).getNumber("value").intValue());
- }
- if (metadata.isAnnotated(Description.class.getName())) {
- absBd.setDescription(attributesFor(metadata, Description.class).getString("value"));
- }
- }
- }
处理Lazy、Primary、DependsOn、Role、Description等注解
最后调用
[html] view plain copy
- BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
注册bean信息,在注册bean信息之前通过
[html] view plain copy
- String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
获取bean名称
bean的注册调用为
[html] view plain copy
- registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
该代码在上篇中已有说明。
此时Application对应bean已创建完成。
spring boot实战(第九篇)Application创建源码分析相关推荐
- spring boot 超市进销存系统源码
spring boot 超市进销存系统源码 功能:本系统根据权限划分为三种用户:系统管理员,货物管理员,商品管理员(具体角色可以根据权限划分多个) 系统管理员默认功能:客户管理,供应商管理,商品管理, ...
- 番外篇——直流电机桥源码分析LED驱动例程开发
[番外篇]直流电机桥源码分析&LED驱动例程开发 直流电机桥测试代码分析 直流电机桥驱动代码分析 仿写HBLED驱动程序 利用芯片手册修改设备树 利用原理图寻找引脚 仿写代码 仿写HBLED测 ...
- Spring自定义注解驱动开发使用及源码分析
目录 前言 注解驱动开发使用 需求 代码实现 测试效果 源码分析 BeanDefinitionRegistryPostProcessor接口 解析BeanDefinition 处理Bean上配置的注解 ...
- 聊聊Spring中的数据绑定 --- DataBinder本尊(源码分析)【享学Spring】
每篇一句 唯有热爱和坚持,才能让你在程序人生中屹立不倒,切忌跟风什么语言或就学什么去~ 前言 数据绑定 这个概念在任何一个成型的框架中都是特别重要的(尤其是web框架),它能让框架更多的自动化,更好容 ...
- Java集合篇:LinkedList源码分析
(注:本文内容基于JDK1.6) 一.概述: LinkedList与ArrayList一样实现List接口,只是ArrayList是List接口的大小可变数组的实现,LinkedList是List接口 ...
- Linux项目实战C++轻量级Web服务器源码分析TinyWebServer
目录 文章简介 一. 先跑起来项目 二.再看项目核心 三.逐个击破!立下flag 文章简介 TinyWebServer是Linux下C++轻量级Web服务器,助力初学者快速实践网络编程,搭建属于自己的 ...
- 聊聊Spring中的数据绑定 --- DataBinder本尊(源码分析)
每篇一句 唯有热爱和坚持,才能让你在程序人生中屹立不倒,切忌跟风什么语言或就学什么去~ 相关阅读 [小家Spring]聊聊Spring中的数据转换:Converter.ConversionServic ...
- 【小家Spring】聊聊Spring中的数据绑定 --- DataBinder本尊(源码分析)
每篇一句 > 唯有热爱和坚持,才能让你在程序人生中屹立不倒,切忌跟风什么语言或就学什么去~ 相关阅读 [小家Spring]聊聊Spring中的数据绑定 --- 属性访问器PropertyAcce ...
- spring boot实战(第二篇)事件监听
前言 spring boot在启动过程中增加事件监听机制,为用户功能拓展提供极大的便利. 支持的事件类型四种 ApplicationStartedEvent ApplicationEnvironmen ...
最新文章
- Centos安装g++错误 :No package g++ available
- 解决Sqlite UTF-8中文数据格式在DOS窗口下中文乱码
- iOS 系统版本判断
- 自省:我为什么没有成功--对照35前务必完成的12跳
- 通俗易懂,C#如何安全、高效地玩转任何种类的内存之Span
- delphi连接sql server的字符串2011-10-11 16:07
- 趣谈 JOIN 操作
- 英文教材《FPGA-Prototyping-By-Verilog-Examples》下载
- 2017云计算与IT风向标-------- 移动、转型、整合
- [傅里叶变换及其应用学习笔记] 十五. 傅里叶变换在衍射上的应用
- AI智能写作系统文章生成器,写原创文章更快更简单
- 使用Setup Factory如何制作程序安装包
- linux下删除一个环境变量,Linux 添加环境变量和删除环境变量
- 启动docker时映射到宿主机时出现 /usr/bin/docker-current: Error response from daemon: driver failed……的解决方案
- uni-app相关知识积累
- Putty登陆root输入密码被拒绝(没开启ssh)
- sqlserver的分页查询
- 网络安全实验室CTF—脚本关 writeup
- 我们用ESP32-Wrover-Kit做自平衡小车犯过的坑
- LeetCode 254. Factor Combinations(因式分解)
热门文章
- resize函数缩小图片的尺寸 车辆检测
- Rancher upgrade webhook之CI/CD
- C# 6.0的字典(Dictionary)的语法
- Intellij Idea 创建Web项目入门(一)
- 三层交换的HSRP与VRRP
- Python初学者之ImportError: No module named moviepy.editor 的解决办法
- 对于WebAssembly编译出来的.wasm文件js如何调用
- 修改Intelij IDEA的maven下载地址为国内阿里云镜像
- linux下aio异步读写详解与实例
- 论文笔记:GVCNN: Group-View Convolutional Neural Networks for 3D Shape Recognition