菜瓜:我自己去调Mvc的源码差点没给Spring的逻辑秀死。。。难受

水稻:那今天咱们看一个简单易用的SpringBoot吧

菜瓜:可以,这个我熟悉

水稻:熟悉?

菜瓜:当我没说,请开始你的表演

水稻:我没有别的意思,就是单纯的反问(手动狗头)。平时工作中用多了SpringBoot。咱们今天带着几个问题来看看它的操作吧

  1. 如何启动Spring容器
  2. 如何内嵌Tomcat容器
  3. 如何完成自动装配,就是0配置

菜瓜:你确定这是我熟悉的SpringBoot???

水稻:。。。看过来

  • 启动类点进去
  • public ConfigurableApplicationContext run(String... args) {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ...
    listeners.starting();
    try {
    ...
    // ①创建Spring上下文容器对象 - 默认Servlet容器
    context = createApplicationContext();
    ...
    // ②调用refresh方法 - 回到熟悉的容器启动流程
    refreshContext(context);
    afterRefresh(context, applicationArguments);
    ...
    ...
    return context;
    }
  • ① 创建上下文对象
    • protected ConfigurableApplicationContext createApplicationContext() {
      Class<?> contextClass = this.applicationContextClass;
      if (contextClass == null) {
      try {
      switch (this.webApplicationType) {
      case SERVLET:
      contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
      break;
      case REACTIVE:
      contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
      break;
      default:
      contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
      }
      }
      catch (ClassNotFoundException ex) {
      ...
      }
      }
      return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
      }
  • ②启动容器
    •     @Override
      public void refresh() throws BeansException, IllegalStateException {
      synchronized (this.startupShutdownMonitor) {
      prepareRefresh();
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context.
      prepareBeanFactory(beanFactory); try {
      ...
      // ①springboot 内嵌tomcat容器
      onRefresh();
      ... } @Override
      protected void onRefresh() {
      super.onRefresh();
      try {
      // ②创建Servlet容器 默认tomcat
      createWebServer();
      }
      ...
      } private void createWebServer() {
      WebServer webServer = this.webServer;
      ServletContext servletContext = getServletContext();
      if (webServer == null && servletContext == null) {
      ServletWebServerFactory factory = getWebServerFactory();
      // ③看进去 回到mvc集成tomcat的场景
      this.webServer = factory.getWebServer(getSelfInitializer());
      }
      ...
      initPropertySources();
      } @Override
      public WebServer getWebServer(ServletContextInitializer... initializers) {
      Tomcat tomcat = new Tomcat();
      File baseDir = (this.baseDirectory != null) ? this.baseDirectory
      : createTempDir("tomcat");
      tomcat.setBaseDir(baseDir.getAbsolutePath());
      Connector connector = new Connector(this.protocol);
      tomcat.getService().addConnector(connector);
      customizeConnector(connector);
      tomcat.setConnector(connector);
      tomcat.getHost().setAutoDeploy(false);
      configureEngine(tomcat.getEngine());
      for (Connector additionalConnector : this.additionalTomcatConnectors) {
      tomcat.getService().addConnector(additionalConnector);
      }
      prepareContext(tomcat.getHost(), initializers);
      return getTomcatWebServer(tomcat);
      }

水稻:好了,第一步和第二步完成了

菜瓜:就这???

水稻:是不是极其简单,令人发指。重头戏是后面的自动装配

  • 回到咱们启动类的注解上
  • ...
    // 标记自身被扫描
    @SpringBootConfiguration
    // 下一步 - 自动装配入口
    @EnableAutoConfiguration
    // 扫描bean路径 - 约定是启动类所在的包:所以没事别把启动类挪走(都是泪)
    @ComponentScan(excludeFilters = {
    @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
    @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
    public @interface SpringBootApplication ->
    @Import(AutoConfigurationImportSelector.class)
    public @interface EnableAutoConfiguration -> 重要
    public class AutoConfigurationImportSelector ... {
    @Override
    public void process(AnnotationMetadata annotationMetadata,
    DeferredImportSelector deferredImportSelector) {
    ...
    // 获取以EnableAutoConfiguration命名的/META-INF/Spring.factories文件中的value去重
    AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector).getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);
    // 启动的时候断点可以看到
    this.autoConfigurationEntries.add(autoConfigurationEntry);
    for (String importClassName : autoConfigurationEntry.getConfigurations()) {
    this.entries.putIfAbsent(importClassName, annotationMetadata);
    }
    }
    }
  • AutoConfigurationImportSelector 中的process是被ConfigurationClassPostProcessor通过processConfigBeanDefinitions方法调用(调用链如下)
    1. this.processConfigBeanDefinitions(registry);
    2. parser.parse(candidates);
    3. this.parse(((AnnotatedBeanDefinition)bd).getMetadata(), holder.getBeanName());
    4. sourceClass = this.doProcessConfigurationClass(configClass, sourceClass);
    5. this.processImports(configClass, sourceClass, this.getImports(sourceClass), true);
    6. this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector)selector);
    7. handler.processGroupImports();
    8. grouping.getImports().forEach...
    9. this.group.process(...);
    --
    搜集到需要自动装配的类,封装成BeanDefinition后续实例化,实现自动装配功能
    譬如引入WebMvcAutoConfiguration类 - webmvc功能自动集成
    org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration 

菜瓜:原来如此。你把调用链拎出来就简单了很多。自动装配就是通过SPI加载org.springframework.boot.autoconfigure包下的class,封装成BeanDefinition后交给容器加载

总结:SpringBoot只需要一行代码便能启动一个Java应用。完全解放开发者复杂的配置

  • 内嵌Servlet容器,默认tomcat
  • 启动SpringWeb容器
  • 自动装配了简单web应用需要的工具和组建

【Spring】原来SpringBoot是这样玩的的更多相关文章

  1. Spring及SpringBoot @Async配置步骤及注意事项

    前言 最近在做一个用户反馈的功能,就是当用户反馈意见或建议后服务端将意见保存然后发邮件给相关模块的开发者.考虑发邮件耗时的情况所以我想用异步的方法去执行,于是就开始研究Spring的@Async了.但 ...

  2. spring springMvc spring-boot spring-cloud分别是什么

    本文来源于:克己习礼成仁   的<spring springMvc spring-boot spring-cloud分别是什么> 什么是spring 关于spring的定义无论是从官方还是 ...

  3. Spring和SpringBoot比较,解惑区别

    1.概述: 对于Spring和SpringBoot到底有什么区别,我听到了很多答案,刚开始迈入学习SpringBoot的我当时也是一头雾水,随着经验的积累.我慢慢理解了这两个框架到底有什么区别,我相信 ...

  4. spring 或 springboot统一异常处理

    spring 或 springboot统一异常处理https://blog.csdn.net/xzmeasy/article/details/76150370 一,本文介绍spring MVC的自定义 ...

  5. 品Spring:SpringBoot和Spring到底有没有本质的不同?

    现在的Spring相关开发都是基于SpringBoot的. 最后在打包时可以把所有依赖的jar包都打进去,构成一个独立的可执行的jar包.如下图13: 使用java -jar命令就可以运行这个独立的j ...

  6. 品Spring:SpringBoot轻松取胜bean定义注册的“第一阶段”

    上一篇文章强调了bean定义注册占Spring应用的半壁江山.而且详细介绍了两个重量级的注册bean定义的类. 今天就以SpringBoot为例,来看看整个SpringBoot应用的bean定义是如何 ...

  7. 品Spring:SpringBoot发起bean定义注册的“二次攻坚战”

    上一篇文章整体非常轻松,因为在容器启动前,只注册了一个bean定义,就是SpringBoot的主类. OK,今天接着从容器的启动入手,找出剩余所有的bean定义的注册过程. 具体细节肯定会颇为复杂,同 ...

  8. Spring SpringMVC SpringBoot SpringCloud 注解整理大全

    Spring SpringMVC SpringBoot SpringCloud 注解整理 才开的博客所以放了一篇以前整理的文档,如果有需要添加修改的地方欢迎指正,我会修改的φ(๑˃∀˂๑)♪ Spri ...

  9. Spring 与 SpringBoot 的区别

    概述 Spring 与 SpringBoot 有什么区别???梳理一下 Spring 和 SpringBoot 到底有什么区别,从 Spring 和 SpringBoot 两方面入手. Spring ...

随机推荐

  1. Java并发编程 (六) 线程安全策略

    个人博客网:https://wushaopei.github.io/    (你想要这里多有) 一.不可变对象-1 有一种安全的发布对象,即不可变对象. 1.不可变对象需要满足的条件 ① 对象创建以后 ...

  2. Java实现 LeetCode 111 二叉树的最小深度

    111. 二叉树的最小深度 给定一个二叉树,找出其最小深度. 最小深度是从根节点到最近叶子节点的最短路径上的节点数量. 说明: 叶子节点是指没有子节点的节点. 示例: 给定二叉树 [3,9,20,nu ...

  3. Java实现 LeetCode 44 通配符匹配

    44. 通配符匹配 给定一个字符串 (s) 和一个字符模式 § ,实现一个支持 '?' 和 '*' 的通配符匹配. '?' 可以匹配任何单个字符. '*' 可以匹配任意字符串(包括空字符串). 两个字 ...

  4. Java实现台阶问题

    1 问题描述 一个台阶总共有n级,如果一次可以跳1级,也可以跳2级,求总共有多少种跳法. 2 解决方案 2.1 递归法 如果整个台阶只有1级,则显然只有一种跳法.如果台阶有2级,则有两种跳法:一种是分 ...

  5. 基于Nginx实现访问控制、连接限制

    0 前言 Nginx自带的模块支持对并发请求数进行限制, 还有对请求来源进行限制.可以用来防止DDOS攻击.阅读本文须知道nginx的配置文件结构和语法. 1. 默认配置语法 nginx.conf作为 ...

  6. 嵌入式Linux学习笔记(六) 上位机QT界面实现和串口通讯实现

    目录 (1).参考资料 (2).QT界面布局实现 (3).数据和操作逻辑 在上一章我们实现了下位机的协议制定,并通过串口通讯工具完成了对设备内外设(LED)的状态修改,下面就要进行上位机软件的实现了( ...

  7. SwiftUI - iOS10本地推送通知教程UserNotifications在Swift中的实现方式

    简介 消息推送相信在很多人的眼里都不陌生了吧?像即时聊天微信,好友发信息给你时会在顶部弹下小窗口提醒你.也像是在影院APP预订了电影票,在开场前一小时你也会收到提醒.这类推送是需要经过后端发送请求的, ...

  8. Java 多线程基础(四)线程安全

    Java 多线程基础(四)线程安全 在多线程环境下,如果有多个线程在同时运行,而这些线程可能会同时运行这段代码.程序每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线 ...

  9. MySQL触发器的详细教学与综合分析

    所有知识体系文章,GitHub已收录,欢迎老板们前来Star! GitHub地址: https://github.com/Ziphtracks/JavaLearningmanual MySQL触发器 ...

  10. 关于wifi营销的看过来

    亲测可用.对于一个开发者来说,终于如获至宝.详情联系qq2455994690.源码可二开.包括微信一键关注上网,手机验证码上网.