--- title: 个人吐血系列-总结Spring date: 2020-03-29 15:45:57 tags: spring categories: java-interview --- > 个人感觉,Spring这一块,不仅会用,还得知道底层的源码或者是说初始化加载等过程吧,这篇就不介绍如何配置以及各个注解的使用,在这里不是重点。 ## 大纲图 ![Spring常见问题](http://media.dreamcat.ink/uPic/Spring常见问题.png) ### 什么是Spring框架 我们一般说 Spring 框架指的都是 Spring Framework,它是很多模块的集合,使用这些模块可以很方便地协助我们进行开发。这些模块是:核心容器、数据访问/集成,、Web、AOP(面向切面编程)、工具、消息和测试模块。比如:Core Container 中的 Core 组件是Spring 所有组件的核心,Beans 组件和 Context 组件是实现IOC和依赖注入的基础,AOP组件用来实现面向切面编程。 Spring 官网列出的 Spring 的 6 个特征: - **核心技术** :依赖注入(DI),AOP,事件(events),资源,i18n,验证,数据绑定,类型转换,SpEL。 - **测试** :模拟对象,TestContext框架,Spring MVC 测试,WebTestClient。 - **数据访问** :事务,DAO支持,JDBC,ORM,编组XML。 - **Web支持** : Spring MVC和Spring WebFlux Web框架。 - **集成** :远程处理,JMS,JCA,JMX,电子邮件,任务,调度,缓存。 - **语言** :Kotlin,Groovy,动态语言。 ### 列举一些重要的Spring模块 - **Spring Core:** 基础,可以说 Spring 其他所有的功能都需要依赖于该类库。主要提供 IoC 依赖注入功能。 - **Spring Aspects** : 该模块为与AspectJ的集成提供支持。 - **Spring AOP** :提供了面向切面的编程实现。 - **Spring JDBC** : Java数据库连接。 - **Spring JMS** :Java消息服务。 - **Spring ORM** : 用于支持Hibernate等ORM工具。 - **Spring Web** : 为创建Web应用程序提供支持。 - **Spring Test** : 提供了对 JUnit 和 TestNG 测试的支持。 ### @RestController VS Controller #### Controller **`Controller` 返回一个页面** 单独使用 `@Controller` 不加 `@ResponseBody`的话一般使用在要返回一个视图的情况,这种情况属于比较传统的Spring MVC 的应用,对应于前后端不分离的情况。 #### RestController **`@RestController` 返回JSON 或 XML 形式数据** 但`@RestController`只返回对象,对象数据直接以 JSON 或 XML 形式写入 HTTP 响应(Response)中,这种情况属于 RESTful Web服务,这也是目前日常开发所接触的最常用的情况(前后端分离)。 **`@Controller +@ResponseBody` 返回JSON 或 XML 形式数据** 如果你需要在Spring4之前开发 RESTful Web服务的话,你需要使用`@Controller` 并结合`@ResponseBody`注解,也就是说`@Controller` +`@ResponseBody`= `@RestController`(Spring 4 之后新加的注解)。 ### 谈谈SpringIOC和AOP #### IOC IoC(Inverse of Control:控制反转)是一种**设计思想**,就是 **将原本在程序中手动创建对象的控制权,交由Spring框架来管理。** IoC 在其他语言中也有应用,并非 Spring 特有。 **IoC 容器是 Spring 用来实现 IoC 的载体, IoC 容器实际上就是个Map(key,value),Map 中存放的是各种对象。** 将对象之间的相互依赖关系交给 IoC 容器来管理,并由 IoC 容器完成对象的注入。这样可以很大程度上简化应用的开发,把应用从复杂的依赖关系中解放出来。 **IoC 容器就像是一个工厂一样,当我们需要创建一个对象的时候,只需要配置好配置文件/注解即可,完全不用考虑对象是如何被创建出来的。** 在实际项目中一个 Service 类可能有几百甚至上千个类作为它的底层,假如我们需要实例化这个 Service,你可能要每次都要搞清这个 Service 所有底层类的构造函数,这可能会把人逼疯。如果利用 IoC 的话,你只需要配置好,然后在需要的地方引用就行了,这大大增加了项目的可维护性且降低了开发难度。 Spring 时代我们一般通过 XML 文件来配置 Bean,后来开发人员觉得 XML 文件来配置不太好,于是 SpringBoot 注解配置就慢慢开始流行起来。 推荐阅读:https://www.zhihu.com/question/23277575/answer/169698662 ##### ioc容器初始化流程 **可以看到有很多PostProcessors的后置处理器** ```java @Override public void refresh() throws BeansException, IllegalStateException { // 来个锁,不然 refresh() 还没结束,你又来个启动或销毁容器的操作,那不就乱套了嘛 synchronized (this.startupShutdownMonitor) { // 准备工作,记录下容器的启动时间、标记“已启动”状态、处理配置文件中的占位符 prepareRefresh(); // Tell the subclass to refresh the internal bean factory. //获得容器ApplicationContext的子类BeanFactory。步骤如下: //1.如果已经有了BeanFactory就销毁它里面的单例Bean并关闭这个BeanFactory。 //2.创建一个新的BeanFactory。 //3.对这个BeanFactory进行定制(customize),如allowBeanDefinitionOverriding等参数 //4.转载BeanDefinitions(读取配置文件,将xml转换成对应得BeanDefinition) //5.检查是否同时启动了两个BeanFactory。 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // 设置 BeanFactory 的类加载器,添加几个 BeanPostProcessor,手动注册几个特殊的 bean // 这块待会会展开说 prepareBeanFactory(beanFactory); try { // 设置beanFactory的后置处理器 // 具体的子类可以在这步的时候添加一些特殊的 BeanFactoryPostProcessor 的实现类或做点什么事 postProcessBeanFactory(beanFactory); // 调用beanFactory的后置处理器 // 调用 BeanFactoryPostProcessor 各个实现类的 postProcessBeanFactory(factory) 方法 invokeBeanFactoryPostProcessors(beanFactory); // 注册 BeanPostProcessor 的实现类(bean的后置处理器) // 此接口两个方法: postProcessBeforeInitialization 和 postProcessAfterInitialization // 两个方法分别在 Bean 初始化之前和初始化之后得到执行。注意,到这里 Bean 还没初始化 registerBeanPostProcessors(beanFactory); // 初始化当前 ApplicationContext 的 MessageSource,国际化这里就不展开说了,不然没完没了了 initMessageSource(); // 初始化当前 ApplicationContext 的事件广播器,这里也不展开了 initApplicationEventMulticaster(); // 模板方法(钩子方法), // 具体的子类可以在这里初始化一些特殊的 Bean(在初始化 singleton beans 之前) onRefresh(); // 注册事件监听器,监听器需要实现 ApplicationListener 接口。这也不是我们的重点,过 registerListeners(); // 重点,重点,重点 // 初始化所有的 singleton beans //(lazy-init 的除外) finishBeanFactoryInitialization(beanFactory); // 最后,广播事件,ApplicationContext 初始化完成 finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. // 销毁已经初始化的 singleton 的 Beans,以免有些 bean 会一直占用资源 destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // 把异常往外抛 throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } } ``` ##### 大概总结一下 1. Spring容器在启动的时候,先会保存所有注册进来的Bean的定义信息; 1. xml注册bean; 2. 注解注册Bean;@Service、@Component、@Bean、xxx 2. Spring容器会合适的时机创建这些Bean 1. 用到这个bean的时候;利用getBean创建bean;创建好以后保存在容器中; 2. 统一创建剩下所有的bean的时候;finishBeanFactoryInitialization(); 3. 后置处理器;BeanPostProcessor 1. 每一个bean创建完成,都会使用各种后置处理器进行处理;来增强bean的功能;比如 1. AutowiredAnnotationBeanPostProcessor:处理自动注入 2. AnnotationAwareAspectJAutoProxyCreator:来做AOP功能; 3. xxx 4. 事件驱动模型; 1. ApplicationListener;事件监听; 2. ApplicationEventMulticaster;事件派发: 更详细的源码可看 1. [http://dreamcat.ink/2020/01/31/spring-springaop-yuan-ma-fen-xi/ ](http://dreamcat.ink/2020/01/31/spring-springaop-yuan-ma-fen-xi/ ) 2. [ https://javadoop.com/post/spring-ioc](https://javadoop.com/post/spring-ioc]) #### AOP AOP(Aspect-Oriented Programming:面向切面编程)能够将那些与业务无关,**却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来**,便于**减少系统的重复代码**,**降低模块间的耦合度**,并**有利于未来的可拓展性和可维护性**。 **Spring AOP就是基于动态代理的**,如果要代理的对象,实现了某个接口,那么Spring AOP会使用**JDK Proxy**,去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候Spring AOP会使用**Cglib** ,这时候Spring AOP会使用 **Cglib** 生成一个被代理对象的子类来作为代理。 当然你也可以使用 AspectJ ,Spring AOP 已经集成了AspectJ ,AspectJ 应该算的上是 Java 生态系统中最完整的 AOP 框架了。 使用 AOP 之后我们可以把一些通用功能抽象出来,在需要用到的地方直接使用即可,这样大大简化了代码量。我们需要增加新功能时也方便,这样也提高了系统扩展性。日志功能、事务管理等等场景都用到了 AOP 。 ##### Spring AOP 和 AspectJ AOP 有什么区别 **Spring AOP 属于运行时增强,而 AspectJ 是编译时增强。** Spring AOP 基于代理(Proxying),而 AspectJ 基于字节码操作(Bytecode Manipulation) Spring AOP 已经集成了 AspectJ ,AspectJ 应该算的上是 Java 生态系统中最完整的 AOP 框架了。AspectJ 相比于 Spring AOP 功能更加强大,但是 Spring AOP 相对来说更简单, 如果我们的切面比较少,那么两者性能差异不大。但是,当切面太多的话,最好选择 AspectJ ,它比Spring AOP 快很多。 ##### 源码总结 ![AnnotationAwareAspectJAutoProxyCreator](http://media.dreamcat.ink//20200201150935.png) 1. @EnableAspectJAutoProxy 开启AOP功能 2. @EnableAspectJAutoProxy 会给容器中注册一个组件 AnnotationAwareAspectJAutoProxyCreator 3. AnnotationAwareAspectJAutoProxyCreator是一个后置处理器; 4. 容器的创建流程: 1. registerBeanPostProcessors()注册后置处理器;创建AnnotationAwareAspectJAutoProxyCreator对象(**Spring源码**) 2. finishBeanFactoryInitialization()初始化剩下的单实例bean(**Spring源码**) 1. 创建业务逻辑组件和切面组件 2. AnnotationAwareAspectJAutoProxyCreator拦截组件的创建过程 3. 组件创建完之后,判断组件是否需要增强;是->切面的通知方法,包装成增强器(Advisor);给业务逻辑组件创建一个代理对象(cglib); 5. 执行目标方法: 1. 代理对象执行目标方法 2. CglibAopProxy.intercept(); 1. 得到目标方法的拦截器链(增强器包装成拦截器MethodInterceptor) 2. 利用拦截器的链式机制,依次进入每一个拦截器进行执行; 3. 效果: 1. 正常执行:前置通知-》目标方法-》后置通知-》返回通知 2. 出现异常:前置通知-》目标方法-》后置通知-》异常通知 ### Spring Bean #### 作用域 - singleton : 唯一 bean 实例,Spring 中的 bean 默认都是单例的。 - prototype : 每次请求都会创建一个新的 bean 实例。 - request : 每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP request内有效。 - session : 每一次HTTP请求都会产生一个新的 bean,该bean仅在当前 HTTP session 内有效。 - global-session: 全局session作用域,仅仅在基于portlet的web应用中才有意义,Spring5已经没有了。Portlet是能够生成语义代码(例如:HTML)片段的小型Java Web插件。它们基于portlet容器,可以像servlet一样处理HTTP请求。但是,与 servlet 不同,每个 portlet 都有不同的会话 #### Spring的单例有线程安全问题吗 大部分时候我们并没有在系统中使用多线程,所以很少有人会关注这个问题。单例 bean 存在线程问题,主要是因为当多个线程操作同一个对象的时候,对这个对象的非静态成员变量的写操作会存在线程安全问题。 常见的有两种解决办法: 1. 在Bean对象中尽量避免定义可变的成员变量(不太现实)。 2. 在类中定义一个ThreadLocal成员变量,将需要的可变成员变量保存在 ThreadLocal 中(推荐的一种方式)。 #### Component和Bean的区别 1. 作用对象不同: `@Component` 注解作用于类,而`@Bean`注解作用于方法。 2. `@Component`通常是通过类路径扫描来自动侦测以及自动装配到Spring容器中(我们可以使用 `@ComponentScan` 注解定义要扫描的路径从中找出标识了需要装配的类自动装配到 Spring 的 bean 容器中)。`@Bean` 注解通常是我们在标有该注解的方法中定义产生这个 bean,`@Bean`告诉了Spring这是某个类的示例,当我需要用它的时候还给我。 3. `@Bean` 注解比 `Component` 注解的自定义性更强,而且很多地方我们只能通过 `@Bean` 注解来注册bean。比如当我们引用第三方库中的类需要装配到 `Spring`容器时,则只能通过 `@Bean`来实现。 #### 将类声明为bean有哪些 我们一般使用 `@Autowired` 注解自动装配 bean,要想把类标识成可用于 `@Autowired` 注解自动装配的 bean 的类,采用以下注解可实现: - `@Component` :通用的注解,可标注任意类为 `Spring` 组件。如果一个Bean不知道属于哪个层,可以使用`@Component` 注解标注。 - `@Repository` : 对应持久层即 Dao 层,主要用于数据库相关操作。 - `@Service` : 对应服务层,主要涉及一些复杂的逻辑,需要用到 Dao层。 - `@Controller` : 对应 Spring MVC 控制层,主要用户接受用户请求并调用 Service 层返回数据给前端页面。 #### Bean的声明周期 ![bean周期](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-17/5496407.jpg) - Bean 容器找到配置文件中 Spring Bean 的定义。 - Bean 容器利用 Java Reflection API 创建一个Bean的实例。 - 如果涉及到一些属性值 利用 `set()`方法设置一些属性值。 - 如果 Bean 实现了 `BeanNameAware` 接口,调用 `setBeanName()`方法,传入Bean的名字。 - 如果 Bean 实现了 `BeanClassLoaderAware` 接口,调用 `setBeanClassLoader()`方法,传入 `ClassLoader`对象的实例。 - 与上面的类似,如果实现了其他 `*.Aware`接口,就调用相应的方法。 - 如果有和加载这个 Bean 的 Spring 容器相关的 `BeanPostProcessor` 对象,执行`postProcessBeforeInitialization()` 方法 - 如果Bean实现了`InitializingBean`接口,执行`afterPropertiesSet()`方法。 - 如果 Bean 在配置文件中的定义包含 init-method 属性,执行指定的方法。 - 如果有和加载这个 Bean的 Spring 容器相关的 `BeanPostProcessor` 对象,执行`postProcessAfterInitialization()` 方法 - 当要销毁 Bean 的时候,如果 Bean 实现了 `DisposableBean` 接口,执行 `destroy()` 方法。 - 当要销毁 Bean 的时候,如果 Bean 在配置文件中的定义包含 destroy-method 属性,执行指定的方法。 ### SpringMVC MVC 是一种设计模式,Spring MVC 是一款很优秀的 MVC 框架。Spring MVC 可以帮助我们进行更简洁的Web层的开发,并且它天生与 Spring 框架集成。Spring MVC 下我们一般把后端项目分为 Service层(处理业务)、Dao层(数据库操作)、Entity层(实体类)、Controller层(控制层,返回数据给前台页面)。 ![springmvc](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-10-11/60679444.jpg) #### 工作原理 ![springmvc工作原理](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-10-11/49790288.jpg) **流程说明(重要):** 1. 客户端(浏览器)发送请求,直接请求到 `DispatcherServlet`。 2. `DispatcherServlet` 根据请求信息调用 `HandlerMapping`,解析请求对应的 `Handler`。 3. 解析到对应的 `Handler`(也就是我们平常说的 `Controller` 控制器)后,开始由 `HandlerAdapter` 适配器处理。 4. `HandlerAdapter` 会根据 `Handler`来调用真正的处理器开处理请求,并处理相应的业务逻辑。 5. 处理器处理完业务后,会返回一个 `ModelAndView` 对象,`Model` 是返回的数据对象,`View` 是个逻辑上的 `View`。 6. `ViewResolver` 会根据逻辑 `View` 查找实际的 `View`。 7. `DispaterServlet` 把返回的 `Model` 传给 `View`(视图渲染)。 8. 把 `View` 返回给请求者(浏览器) ### Spring都用到了哪些设计模式 - **工厂设计模式** : Spring使用工厂模式通过 `BeanFactory`、`ApplicationContext` 创建 bean 对象。 - **代理设计模式** : Spring AOP 功能的实现。 - **单例设计模式** : Spring 中的 Bean 默认都是单例的。 - **模板方法模式** : Spring 中 `jdbcTemplate`、`hibernateTemplate` 等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。 - **包装器设计模式** : 我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。 - **观察者模式:** Spring 事件驱动模型就是观察者模式很经典的一个应用。 - **适配器模式** :Spring AOP 的增强或通知(Advice)使用到了适配器模式、spring MVC 中也是用到了适配器模式适配`Controller`。 - ...... ### Spring事务 #### 管理事物有几种 - 编程式事务,在代码中硬编码。 - 声明式事务,在配置文件中配置 **声明式事务又分为两种**: 1. 基于XML的声明式事务 2. 基于注解的声明式事务 #### 隔离级别 **隔离级别就跟mysql几乎差不多** #### 源码分析 1. 开启@EnableTransactionManagement 2. 利用TransactionManagementConfigurationSelector给容器中会导入组件 1. AutoProxyRegistrar 2. ProxyTransactionManagementConfiguration 3. AutoProxyRegistrar: 1. 给容器中注册一个 InfrastructureAdvisorAutoProxyCreator 组件; 2. 利用后置处理器机制在对象创建以后,包装对象,返回一个代理对象(增强器),代理对象执行方法利用拦截器链进行调用; 4. ProxyTransactionManagementConfiguration 做了什么? 1. 给容器中注册事务增强器; 1. 事务增强器要用事务注解的信息,AnnotationTransactionAttributeSource解析事务注解 2. 事务拦截器: 1. TransactionInterceptor;保存了事务属性信息,事务管理器; 2. 他是一个 MethodInterceptor;在目标方法执行的时候; 1. 先获取事务相关的属性 2. 再获取PlatformTransactionManager,如果事先没有添加指定任何transactionmanger,最终会从容器中按照类型获取一个PlatformTransactionManager; 3. 执行目标方法 1. 如果异常,获取到事务管理器,利用事务管理回滚操作; 2. 如果正常,利用事务管理器,提交事务 ### Springboot启动流程 #### SpringApplication实例 ```java public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) { this.sources = new LinkedHashSet(); // 1. this.bannerMode = Mode.CONSOLE; this.logStartupInfo = true; this.addCommandLineProperties = true; this.headless = true; this.registerShutdownHook = true; this.additionalProfiles = new HashSet(); this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet(Arrays.asList(primarySources)); this.webApplicationType = this.deduceWebApplicationType(); this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class)); // 3. this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class)); // 4. this.mainApplicationClass = this.deduceMainApplicationClass(); // 5. } ``` - `com.example.helloworld.HelloworldApplication`放入到Set的集合中 - 判断是否为Web环境:存在(javax.servlet.Servlet && org.springframework.web.context.ConfigurableWebApplicationContext )类 - 创建并初始化ApplicationInitializer列表 (spring.factories) - 创建并初始化ApplicationListener列表 (spring.factories) - 初始化主类mainApplicatioClass (DemoApplication) - **总结:上面就是SpringApplication初始化的代码,new SpringApplication()没做啥事情 ,主要加载了META-INF/spring.factories 下面定义的事件监听器接口实现类** #### ConfigurableApplicationContext的run方法 ```java public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); // 1. 创建计时器StopWatch stopWatch.start(); ConfigurableApplicationContext context = null; Collection exceptionReporters = new ArrayList(); this.configureHeadlessProperty(); SpringApplicationRunListeners listeners = this.getRunListeners(args); // 2. 获取SpringApplicationRunListeners并启动 listeners.starting(); // Collection exceptionReporters; try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // 创建ApplicationArguments ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments); // 创建并初始化ConfigurableEnvironment this.configureIgnoreBeanInfo(environment); // Banner printedBanner = this.printBanner(environment); // 打印Banner context = this.createApplicationContext(); // 创建ConfigurableApplicationContext exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context); this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);// 准备ConfigurableApplicationContext this.refreshContext(context); // 刷新ConfigurableApplicationContext,这个refreshContext()加载了bean,还启动了内置web容器,需要细细的去看看 this.afterRefresh(context, applicationArguments); // 容器刷新后动作,啥都没做 stopWatch.stop();// 计时器停止计时 if (this.logStartupInfo) { (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch); } listeners.started(context); this.callRunners(context, applicationArguments); } catch (Throwable var10) { this.handleRunFailure(context, var10, exceptionReporters, listeners); throw new IllegalStateException(var10); } try { listeners.running(context); return context; } catch (Throwable var9) { this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null); throw new IllegalStateException(var9); } } ``` - 创建计时器StopWatch - 获取SpringApplicationRunListeners并启动 - 创建ApplicationArguments - 创建并初始化ConfigurableEnvironment - 打印Banner - 创建ConfigurableApplicationContext - 准备ConfigurableApplicationContext - 刷新ConfigurableApplicationContext,**这个refreshContext()加载了bean,还启动了内置web容器,需要细细的去看看** - 容器刷新后动作,啥都没做 - 计时器停止计时 #### refreshContext() **该源码中其实就是Spring源码的refresh()的源码** - **不过这里的refresh()是在`AbstractApplicationContext`抽象类上** - **其他就不提了,关注点在onrefresh()方法上,但是个空方法,毕竟是抽象类,去找其子类继承的它** - **debug调试可以找到ServletWebServerApplicationContext** #### ServletWebServerApplicationContext ![](http://media.dreamcat.ink//20200203211202.png) - `onRefresh()`->`createWebServer()`->`getWebServerFactory()`,此时已经加载了个web容器 - 可以返回刚才的`createWebServer()`,然后看`factory.getWebServer` ```java public WebServer getWebServer(ServletContextInitializer... initializers) { //tomcat这位大哥出现了 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); } ``` - 内置的Tomcat就出现了 - **总结:run() 方法主要调用了spring容器启动方法扫描配置,加载bean到spring容器中;启动的内置Web容器** #### SpringBootApplication的注解 **主要是三个注解** - @SpringBootConfiguration:允许在上下文中注册额外的bean或导入其他配置类。 - @EnableAutoConfiguration:启用 SpringBoot 的自动配置机制 - @ComponentScan: 扫描常用的注解