从Spring到Springboot-入门级源码分析整理笔记

文章目录

    • 1 基础知识回顾
      • 1.1 扫盲
      • 1.2 spring简介
        • 1.2.1 Spring是如何简化Java开发的
        • 1.2.2 spring ioc
          • 1.2.2.1 spring使用bean示例
          • 1.2.2.2 spring创建bean的流程
          • 1.2.2.2 自定义创建bean
        • 1.2.3 spring aop
          • 1.2.3.1 介绍
          • 1.2.3.2 实现
          • 1.2.3.3 术语
        • 1.2.4 spring配置方式
    • 2 springboot
      • 2.1 简介
      • 2.2 自动装配
        • 2.2.1 springboot自动装配的底层技术
          • 2.2.1.1 Spring 模式注解装配
          • 2.2.1.2 Spring @Enable 模块装配
          • 2.2.1.3 Spring 条件装配
          • 2.2.1.4 Spring 工厂加载机制
      • 2.3 启动流程
        • 2.3.1 从执行main方法开始
        • 2.3.2 准备阶段
        • 2.3.3 启动阶段
        • 2.3.4 Springboot启动流程总结
      • 2.4 springboot 框架整合
      • 2.5 总结

1 基础知识回顾

1.1 扫盲

白话理解:

  • 什么是pojo? 简单java类
  • 什么是spring容器?就是创建bean的工厂对象,用来管理bean的生命周期
  • 什么是bean? 在spring中所有需要spring容器创建的对象
  • 什么是依赖?在A类中需要引用B类,那么就是说A依赖B
  • 什么是注入?根据上一条,将B的实例赋值给A
  • 什么是装配?将一个A类所有的依赖项全部初始化完成,组装成一个对象的过程。

1.2 spring简介

​ spring是一个2003年兴起的开源框架。Spring是为了解决企业级应用开发的复杂性而创建的。就是简化开发的意思!

1.2.1 Spring是如何简化Java开发的

为了降低Java开发的复杂性,Spring采用了以下4种关键策略:

  • 基于POJO的轻量级和最小侵入性编程,所有东西都是bean;

  • 通过IOC,依赖注入(DI)和面向接口实现松耦合;

  • 基于切面(AOP)和惯例进行声明式编程;

  • 通过切面和模版减少样式代码,RedisTemplate,xxxTemplate;

1.2.2 spring ioc

IoC也称为依赖注入(dependency injection, DI)。在这个过程中,对象只通过构造函数参数、工厂方法的参数或对象实例构造或从工厂方法返回后在对象实例上设置的属性来定义它们的依赖项(即它们所使用的其他对象)。然后容器在创建bean时注入这些依赖项。这个过程本质上是bean本身的逆向创建(因此得名控制反转)官网描述
从Spring到Springboot-入门级源码分析整理笔记_第1张图片

1.2.2.1 spring使用bean示例

通过xml文件配置使用:


<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">
    
    
    <bean id="accountDao"
        class="org.springframework.samples.jpetstore.dao.jpa.JpaAccountDao">
        
    bean>
    <bean id="itemDao" class="org.springframework.samples.jpetstore.dao.jpa.JpaItemDao">
        
    bean>
    
    
    <bean id="petStore" class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl">
        <property name="accountDao" ref="accountDao"/>
        <property name="itemDao" ref="itemDao"/>
        
    bean>

beans>

使用:

// create and configure beans
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml");
// retrieve configured instance
PetStoreService service = context.getBean("petStore", PetStoreService.class);
// use configured instance
List<String> userList = service.getUsernameList();
1.2.2.2 spring创建bean的流程

spring创建bean使用的是工厂模式。

BeanFactory 体系

BeanFactory 是一个非常纯粹的 bean 容器,它是 IOC 必备的数据结构,其中 BeanDefinition 是它的基本结构,它内部维护着一个 BeanDefinition map ,并可根据 BeanDefinition 的描述进行 bean 的创建和管理。

Beandefinition 体系

BeanDefinition 用来描述 Spring 中的 Bean 对象。

ApplicationContext体系

这个就是大名鼎鼎的 Spring 容器,它叫做应用上下文,与我们应用息息相关,它继承 BeanFactory,所以它是 BeanFactory 的扩展升级版,由于 ApplicationContext 的结构就决定了它与 BeanFactory 的不同,其主要区别有:

  1. 继承 MessageSource,提供国际化的标准访问策略。
  2. 继承 ApplicationEventPublisher ,提供强大的事件机制。
  3. 扩展 ResourceLoader,可以用来加载多个 Resource,可以灵活访问不同的资源。
  4. 对 Web 应用的支持。
  5. 在这里插入图片描述
    从Spring到Springboot-入门级源码分析整理笔记_第2张图片

介绍getBean() 方法开始创建过程

// AbstractAutowireCapableBeanFactory 471
protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
    String beanName = this.transformedBeanName(name);
// 首先尝试从缓存获取单例bean
    Object sharedInstance = this.getSingleton(beanName);
    Object bean;
    if (sharedInstance != null && args == null) {
        if (this.logger.isDebugEnabled()) {
            if (this.isSingletonCurrentlyInCreation(beanName)) {
                this.logger.debug("Returning eagerly cached instance of singleton bean '" + beanName + "' that is not fully initialized yet - a consequence of a circular reference");
            } else {
                this.logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
            }
        }
        // 获取给定bean的实例对象 如果是FactoryBean需要进行相关处理
        bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, (RootBeanDefinition)null);
    } else {
    // 只有在单例情况下会去解决循环依赖,原型模式下,如果存在A中有B属性,B中有A属性,依赖注入时
    // 会产生A还没创建完因为对于B的创建再次返回创建A,造成循环依赖,因此对于这种情况直接抛出异常
        if (this.isPrototypeCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }

        BeanFactory parentBeanFactory = this.getParentBeanFactory();
    // 检查当前BeanFactory是否存在需要创建实例的BeanDefinition 如果不存在 则委托父级容器加载
        if (parentBeanFactory != null && !this.containsBeanDefinition(beanName)) {
            String nameToLookup = this.originalBeanName(name);
            if (parentBeanFactory instanceof AbstractBeanFactory) {
                return ((AbstractBeanFactory)parentBeanFactory).doGetBean(nameToLookup, requiredType, args, typeCheckOnly);
            }

            if (args != null) {
                return parentBeanFactory.getBean(nameToLookup, args);
            }

            return parentBeanFactory.getBean(nameToLookup, requiredType);
        }
        // 判断创建的bean是否需要进行类型验证
        if (!typeCheckOnly) {
        // 向容器标记指定的bean实例已经创建
            this.markBeanAsCreated(beanName);
        }

        try {
        // 根据指定bean名称获取父级BeanDefinition 解决bean继承时子类合并父类公共属性问题
            RootBeanDefinition mbd = this.getMergedLocalBeanDefinition(beanName);
            this.checkMergedBeanDefinition(mbd, beanName, args);
      // 获取当前bean所有依赖bean的名称
            String[] dependsOn = mbd.getDependsOn();
            String[] var11;
            if (dependsOn != null) {
                var11 = dependsOn;
                int var12 = dependsOn.length;

                for(int var13 = 0; var13 < var12; ++var13) {
                    String dep = var11[var13];
                    if (this.isDependent(beanName, dep)) {
                        throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
                    }
                    // 为当前bean注册依赖bean 以便当前bean销毁前先销毁依赖bean
                    this.registerDependentBean(dep, beanName);

                    try {
                    // 创建依赖bean
                        this.getBean(dep);
                    } catch (NoSuchBeanDefinitionException var24) {
                        throw new BeanCreationException(mbd.getResourceDescription(), beanName, "'" + beanName + "' depends on missing bean '" + dep + "'", var24);
                    }
                }
            }

            if (mbd.isSingleton()) {
            // 单例bean 且 缓存不存在
                sharedInstance = this.getSingleton(beanName, () -> {
                    try {
                    // 创建bean
                        return this.createBean(beanName, mbd, args);
                    } catch (BeansException var5) {
                        this.destroySingleton(beanName);
                        throw var5;
                    }
                });
            // 获取给定bean的实例对象 如果是FactoryBean需要进行相关处理
                bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
            } else if (mbd.isPrototype()) {
                var11 = null;

                Object prototypeInstance;
                try {
                // 回调beforePrototypeCreation方法 默认实现是注册当前创建的原型对象
                    this.beforePrototypeCreation(beanName);
                // 创建bean
                    prototypeInstance = this.createBean(beanName, mbd, args);
                } finally {
                // 回调afterPrototypeCreation方法 默认实现告诉IoC容器指定Bean的原型对象不再创建
                    this.afterPrototypeCreation(beanName);
                }
                // 获取给定bean的实例对象 如果是FactoryBean需要进行相关处理
                bean = this.getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
            } else {
            // 其它scope类型处理
                String scopeName = mbd.getScope();
                Scope scope = (Scope)this.scopes.get(scopeName);
                if (scope == null) {
                    throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
                }

                try {
                    Object scopedInstance = scope.get(beanName, () -> {
                        this.beforePrototypeCreation(beanName);

                        Object var4;
                        try {
                        // 创建bean
                            var4 = this.createBean(beanName, mbd, args);
                        } finally {
                            this.afterPrototypeCreation(beanName);
                        }

                        return var4;
                    });
                    bean = this.getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
                } catch (IllegalStateException var23) {
                    throw new BeanCreationException(beanName, "Scope '" + scopeName + "' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton", var23);
                }
            }
        } catch (BeansException var26) {
            this.cleanupAfterBeanCreationFailure(beanName);
            throw var26;
        }
    }
    // 如果需要对创建的bean实例进行类型检查
    if (requiredType != null && !requiredType.isInstance(bean)) {
        try {
            T convertedBean = this.getTypeConverter().convertIfNecessary(bean, requiredType);
            if (convertedBean == null) {
                throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
            } else {
                return convertedBean;
            }
        } catch (TypeMismatchException var25) {
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Failed to convert bean '" + name + "' to required type '" + ClassUtils.getQualifiedName(requiredType) + "'", var25);
            }

            throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
        }
    } else {
        return bean;
    }
}

所有创建bean实例的时序图:
从Spring到Springboot-入门级源码分析整理笔记_第3张图片

1.2.2.2 自定义创建bean

如果想在bean的定义之外创建bean实例,我们可以使用以下流程方式:

ApplicationContext.getBeanFactory() -> 返回的类中的方法DefaultListableBeanFactory 的 registerSingleton(..) and registerBeanDefinition(..)

registerBeanDefinition(..)源码

	@Override
	public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException {

		Assert.hasText(beanName, "Bean name must not be empty");
		Assert.notNull(beanDefinition, "BeanDefinition must not be null");

		if (beanDefinition instanceof AbstractBeanDefinition) {
			try {
				((AbstractBeanDefinition) beanDefinition).validate();
			}
			catch (BeanDefinitionValidationException ex) {
				throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
						"Validation of bean definition failed", ex);
			}
		}

		BeanDefinition oldBeanDefinition;

		oldBeanDefinition = this.beanDefinitionMap.get(beanName);
		if (oldBeanDefinition != null) {
			if (!isAllowBeanDefinitionOverriding()) {
				throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
						"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
						"': There is already [" + oldBeanDefinition + "] bound.");
			}
			else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
				// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
				if (this.logger.isWarnEnabled()) {
					this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
							"' with a framework-generated bean definition: replacing [" +
							oldBeanDefinition + "] with [" + beanDefinition + "]");
				}
			}
			else if (!beanDefinition.equals(oldBeanDefinition)) {
				if (this.logger.isInfoEnabled()) {
					this.logger.info("Overriding bean definition for bean '" + beanName +
							"' with a different definition: replacing [" + oldBeanDefinition +
							"] with [" + beanDefinition + "]");
				}
			}
			else {
				if (this.logger.isDebugEnabled()) {
					this.logger.debug("Overriding bean definition for bean '" + beanName +
							"' with an equivalent definition: replacing [" + oldBeanDefinition +
							"] with [" + beanDefinition + "]");
				}
			}
			this.beanDefinitionMap.put(beanName, beanDefinition);
		}
		else {
			if (hasBeanCreationStarted()) {
				// Cannot modify startup-time collection elements anymore (for stable iteration)
				synchronized (this.beanDefinitionMap) {
					this.beanDefinitionMap.put(beanName, beanDefinition);
					List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
					updatedDefinitions.addAll(this.beanDefinitionNames);
					updatedDefinitions.add(beanName);
					this.beanDefinitionNames = updatedDefinitions;
					if (this.manualSingletonNames.contains(beanName)) {
						Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
						updatedSingletons.remove(beanName);
						this.manualSingletonNames = updatedSingletons;
					}
				}
			}
			else {
				// Still in startup registration phase
				this.beanDefinitionMap.put(beanName, beanDefinition);
				this.beanDefinitionNames.add(beanName);
				this.manualSingletonNames.remove(beanName);
			}
			this.frozenBeanDefinitionNames = null;
		}

		if (oldBeanDefinition != null || containsSingleton(beanName)) {
			resetBeanDefinition(beanName);
		}
	}

1.2.3 spring aop

1.2.3.1 介绍

AOP是面向切面的编程,其编程思想是把散布于不同业务但功能相同的代码从业务逻辑中抽取出来,封装成独立的模块,这些独立的模块被称为切面,切面的具体功能方法被称为关注点。在业务逻辑执行过程中,AOP会把分离出来的切面和关注点动态切入到业务流程中,这样做的好处是提高了功能代码的重用性和可维护性。

1.2.3.2 实现

Spring框架提供了@AspectJ 注解方法和基于XML架构的方法来实现AOP。

1.2.3.3 术语

● Aspect

表示切面。切入业务流程的一个独立模块。例如,前面案例的VerifyUser类,一个应用程序可以拥有任意数量的切面。

● Join point

表示连接点。也就是业务流程在运行过程中需要插入切面的具体位置。例如,前面案例的AopEmailNotice类的setTeacher方法就是一个连接点。

● Advice

表示通知。是切面的具体实现方法。可分为前置通知(Before)、后置通知(AfterReturning)、异常通知(AfterThrowing)、最终通知(After)和环绕通知(Around)五种。实现方法具体属于哪类通知,是在配置文件和注解中指定的。

● Pointcut

表示切入点。用于定义通知应该切入到哪些连接点上,不同的通知通常需要切入到不同的连接点上。

● Target

表示目标对象。被一个或者多个切面所通知的对象。

● Proxy

表示代理对象。将通知应用到目标对象之后被动态创建的对象。可以简单地理解为,代理对象为目标对象的业务逻辑功能加上被切入的切面所形成的对象。

● Weaving

表示切入,也称为织入。将切面应用到目标对象从而创建一个新的代理对象的过程。这个过程可以发生在编译期、类装载期及运行期。

1.2.4 spring配置方式

第一阶段:xml配置

在Spring 1.x时代,使用Spring开发满眼都是xml配置的Bean,随着项目的扩大,我们需要把xml配置文件放到不同的配置文件里,那时需要频繁的在开发的类和配置文件之间进行切换

第二阶段:注解配置

在Spring 2.x 时代,随着JDK1.5带来的注解支持,Spring提供了声明Bean的注解(例如@Component、@Service),大大减少了配置量。主要使用的方式是应用的基本配置(如数据库配置)用xml,业务配置用注解

第三阶段:java配置

Spring 3.0 引入了基于 Java 的配置能力,这是一种类型安全的可重构配置方式,可以代替 XML。我们目前刚好处于这个时代,Spring4.x和Spring Boot都推荐使用Java配置。

所有这些配置都代表了开发时的损耗。 因为在思考 Spring 特性配置和解决业务问题之间需要进行思维切换,所以写配置挤占了写应用程序逻辑的时间。除此之外,项目的依赖管理也是件吃力不讨好的事情。决定项目里要用哪些库就已经够让人头痛的了,你还要知道这些库的哪个版本和其他库不会有冲突,这难题实在太棘手。并且,依赖管理也是一种损耗,添加依赖不是写应用程序代码。一旦选错了依赖的版本,随之而来的不兼容问题毫无疑问会是生产力杀手。

2 springboot

2.1 简介

Spring Boot 简化了基于Spring的应用开发,只需要“run”就能创建一个独立的、生产级别的Spring应用。Spring Boot为Spring平台及第三方库提供开箱即用的设置(提供默认设置),这样我们就可以简单的开始。多数Spring Boot应用只需要很少的Spring配置。

我们可以使用SpringBoot创建java应用,并使用java –jar 启动它,或者采用传统的war部署方式。

Spring Boot 主要目标是:

  • 为所有 Spring 的开发提供一个从根本上更快的入门体验

  • 开箱即用,但通过自己设置参数,即可快速摆脱这种方式。

  • 提供了一些大型项目中常见的非功能性特性,如内嵌服务器、安全、指标,健康检测、外部化配置等

  • 无需 XML 配置。

2.2 自动装配

2.2.1 springboot自动装配的底层技术

2.2.1.1 Spring 模式注解装配

从Spring到Springboot-入门级源码分析整理笔记_第4张图片

模式注解是一种用于声明在spring应用中扮演“组件”角色的注解。如 Spring Framework 中的 @Repository 标注在任何类上 ,用于扮演仓储角色的模式注解。

@Component 作为一种由 Spring 容器托管的通用模式组件,任何被 @Component 标注的组件均为组件扫描的候选对象。类似地,凡是被 @Component 元标注(meta-annotated)的注解,如 @Service ,当任何组件标注它时,也被视作组件扫描的候选对象

模式注解举例

Spring Framework注解 场景说明 起始版本
@Repository 数据仓储模式注解 2.0
@Component 通用组件模式注解 2.5
@Service 服务模式注解 2.5
@Controller 控制器模式注解 2.5
@Configuration 配置类模式注解 3.0
2.2.1.2 Spring @Enable 模块装配

Spring Framework 3.1 开始支持@Enable 模块驱动。所谓”模块”是指具备相同领域的功能组件集合, 组合所形成一个独立的单元。比如 Web MVC 模块、AspectJ代理模块、Caching(缓存)模块、JMX(Java 管 理扩展)模块、Async(异步处理)模块等。

框架实现 @Enable注解模块 激活模块
Spring Framework @EnableWebMvc Web MVC 模块
@EnableTransactionManagement 事务管理模块
@EnableCaching Caching 模块
@EnableMBeanExport JMX 模块
@EnableAsync 异步处理模块
@EnableWebFlux Web Flux 模块
@EnableAspectJAutoProxy AspectJ 代理模块
Springboot @EnableAutoConfiguration 自动装配模块
@EnableManagementContext Actuator 管理模块
@EnableConfigurationProperties 配置属性绑定模块
@EnableOAuth2Sso OAuth2 单点登录模块
2.2.1.3 Spring 条件装配

从 Spring Framework 3.1 开始,允许在 Bean 装配时增加前置条件判断

条件注解举例

Spring注解 场景说明 起始版本
@Profile 配置化条件装配 3.1
@Conditional 编程条件装配 4.0
2.2.1.4 Spring 工厂加载机制
  • 实现类: SpringFactoriesLoader

  • 配置资源: META-INF/spring.factories

自动装配模块实现方法

  1. 激活自动装配 - @EnableAutoConfiguration

  2. 实现自动装配 - XXXAutoConfiguration

  3. 配置自动装配实现 - META-INF/spring.factories

源码分析:--------------------------------------------------------

2.3 启动流程

2.3.1 从执行main方法开始

开头已经说过,Spring Boot 是为了简化Spring的应用开发,创建一个独立的、生产级别的Spring应用而产生的

所以我们先从创建Spring应用开始看springboot的启动流程

	public static void main(String[] args) {
        SpringApplication.run(SpringbootApplication.class, args);
	}

也可以通过 SpringApplicationBuilder API 进行个性化调整

new SpringApplicationBuilder(DiveInSpringBootApplication.class)
.bannerMode(Banner.Mode.CONSOLE)
.web(WebApplicationType.NONE)
.profiles("prod")
.headless(true)
.run(args);

接着看源码

public static ConfigurableApplicationContext run(Class<?> primarySource,
			String... args) {
        //这个里面调用了run() 方法
		return run(new Class<?>[] { primarySource }, args);
	}
//这个run方法代码也很简单,就做了两件事情
//1、new了一个SpringApplication()  我把他叫做准备阶段
//2、执行new出来的SpringApplication()对象的run()方法  这个我把它分为启动阶段
public static ConfigurableApplicationContext run(Class<?>[] primarySources,
			String[] args) {
		return new SpringApplication(primarySources).run(args);
	}

2.3.2 准备阶段

@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
	this.resourceLoader = resourceLoader;
	Assert.notNull(primarySources, "PrimarySources must not be null");
    //1、保存主spring bean源
	this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
	//2、判断运行项目web类型
    this.webApplicationType = deduceWebApplicationType();
   //3、扫描当前路径下META-INF/spring.factories文件的,加载ApplicationContextInitializer接口实例
   // 利用 Spring 工厂加载机制,实例化 ApplicationContextInitializer 实现类
    setInitializers((Collection) getSpringFactoriesInstances(
			ApplicationContextInitializer.class));
   //4、扫描当前路径下META-INF/spring.factories文件的,加载ApplicationListener接口实例
   // 利用 Spring 工厂加载机制,实例化 ApplicationListener 实现类
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
	//5、推断引导类(Main Class)
    this.mainApplicationClass = deduceMainApplicationClass();
}

2.3.3 启动阶段

/**
* Run the Spring application, creating and refreshing a new
* {@link ApplicationContext}.
* @param args the application arguments (usually passed from a Java main method)
* @return a running {@link ApplicationContext}
*/
public ConfigurableApplicationContext run(String... args) {
        //1、创建一个计时器
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
        //2、设置了一些环境变量
        configureHeadlessProperty();
        //3、获取事件监听器SpringApplicationRunListener类型,并且执行starting()方法
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
		try {
            //4、把参数args封装成DefaultApplicationArguments
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);
            //5、准备环境了并且把环境跟spring上下文绑定,并且执行environmentPrepared()方法
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
            //6、判断环境的值,并设置环境的值
			configureIgnoreBeanInfo(environment);
            //7、打印banner
			Banner printedBanner = printBanner(environment);
            //8、创建上下文,根据项目类型创建上下文
			context = createApplicationContext();
            //9、获取异常报告事件监听
			exceptionReporters = getSpringFactoriesInstances(
					SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
            //10、准备上下文,执行完成后调用contextPrepared()方法,contextLoaded()方法
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);
            //11、spring启动的代码,这里扫描并且初始化单实列bean
            //这个refreshContext()加载了bean,还启动了内置web容器,需要细细的去看看
			refreshContext(context);
 
            //12、啥事情都没有做,方法体是空的
			afterRefresh(context, applicationArguments);
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass)
						.logStarted(getApplicationLog(), stopWatch);
			}
            //13、执行ApplicationRunListeners中的started()方法
			listeners.started(context);
            //14、执行Runner(ApplicationRunner和CommandLineRunner)
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, listeners, exceptionReporters, ex);
			throw new IllegalStateException(ex);
		}
		listeners.running(context);
		return context;
	}

2.3.4 Springboot启动流程总结

springboot启动流程分为两个步骤:

1、new了一个SpringApplication()

  • 配置Springboot Bean源

    Java 配置 Class 或 XML 上下文配置文件集合,用于 Spring Boot BeanDefinitionLoader 读取 ,并且将配置源解析加载为Spring Bean 定义

  • 推断 Web 应用类型

    根据当前应用 ClassPath 中是否存在相关实现类来推断 Web 应用的类型,包括:

    • Web Reactive: WebApplicationType.REACTIVE

    • Web Servlet: WebApplicationType.SERVLET

    • 非 Web: WebApplicationType.NONE

    private WebApplicationType deduceWebApplicationType() {
    if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)
    && !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) {
    return WebApplicationType.REACTIVE;
    }
    for (String className : WEB_ENVIRONMENT_CLASSES) {
    if (!ClassUtils.isPresent(className, null)) {
    return WebApplicationType.NONE;
    }
    }
    return WebApplicationType.SERVLET;
    }
    
  • 加载应用上下文初始器和加载应用事件监听器

    利用 Spring 工厂加载机制,实例化 ApplicationContextInitializer 实现类以及ApplicationListener 实现类

    • 技术
      • 实现类: org.springframework.core.io.support.SpringFactoriesLoader
      • 配置资源: META-INF/spring.factories
  • 推断引导类

    根据 Main 线程执行堆栈判断实际的引导类

2、执行new出来的SpringApplication()对象的run()方法

  • 加载并运行SpringApplication运行监听器( SpringApplicationRunListeners )

    利用 Spring 工厂加载机制,读取 SpringApplicationRunListener 对象集合,并且封装到组合类

    SpringApplicationRunListeners

    SpringApplicationRunListener 监听多个运行状态方法:

    监听方法阶段说明 Spring Boot 起始版本
    starting() Spring 应用刚启动 1.0
    environmentPrepared(ConfigurableEnvironment) ConfigurableEnvironment 准备妥当,允许将其调整 1.0
    contextPrepared(ConfigurableApplicationContext) ConfigurableApplicationContext 准备妥当,允许将其调整 1.0
    contextLoaded(ConfigurableApplicationContext) ConfigurableApplicationContext 已装载,但仍未启动 1.0
    started(ConfigurableApplicationContext) ConfigurableApplicationContext 已启动,此时 Spring Bean 已初始化完成 2.0
    running(ConfigurableApplicationContext) Spring 应用正在运行 2.0
    failed(ConfigurableApplicationContext,Throwable) Spring 应用运行失败 2.0
  • 创建 Environment

  • 创建 Spring 应用上下文( ConfigurableApplicationContext

  • 监听 Spring Boot 事件 / Spring 事件

2.4 springboot 框架整合

关于springboot的生态整合,那就太多了。虽然多但是非常方便。springboot提供了非常方便的整合框架。具体使用细节的话,每一个整合技术,都能写上几篇分享文章。

  • 数据
    • 关系型数据库
      • jdbc
      • jpa
      • springboot事务
    • 非关系型数据库
      • elasticsearch
  • 缓存
    • redis
  • 消息
    • 消息队列
  • 安全
  • 日志
    • 日志框架
  • 任务
    • 异步任务
    • 定时任务
  • 单元测试
  • 文档整合
  • 容器
    • servlet
    • Reactive
  • 运维
    • 生产准备特性
      • 指标
      • 健康检查
      • 外部配置
      • 热部署
      • 性能优化

2.5 总结

本周总结整理了关于spring到springboot入门级别的源码分析。后续会持续更新源码相关解读以及关于的springboot生态整合的文章。用于记录、学习与分享

你可能感兴趣的:(java)