1. SpringBoot 1.x 基于 Spring 4.x开发的
  2. SpringBoot 2.x 基于 Spring 5.x开发的
  3. spring全称:spring framework
  4. 弹性:可变形、多功能性
  5. 阅读文档一定要结合代码
  6. 笔记解析的SpringBoot版本: 2.0.2.RELEASE,没有特殊说明都是默认该版本

1. 走向自动装配

1.1 spring的手动装配

1.1.1 模式注解装配

1.1.1.1 举例了解模式注解

定义:模式注解是一种用于声明在应用中扮演“组件”角色的注解

Spring Framework 模式注解 场景说明 启始版本
@Component 将类交给spring管理 2.0
@Controller Web控制器模式注解 2.5
@Service 服务模式注解 2.5
@Repository 数据仓储模式注解 2.5
@Configuration 配置类模式注解 3.0

1.1.1.2 扫描方式、装配方式

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:mvc="http://www.springframework.org/schema/mvc"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  6. xmlns:mv="http://www.springframework.org/schema/mvc"
  7. xsi:schemaLocation="http://www.springframework.org/schema/beans
  8. http://www.springframework.org/schema/beans/spring-beans.xsd
  9. http://www.springframework.org/schema/context
  10. http://www.springframework.org/schema/context/spring-context.xsd
  11. http://www.springframework.org/schema/mvc
  12. http://www.springframework.org/schema/mvc/spring-mvc.xsd">
  13. <!-- 激活注解驱动特性 -->
  14. <context:annotation-config/>
  15. <!--
  16. context:component-scan: 标签作用,在Spring IOC初始化过程中,自动创建并管理com.imooc.springmvc及子包中,
  17. 拥有以下注解的对象: @Repository、@Service、@Controller、@Component、以及包含这些注解的派生注解,将它们注册为Spring Bean
  18. -->
  19. <context:component-scan base-package="com.yihua.mvc"/>
  1. @ComponentScan(basePackages = "com.yihua.repository")
  2. public class RepositoryBootstrap {
  3. ......
  4. }
  5. 1.这个在SpringBoot中一般放在启动类上

1.1.1.3 注解的派生性

派生性也可以称继承性、层次性,@Component作为一种由 Spring 容器托管的通用模式组件,任何被 @Component标准的组件均为组件扫描的候选对象。类似地,凡是被@Component元标注(meta-annotated)的注解,如 @Service,当任何组件标注它时,也被视作组件扫描的候选对象,结果举例:@Service@Component其实是没有区别的,前者继承了后者所有的特性
满足条件:

  1. 注解需要继承需要的注解
  2. 父注解的字段,子注解必须重写,并且使用@AliasFor标识

既然功能一样,为什么要使用?

  1. 个人理解:是为了满足模块化区分,让注解更具有标识性
  2. 扩展性,可以在父注解的基础上新增功能

    1.1.2 @Enable模块装配置

    1.1.2.1 了解模块装配

Spring Framework 3.1开始支持@Enable 模块驱动。所谓“模块”是指具备相同领域的功能组件集合, 组合所形成一个独立的单元比如Web MVC模块、AspectJ代理模块、Caching缓存模块、Java管理扩展模块、Async(异步处 理)模块等。这样就可以简化一些配置,不用一个一个配置的写。@Import注解是可以导入多个配置的,参数是一个数组

框架实现 @Enable注解模块 激活模块
Spring Framework @EnableWebMvc Web MVC模块

image.png

1.1.2.2 注解驱动的方式

  1. @Retention(RetentionPolicy.RUNTIME)
  2. @Target(ElementType.TYPE)
  3. @Documented
  4. @Import(DelegatingWebMvcConfiguration.class)
  5. public @interface EnableWebMvc {
  6. }
  7. @Configuration
  8. public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport{
  9. ...
  10. }
  11. @EnableWebMvc
  12. public class AutoconfigApplication {
  13. // 我是启动类
  14. }
  15. 1.观察mvc的实现方式,通过@Import导入一个实现了很多bean模块的类完成的
  16. 2.自己仿写的查看项目"dive-sprong-boot"的自动配置章节

1.1.2.3 接口驱动的方式

  1. @Target(ElementType.TYPE)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. @Import(CachingConfigurationSelector.class)
  5. public @interface EnableCaching {
  6. ......
  7. }
  8. public class CachingConfigurationSelector extends AdviceModeImportSelector<EnableCaching> {
  9. ......
  10. }
  11. public abstract class AdviceModeImportSelector<A extends Annotation> implements ImportSelector {
  12. ......,这里返回的都是一个一个bean的名称
  13. }
  14. @EnableCaching
  15. public class AutoconfigApplication {
  16. // 我是启动类
  17. }
  18. 1.这种方式更灵活,可以在返回bean的前提上做很多操作
  19. 2.接口装配只是对于注解装配进行了一个增强操作,或者说就是增加条件来判断装配哪一个已经被注解的bean去装配,而不是脱离注解进行装配
  20. 3.这种方式最终都是实现ImportSelector接口
  21. 4.自己仿写的查看项目"dive-sprong-boot"的自动配置章节

1.1.3 条件装配

1.1.3.1 了解条件配置

Spring Framework 3.1开始,允许在 Bean 装配时增加前置条件判断,这就可以理解为,需要用户输入条件装配哪些内容,这样是更灵活的

Spring Framework 注解 场景说明 启开版本
@Profile 配置话条件装配 3.1
@Conditional 编程条件装配 4.0

1.1.3.2 配置方式实现

是通过@profile注解实现的,可以指定条件,然后实例化那个对象;项目中实现的计算服务查看源码即可

1.1.3.3 编程方式实现

是基于@Conditional注解实现的,需要实现Condition接口
ConditionalOnClass:当前类是否存在
ConditionalOnProperty:仿写的是它
@profile

1.2 SpringBoot的自动装配置

1.2.1 了解自动装配

  1. 一些经典的类:WebMvcAutoConfiguration
  2. 定义:基于约定大于配置的原则,实现Spring组件自动装配的目的
  3. 底层装配技术
    1. Spring的模式注解
    2. Spring@Enable模块装配
    3. Spring的条件装配
    4. Spring的工厂加载机制
      1. 实现类:SpringFactoriesLoader,重要的loadFactories方法不断遍历配置文件
      2. 配置资源:
        1. MATE-INF/spring.factorise,这个文件可以在jar包里面,可以在项目文件中
        2. SpringBoot中这个文件的路径是:spring-boot-autoconfig:版本号
  4. 实现方式

    1. 激活自动装配
    2. 实现自动装配
    3. 配置自动装配实现

      1.2.2 自定义自动装配

      自定义实现HelloWorldAutoConfiguration自动装配
  5. 实现HelloWorldAutoConfiguration配置类

    1. 条件判断: user.name == "Mercy"
    2. 模式注解: @Configuration
    3. 模块@Enable@EnableYihua-YihuaSelector-YihuaConfiguration-yihuaSay
  6. 定义工厂配置文件
    1. spring.factories
  7. 定义启动类@EnableAutoConfiguration,其实这一步不用写,因为你如果做组件或者写依赖包,人家引用你的时候自然有,一般都是使用@SpringBootApplication包装

    1.2.3 自动装配源码分析

    2. 理解SpringApplication

    2.1 准备阶段

    2.1.1 配置SpringBootBean源

2.1.2 推断web应用类型

为什么SpringBoot要去推断类型,因为它希望你少去配置,定义好了类型,可以直接去加载定义好的配置
根据当前应用ClassPath是否存在相关实现类来推断Web应用类型

  1. public class demo {
  2. // Web Reactive: WebApplicationType.REACTIVE
  3. // Web Servlet: WebApplicationType.SERVLET
  4. // 非 Web: WebApplicationType.NONE
  5. private static final String REACTIVE_WEB_ENVIRONMENT_CLASS = "org.springframework."
  6. + "web.reactive.DispatcherHandler";
  7. private static final String MVC_WEB_ENVIRONMENT_CLASS = "org.springframework."
  8. + "web.servlet.DispatcherServlet";
  9. private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
  10. "org.springframework.web.context.ConfigurableWebApplicationContext" };
  11. // 这个就是推断web应用类型的方法,org.springframework.boot.SpringApplication
  12. private WebApplicationType deduceWebApplicationType() {
  13. // 当reactive类存在的时候,mvc不存在的时候,选择reactive方式
  14. if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)
  15. && !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) {
  16. return WebApplicationType.REACTIVE;
  17. }
  18. // 走到这里说明reactive类不存在,或者reactive/mvc都存在
  19. // 遍历Servlet、ConfigurableWebApplicationContext是否存在,不存在则定义为none
  20. for (String className : WEB_ENVIRONMENT_CLASSES) {
  21. if (!ClassUtils.isPresent(className, null)) {
  22. return WebApplicationType.NONE;
  23. }
  24. }
  25. // 否则返回的是Servlet
  26. return WebApplicationType.SERVLET;
  27. }
  28. // 开发者也可以强制设置成哪一种类型,在启动类中设置
  29. springApplication.setWebApplicationType(WebApplicationType.NONE)
  30. }

新版本已经修改了实现,方法也是类似的,org.springframework.boot.WebApplicationType#deduceFromClasspath通过ClassPath加载类名来判断

2.1.3 推断引导类

为什么需要推导引导类?常规的执行run方法的时候已经传递的了引导类,但是SpringBoot支持多种启动方式,像如下的方式就没有传递引导类,那么SpringBoot就需要知道引导类

  1. public class demo {
  2. // 是根据main线程执行的堆栈判断实际的引导类
  3. private Class<?> deduceMainApplicationClass() {
  4. try {
  5. // StackTraceElement这个是一个线程的栈信元素
  6. // 获取正在执行的线程的栈元素
  7. StackTraceElement[] stackTrace =
  8. new RuntimeException().getStackTrace();
  9. // 遍历所有线程的栈元素
  10. for (StackTraceElement stackTraceElement : stackTrace) {
  11. // 如果methodName = main,则说明是引导类
  12. if ("main".equals(stackTraceElement.getMethodName())) {
  13. return Class.forName(stackTraceElement.getClassName());
  14. }
  15. }
  16. }
  17. catch (ClassNotFoundException ex) {
  18. // Swallow and continue
  19. }
  20. return null;
  21. }
  22. }

2.1.4 加载应用上下文初始器

  1. 流程:是利用Spring的工厂加载机制,实例化ApplicationContextInitializer,并排序对象集合
  2. 技术
    1. 实现类:org.springframework.core.io.support.SpringFactoriesLoader
    2. 资源配置:MATE-INF/spring.factories
    3. 排序:AnnotationAwareOrderComparator.sort();
      1. public class demo {
      2. /**
      3. * 获取Spring的工厂实例
      4. * 解释一下:为什么下面的代码看起来和上下文初始化器毫无关系,
      5. * 因为下面的代码是加载所有的工厂实例
      6. * 上下文初始器只是其中一部分,并且排在最前面,
      7. * 查看"MATE-INF/spring.factories"文件发现第一个
      8. * 需要加载的就是ApplicationContextInitializer
      9. *
      10. * @param type
      11. * @param parameterTypes
      12. * @param args
      13. * @param <T>
      14. * @return
      15. */
      16. private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
      17. // 获取当前线程的上下文类加载器
      18. ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
      19. // 使用名称并确保唯一以防止重复
      20. // 这里就是使用Spring工厂机制的实现类SpringFactoriesLoader加载工厂
      21. Set<String> names =
      22. new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
      23. // 创建Spring的工厂实例
      24. List<T> instances =
      25. createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
      26. // 对实例进行排序
      27. AnnotationAwareOrderComparator.sort(instances);
      28. // 返回所有的工厂实例
      29. return instances;
      30. }
      31. }

注解:org.springframework.core.annotation.Order
接口:org.springframework.core.Ordered
控制加载顺序的
个人理解:加载加载,肯定是分先后顺序的

2.1.5 加载应用事件监听器

加载方式和加载应用上下文是一样的,都是在一起的被加载。

2.2 运行阶段

2.2.1 运行事件监听器编程模型

2.2.2 运行监听器

image.png