三. SpringBoot原理

对于springboot来说,还有两块是比较有意思的,第一就是发现他内置了tomcat,接下来一快就是他对springmvc进行了无缝整合

1. 内嵌tomcat

首先来看下最简单的tomcat集成。

pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>hgy</groupId><artifactId>embed-tomcat</artifactId><version>1.0-SNAPSHOT</version><dependencies><!--Java语言操作tomcat --><dependency><groupId>org.apache.tomcat.embed</groupId><artifactId>tomcat-embed-core</artifactId><version>8.5.11</version></dependency><!-- tomcat对jsp支持 --><dependency><groupId>org.apache.tomcat</groupId><artifactId>tomcat-jasper</artifactId><version>8.5.11</version></dependency></dependencies>
</project>

新建立一个servlet

package len.hgy.servlet;import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;public class IndexServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doPost(req, resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.getWriter().print("this is index... tomcat");}
}

tomcat 整合

package len.hgy;import len.hgy.servlet.IndexServlet;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.startup.Tomcat;public class MyTomcat {private static final int PORT = 8080;private static final String CONTEX_PATH = "/hgy";private static final String SERVLET_NAME = "indexServlet";public static void main(String[] args) throws LifecycleException, InterruptedException {// 创建tomcat服务器Tomcat tomcatServer = new Tomcat();// 指定端口号tomcatServer.setPort(PORT);// 是否设置自动部署tomcatServer.getHost().setAutoDeploy(false);// 创建上下文StandardContext standardContext = new StandardContext();standardContext.setPath(CONTEX_PATH);// 监听上下文standardContext.addLifecycleListener(new Tomcat.FixContextListener());// tomcat容器添加standardContexttomcatServer.getHost().addChild(standardContext);// 创建ServlettomcatServer.addServlet(CONTEX_PATH, SERVLET_NAME, new IndexServlet());// servleturl映射standardContext.addServletMappingDecoded("/index", SERVLET_NAME);tomcatServer.start();System.out.println("tomcat服务器启动成功..");// 异步进行接收请求tomcatServer.getServer().await();}
}

运行main方法,在浏览器输入:

http://localhost:8080/hgy/index

注意: 9.xx版本的并不能直接访问

2. SpringMVC 整合

新增spring依赖

<dependency><groupId>org.springframework</groupId><artifactId>spring-web</artifactId><version>5.0.4.RELEASE</version><scope>compile</scope>
</dependency>
<dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.0.4.RELEASE</version><scope>compile</scope>
</dependency>

UserService

package len.hgy.service;import org.springframework.stereotype.Service;@Service
public class UserService {public String index() {return "springboot 2.0  我正在加载UserService";}
}

RestController

package len.hgy.controller;import len.hgy.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class IndexController {@Autowiredprivate UserService userService;@RequestMapping(value = "/index", produces = "text/html;charset=UTF-8")public String index() {return userService.index();}
}

DispatcherServlet配置类

虽然前面有了service,有了controller,但依然没有把这些组建交给spring,对于springmvc来说,有个DispatcherServlet,这是springmvc的前端控制器,以前是配置在web.xml中。只是现在用的是注解。

package len.hgy.conf;import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;@Configuration
@ComponentScan("len.hgy")
public class RootConfig {
}
package len.hgy.conf;import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
@EnableWebMvc
@ComponentScan(basePackages = {"len.hgy.controller"})
//public class WebConfig extends WebMvcConfigurerAdapter {
public class WebConfig implements WebMvcConfigurer {
}
package len.hgy.conf;import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;@Configuration
public class SpringWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {// 加载根配置信息 spring核心protected Class<?>[] getRootConfigClasses() {return new Class[]{RootConfig.class};}// springmvc 加载 配置信息protected Class<?>[] getServletConfigClasses() {return new Class[]{WebConfig.class};}// springmvc 拦截url映射 拦截所有请求protected String[] getServletMappings() {return new String[]{"/"};}
}

Tomcat集成

TomcatApp

package len.hgy;import java.io.File;
import javax.servlet.ServletException;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.WebResourceRoot;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.webresources.DirResourceSet;
import org.apache.catalina.webresources.StandardRoot;public class TomcatApp {public static void main(String[] args) throws ServletException, LifecycleException {start();}public static void start() throws ServletException, LifecycleException {// 创建Tomcat容器Tomcat tomcatServer = new Tomcat();// 端口号设置tomcatServer.setPort(9090);// 读取项目路径 加载静态资源StandardContext ctx = (StandardContext) tomcatServer.addWebapp("/", new File("src/main").getAbsolutePath());// 禁止重新载入ctx.setReloadable(false);// class文件读取地址File additionWebInfClasses = new File("target/classes");// 创建WebRootWebResourceRoot resources = new StandardRoot(ctx);// tomcat内部读取Class执行resources.addPreResources(new DirResourceSet(resources, "/WEB-INF/classes", additionWebInfClasses.getAbsolutePath(), "/"));tomcatServer.start();// 异步等待请求执行tomcatServer.getServer().await();}
}

启动,在地址栏输入:

http://localhost:9090/index

JSP支持

要支持JSP,回忆学习springmvc中的内容,需要用到一个试图解析器

修改WebConfig

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = {"len.hgy.controller"})
//public class WebConfig extends WebMvcConfigurerAdapter {
public class WebConfig implements WebMvcConfigurer {// 创建SpringMVC视图解析器@Overridepublic void configureViewResolvers(ViewResolverRegistry registry) {InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();viewResolver.setPrefix("/WEB-INF/views/");viewResolver.setSuffix(".jsp");// 可以在JSP页面中通过${}访问beansviewResolver.setExposeContextBeansAsAttributes(true);registry.viewResolver(viewResolver);}
}

新增controller

@Controller
public class UserController {@RequestMapping("/pageIndex")public String pageIndex() {return "pageIndex";}
}

增加JSP

在resources里面新增WEB-INF\views\pageIndex.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>jsp page</title>
</head>
<body>
<h1>这是个jsp页面</h1>
</body>
</html>

重启tomcat ,访问 http://localhost:9090/pageIndex

3. SpringBootTomcat 加载流程

tomcat如何启动的?

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
...
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
...

ServletWebServerFactoryAutoConfiguration 这类里面有个TomcatServletWebServerFactoryCustomizer这个类实现了WebServerFactoryCustomizer

@Configuration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,ServletWebServerFactoryConfiguration.EmbeddedJetty.class,ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {

EmbeddedTomcat->TomcatServletWebServerFactory->TomcatServletWebServerFactory.getWebServer()->getTomcatWebServer->TomcatWebServer->启动tomcat

// Start the server to trigger initialization listeners

this.tomcat.start();

在注入bean时候已经在构造器里面启动了tomcat

getWebServer谁调用的?

SpringApplication.run(App.class, args);

org.springframework.boot.SpringApplication#refreshContext

org.springframework.context.support.AbstractApplicationContext#refresh (Spring启动的核心方法)

org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#onRefresh

org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#createWebServer

org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory#getWebServer

大致分析:

​ 更具classpath中的class分析出当前的项目类型是REACTIVE, SERVELET, NONE, 如果是SERVLET类型创建ServletWebServerApplicationContext的Context, 在AbstractApplicationContext的onRefresh()的钩子方法调用ServletWebServerApplicationContext的onRefresh()方法调用createWebServer(),最终调用如下代码和Tomcat挂钩

if (webServer == null && servletContext == null) {ServletWebServerFactory factory = getWebServerFactory();this.webServer = factory.getWebServer(getSelfInitializer());
}

以上是SpringBoot在OnRefresh()中启动Tomcat的逻辑, 其实还是在Spring的refresh()流程中

Tomcat如何初始化Spring呢?

只需要在refresh()方法打一个断点就知道了

其中有好几个异步追踪, 说明Tomcat初始化Spring使用的是异步线程方式

main线程

​ tomcatServer.start();

​ server.start();

​ startInternal(); 提交异步任务

异步任务线程1

异步任务线程2

​ listener.contextInitialized(tldEvent);

​ public void contextInitialized(ServletContextEvent event) {

​ this.initWebApplicationContext(event.getServletContext());

​ }

​ configureAndRefreshWebApplicationContext的实例是AnnotationConfigWebApplicationContext

而AnnotationConfigWebApplicationContext的继承结构是

所以在调用refresh()方法就会触发spring初始化的核心逻辑

而此时的onRefresh()钩子方法不会触发再次Tomcat启动, 所以不会出现Tomcat重复启动

@Override
protected void onRefresh() {this.themeSource = UiApplicationContextUtils.initThemeSource(this);
}

4. SpringBoot 启动流程分析

准备工作

ApplicationContextInitializer Context初始化后调用的类

SpringApplicationRunListener SpringBoot运行监听的类

ApplicationRunner/CommandLineRunner 两个几乎可以等价,用于启动后做客户自定义的操作

MyApplicationRunner

@Component
public class MyApplicationRunner implements ApplicationRunner {@Overridepublic void run(ApplicationArguments args) throws Exception {System.out.println("MyApplicationRunner.run()执行了");}
}

MyCommandLineRunner

@Component
public class MyCommandLineRunner implements CommandLineRunner {@Overridepublic void run(String... args) throws Exception {System.out.println("MyCommandLineRunner.run()执行了" + Arrays.asList(args));}
}

MyApplicationContextInitializer

public class MyApplicationContextInitializerimplements ApplicationContextInitializer<ConfigurableApplicationContext> {@Overridepublic void initialize(ConfigurableApplicationContext applicationContext) {System.out.println("MyApplicationContextInitializer.initialize()执行了" + applicationContext);}
}

MySpringApplicationRunListener

public class MySpringApplicationRunListener implements SpringApplicationRunListener {//必须有的构造器public MySpringApplicationRunListener(SpringApplication application, String[] args) {}@Overridepublic void starting() {System.out.println("MySpringApplicationRunListener.starting()执行了");}@Overridepublic void environmentPrepared(ConfigurableEnvironment environment) {System.out.println("MySpringApplicationRunListener.environmentPrepared()执行了");}@Overridepublic void contextPrepared(ConfigurableApplicationContext context) {System.out.println("MySpringApplicationRunListener.contextPrepared()执行了");}@Overridepublic void contextLoaded(ConfigurableApplicationContext context) {System.out.println("MySpringApplicationRunListener.contextLoaded()执行了");}@Overridepublic void started(ConfigurableApplicationContext context) {System.out.println("MySpringApplicationRunListener.started()执行了");}@Overridepublic void running(ConfigurableApplicationContext context) {System.out.println("MySpringApplicationRunListener.running()执行了");}@Overridepublic void failed(ConfigurableApplicationContext context, Throwable exception) {System.out.println("MySpringApplicationRunListener.failed()执行了");}
}

在resources/META-INF/spring.factories增加

org.springframework.context.ApplicationContextInitializer=len.hgy.listener.MyApplicationContextInitializer
org.springframework.boot.SpringApplicationRunListener=len.hgy.listener.MySpringApplicationRunListener

创建SpringApplication

SpringApplication源码

public static ConfigurableApplicationContext run(Class<?> primarySource,String... args) {return run(new Class<?>[] { primarySource }, args);
}

其实SpringBoot启动就着两个步骤,先创建ConfigurableApplicationContext ,然后再调用Run方法。

ConfigurableApplicationContext 是其他方式接入Spring的入口上下文

SpringApplication构造方法源码

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {this.resourceLoader = resourceLoader;Assert.notNull(primarySources, "PrimarySources must not be null");//保存主类this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));//判断当前是什么类型项目, Tomcat启动Spring不需要判断,因为使用Tomcat就说明是SERVLET类型this.webApplicationType = WebApplicationType.deduceFromClasspath();//从类路径下找到META-INF/spring.factories配置的所有 ApplicationContextInitializersetInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));//从类路径下找到META-INF/spring.factories配置的所有ApplicationListenersetListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));this.mainApplicationClass = deduceMainApplicationClass();
}

Run方法

核心方法

public ConfigurableApplicationContext run(String... args) {StopWatch stopWatch = new StopWatch();stopWatch.start();ConfigurableApplicationContext context = null;Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();configureHeadlessProperty();//从类路径下META‐INF/spring.factories,取得 SpringApplicationRunListeners;SpringApplicationRunListeners listeners = getRunListeners(args);//回调所有的获取SpringApplicationRunListener.starting()方法listeners.starting();try {//封装命令行参数ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);//准备环境ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);configureIgnoreBeanInfo(environment);//创回调SpringApplicationRunListener.environmentPrepared();//表示环境准备完成//打印 BannerBanner printedBanner = printBanner(environment);//根据环境创建contextcontext = createApplicationContext();//错误的异常报表exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,new Class[] { ConfigurableApplicationContext.class }, context);//准备上下文环境;//将environment保存到ioc中;//applyInitializers()调用所有的ApplicationContextInitializer的initialize方法//调用所有的SpringApplicationRunListener的contextPrepared();prepareContext(context, environment, listeners, applicationArguments,printedBanner);//SpringApplicationRunListener的contextLoaded//刷新容器//扫描,创建,加载所有组件;refreshContext(context);afterRefresh(context, applicationArguments);stopWatch.stop();if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);}//所有的SpringApplicationRunListener回调started方法listeners.started(context);//获取所有的ApplicationRunner和CommandLineRunner进行调用callRunners(context, applicationArguments);}catch (Throwable ex) {handleRunFailure(context, ex, exceptionReporters, listeners);throw new IllegalStateException(ex);}try {//所有的SpringApplicationRunListener的running();listeners.running(context);}catch (Throwable ex) {handleRunFailure(context, ex, exceptionReporters, null);throw new IllegalStateException(ex);}return context;
}

注意

ApplicationContextInitializer是在构造方法中从spring.factories中获取初始化的

SpringApplicationRunListener是在run中获取的

callRunners(context, applicationArguments); // 会调用所有的ApplicationRunner和CommandLineRunner

private void callRunners(ApplicationContext context, ApplicationArguments args) {List<Object> runners = new ArrayList<>();runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());AnnotationAwareOrderComparator.sort(runners);for (Object runner : new LinkedHashSet<>(runners)) {if (runner instanceof ApplicationRunner) {callRunner((ApplicationRunner) runner, args);}if (runner instanceof CommandLineRunner) {callRunner((CommandLineRunner) runner, args);}}
}

spi的bean不需要加注解就可以导入到Spring中, Runner需要加注解才能导入

总结:

springboot的启动核心步骤就是调用refresh()方法,启动spring核心流程, 调用onRefresh()方法, new Tomcat(),并设置tomcat项目路径,端口等(就是创建一个类似web.xml的对象)

另外springboot还有一个核心功能是自动配置, 接下来看自动配置

1、SPI

SPI 在 springboot 中是去读取 META-INF/spring.factories 目录的配置文件内 容,把配置文件中的类加载到 spring 容器中。这样如果你想把一个类加载到 spring 容器中,也可以采用这种方式来做。把类配置到 spring.factories 配置 文件中即可。

如果你想把一个类加载到 spring 容器中管理有几种方式:

1、通过 xml 的 bean 标签

2、通过加@Component 注解被@ComponentScan 扫描

3、通过在 spring.factories 配置该类

前两者是加载本工程的 bean,扫描本工程的 bean,第三点可以加载第三方定义 的 jar 包中的 bean,毕竟第三方 jar 包的包名跟本工程包名可能不一样,所以 前两个方式扫描不到。

2、创建 springboot 的上下文对象

在这个上下文对象构造函数中把 ConfigurationClassPostProcessor 变成 beanDefinition 对象(核心的ConfigurationClassPostProcessor 是在这个地方编程了BeanDefinition的)

public class AnnotationConfigServletWebServerApplicationContextextends ServletWebServerApplicationContext implements AnnotationConfigRegistry {
...public AnnotationConfigServletWebServerApplicationContext() {this.reader = new AnnotatedBeanDefinitionReader(this);this.scanner = new ClassPathBeanDefinitionScanner(this);}
...
}this.reader = new AnnotatedBeanDefinitionReader(this);
// 注册 ConfigurationClassPostProcessor的BeanDefinition
if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);def.setSource(source);beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
}

3、容器的启动

其实很简单就是调用了上下文对象的 refresh 核心方法:

// 核心方法, 启动Spring容器
refreshContext(context);// 进入AbstractApplicationContext
public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {// Prepare this context for refreshing.prepareRefresh();// 创建BeanFactory和xml的标签解析哦ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// Prepare the bean factory for use in this context.prepareBeanFactory(beanFactory); // 设置一些属性, 不重要try {// 允许在上下文子类中对bean工厂进行后处理, 主要是注册WebApplication的Scopes和Environment的Beans到BeanFactory中, 所以是到beanFactory的后处理
//WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext);
//WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.servletContext, this.servletConfig);postProcessBeanFactory(beanFactory);/** BeanDefinitionRegistryPostProcessor* BeanFactoryPostProcessor* 完成对这两个接口的调用* 即BeanDefinition的注册器的后置处理器和bean工厂的后置处理器调用* 也就是说实现这两个接口的beanDefinition必须最先实例化(只有一部分bean先实例化, 才能带动其他基于这些类的注解等生效)*  /** BeanFactoryPostProcessors to apply on refresh. */
*   private final List<BeanFactoryPostProcessor> beanFactoryPostProcessors = new ArrayList<>();* SpringBoot中在applyInitializers(context);里面添加这个容器* 这些bean不在核心容器里面, 是在一个单独的beanFactoryPostProcessors中* 通过getBean实例化,让后调用后置处理器* currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));* */invokeBeanFactoryPostProcessors(beanFactory); // 是BeanDefinitionRegistry和BeanFactory的后置处理器调用/** 把实现了BeanPostProcessor接口的类实例化,并且加入到BeanFactory中* 所以invokeBeanFactoryPostProcessors和registerBeanPostProcessors都是PostProcessor的后置处理器调用* */registerBeanPostProcessors(beanFactory); // 是Bean的后置处理器调用// Initialize message source for this context.initMessageSource();// Initialize event multicaster for this context.initApplicationEventMulticaster();// Initialize other special beans in specific context subclasses.onRefresh(); // new Tomcat(), 并启动// Check for listener beans and register them.registerListeners();// Instantiate all remaining (non-lazy-init) singletons.finishBeanFactoryInitialization(beanFactory);// Last step: publish corresponding event.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.destroyBeans();// Reset 'active' flag.cancelRefresh(ex);// Propagate exception to caller.throw ex;} finally {// Reset common introspection caches in Spring's core, since we// might not ever need metadata for singleton beans anymore...resetCommonCaches();}}
}

4、内置 tomcat 的启动和部署

Tomcat 的启动在 onRefresh()中:

// org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#onRefresh
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {Tomcat tomcat = new Tomcat(); // hostFile baseDir = (this.baseDirectory != null) ? this.baseDirectory: createTempDir("tomcat");tomcat.setBaseDir(baseDir.getAbsolutePath());Connector connector = new Connector(this.protocol); // connectortomcat.getService().addConnector(connector); // set servicecustomizeConnector(connector);tomcat.setConnector(connector); // set connectortomcat.getHost().setAutoDeploy(false); // 设置非自动部署configureEngine(tomcat.getEngine());for (Connector additionalConnector : this.additionalTomcatConnectors) {tomcat.getService().addConnector(additionalConnector);}prepareContext(tomcat.getHost(), initializers);return getTomcatWebServer(tomcat);
}

SpringBoot启动核心就做了两件是

启动Spring的refresh()方法(refresh())

AbstractApplicationContext启动核心流程中回调ServletWebServerApplicationContext.onRefresh()的钩子方法(onRefresh())

5. SpringBoot的自动配置

为什么要有 springboot 自动配置功能?

在 springboot 项目中,我们可以在业务代码里面用事务注解,用缓存注解,用 mvn 相关的功能等等,但是我们并没有在 springboot 项目把这些功能开启添加 进来,那么为什么我们可以在业务代码中使用这些功能呢?也就是说这些功能如 何跑到 springboot 项目中来的呢?这就是 springboot 的自动配置功能

自动配置功能开启

@SpringBootConfiguration
@EnableAutoConfiguration // SpringBoot自动配置注解
@ComponentScan(excludeFilters = {@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class) // 使用Import导入
public @interface EnableAutoConfiguration {

那这个AutoConfigurationImportSelector如何被调用的?

在refresh()的核心流程中有两个非常重要的后置处理方法

invokeBeanFactoryPostProcessors(beanFactory); // BeanDefinitionRegistry和BeanFactory的后置处理器

// 这两个是对beanFactory的修改(当然, 主要是修改BeanDefinition)

// 继承关系 public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); // BeaDefinitionRegistry
// BeanDefinitionRegistryPostProcessor.postProcessBeanDefinitionRegistry(registry);
invokeBeanFactoryPostProcessors(registryProcessors, beanFactory); // BeanFactory
// BeanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
// BeanDefinitionRegistryPostProcessor调用后将对象添加到BeanFactoryPostProcessor容器中
// 所以BeanDefinitionRegistryPostProcessor容器是BeanFactoryPostProcessor容器的一个自己
// 所有的BeanDefinitionRegistryPostProcessor对象会调用postProcessBeanDefinitionRegistry和postProcessBeanFactory两个方法

registerBeanPostProcessors(beanFactory); // BeanPostProcessor接口的调用, 即bean的后置处理器调用

public interface BeanPostProcessor { // 接口可以看出, bean的后置处理器是对Bean的修改@Nullabledefault Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {return bean; // 默认不修改返回原始bean}@Nullabledefault Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {return bean; // 默认不修改返回原始bean}
}

@Configuration,@Import,@Bean,@ImportResource,@ComponentScan等注解是在ConfigurationClassPostProcessor中完成的

ConfigurationClassPostProcessor的类结构

public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {

理所当然Registry的postProcessBeanDefinitionRegistry后置处理器就是处理这些注解的入口

@Override // 不用忘记了, 这个是refresh核心流程调用过来的invokeBeanFactoryPostProcessor()
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {int registryId = System.identityHashCode(registry);if (this.registriesPostProcessed.contains(registryId)) {throw new IllegalStateException("postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);}if (this.factoriesPostProcessed.contains(registryId)) {throw new IllegalStateException("postProcessBeanFactory already called on this post-processor against " + registry);}this.registriesPostProcessed.add(registryId);processConfigBeanDefinitions(registry); // 处理配置beanDefinition, 处理注解的核心逻辑
}
this.deferredImportSelectorHandler.process(); // deferredImportSelector的处理器// ConfigurationClassParser.DeferredImportSelectorHandler#process
public void process() {List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;this.deferredImportSelectors = null;try {if (deferredImports != null) {DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);deferredImports.forEach(handler::register);handler.processGroupImports(); // 处理 group imports逻辑}} finally {this.deferredImportSelectors = new ArrayList<>();}
}// ConfigurationClassParser.DeferredImportSelectorGrouping#getImports
public Iterable<Group.Entry> getImports() {for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {this.group.process(deferredImport.getConfigurationClass().getMetadata(),deferredImport.getImportSelector()); // process调用ImportSelector的接口方法}return this.group.selectImports();
}

AutoConfigurationImportSelector的类继承结构如下

public class AutoConfigurationImportSelector // 是DeferredImportSelector的类implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,BeanFactoryAware, EnvironmentAware, Ordered {

getImports()->this.group.process()->AutoConfigurationImportSelector.AutoConfigurationGroup#process()

->AutoConfigurationImportSelector#getAutoConfigurationEntry()

->AutoConfigurationImportSelector#getCandidateConfigurations()

-> SpringFactoriesLoader.loadFactoryNames() // 此处就是SpringBoot通过SPI获取spring.Factories中bean实现,然后导入里面的类的

public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {String factoryClassName = factoryClass.getName();return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}

然后就是Spring的Import注解的处理逻辑

AutoConfigurationImportSelector实现了 DeferredImportSelector 接口。 这个类的核心功能是通过 SPI 机制收集 EnableAutoConfiguration 为 key 的所有 类,然后通过 ConfigurationClassPostProcessor 这个类调用到该类中的方法, 把收集到的类变成 beanDefinition 对象最终实例化加入到 spring 容器。

# Auto Configure, 一个实现代表导入一个功能
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
...

AutoConfigurationImportSelector 类中两个方法会被 ConfigurationClassPostProcessor 调到:

org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#process

org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#selectImports

在这两个方法中完成了 SPI 类的收集。

ConfigurationClassPostProcessor 类只是把收集到的类变成 beanDefinition 并加入到 spring 容器。

ConfigurationClassPostProcessor 类调用的地方。

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
...if (candidate.isAssignable(ImportSelector.class)) {// Candidate class is an ImportSelector -> delegate to it to determine importsClass<?> candidateClass = candidate.loadClass();// 需要实例化这个候选的beanImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);ParserStrategyUtils.invokeAwareMethods(selector, this.environment, this.resourceLoader, this.registry);if (selector instanceof DeferredImportSelector) { // 看看是不是一个Defer selector(延迟被导入的)this.deferredImportSelectorHandler.handle( // 处理加入configClass, (DeferredImportSelector) selector);} else {String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);processImports(configClass, currentSourceClass, importSourceClasses, false);}}
...
}public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(configClass, importSelector);if (this.deferredImportSelectors == null) {DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();handler.register(holder);handler.processGroupImports(); // 调用处理}else {this.deferredImportSelectors.add(holder);}
}public void processGroupImports() {for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {grouping.getImports().forEach(entry -> { // hereConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());try {processImports(configurationClass, asSourceClass(configurationClass),asSourceClasses(entry.getImportClassName()), false);}catch (BeanDefinitionStoreException ex) {throw ex;}catch (Throwable ex) {throw new BeanDefinitionStoreException("Failed to process import candidates for configuration class [" +configurationClass.getMetadata().getClassName() + "]", ex);}});}
}public Iterable<Group.Entry> getImports() {for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {// 调用 processthis.group.process(deferredImport.getConfigurationClass().getMetadata(),deferredImport.getImportSelector());}return this.group.selectImports(); // selectImports
}

这就是这两个方法的调用地方。

上述就是 EnableAutoConfiguration 为 key 自动配置类的收集过程。有自动配置类的收集并加入到 spring 容器,前面提到的 aop,事务,缓存,mvc 功能就已经导入到 springboot 工程了。

6. AOP 功能的自动配置类

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\

AopAutoConfiguration

这个就是 AOP 功能的自动配置类,如果我们要开启 aop 功能,必须要在 xml 里面加配置或者用注解的方式手动开启 aop 功能、注解方式开启 aop

功能如图:

手动开启 aop 功能的这个注解其实就是往 spring 容器里面加入了 一个支持 aop 功能的入口类。

@Configuration
@ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class,AnnotatedElement.class })
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {@Configuration@EnableAspectJAutoProxy(proxyTargetClass = false)@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = false)public static class JdkDynamicAutoProxyConfiguration {}@Configuration@EnableAspectJAutoProxy(proxyTargetClass = true)@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true)public static class CglibAutoProxyConfiguration {}
}

默认是开启 aop 自动配置功能的,默认是开启了 cglib 的动态代理功能的,并且也加上了EnableAspectJAutoProxy(proxyTargetClass = false) 注解了,加上这个注解就会开启 aop 功能。

EnableAspectJAutoProxy这个注解可以作用在内部类, 并且在SpringBoot的自动配置类中经常出现内部类, 并且这两个方法都是标记作用,没有任何实现逻辑

EnableAspectJAutoProxy说明

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {/*** 指示是否要创建基于子类 (CGLIB) 的代理,而不是创建标准的基于 Java 接口的代理。 默认值为false * true: cglib代理* false: jdk代理, 默认是jdk代理*/boolean proxyTargetClass() default false;/*** 指示代理应该由 AOP 框架作为ThreadLocal公开,以便通过org.springframework.aop.framework.AopContext类进行检索。默    * 认关闭,即不保证AopContext访问将起作用。(暴露ThreadLocal可以访问)*/boolean exposeProxy() default false;}

Configuration说明

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {/*** 该值可能表示对逻辑组件名称的建议,在自动检测到组件的情况下将其转换为 Spring bean。* 显式指定与@Configuration类关联的 Spring bean 定义的名称。如果未指定(常见情况),将自动生成 bean 名称。* 仅当通过组件扫描获取@Configuration类或直接提供给AnnotationConfigApplicationContext时,自定义名称才适用。如果         * @Configuration类注册为传统的 XML bean 定义,则 bean 元素的名称/id 将优先。*/@AliasFor(annotation = Component.class)String value() default "";/*** 指定是否应该代理@Bean方法以强制执行 bean 生命周期行为,例如即使在用户代码中直接调用@Bean方法的情况下也返回共享的单例 * bean 实例。* 此功能需要方法拦截,通过运行时生成的 CGLIB 子类实现,该子类具有配置类及其方法不允许声明final等限制。* 默认值为true ,允许配置类中的“bean 间引用”以及对此配置的@Bean方法的外部调用,例如从另一个配置类。* 如果不需要这样做,因为每个特定配置的@Bean方法都是自包含的,并且设计为容器使用的普通工厂方法,请将此标志切换为false以避免 * CGLIB 子类处理。*/boolean proxyBeanMethods() default true; // 代理@Bean的方法, 默认代理且代理都是通过CGLIB代理实现, 这里没有使用JDK的选项, 这里和EnableAspectJAutoProxy#proxyTargetClass的相同,true表示使用cglib, 只不过这里的false不使用代理, 默认比是true不是false}

7. Condition 功能的实现

Condition 功能的使用很简单 使用的需求是:有的时候我们需要当某一个条件满足的时候才把一些类实例化并 加入到 spring 容器中。

@ConditionalOnBean: 当 spring 容器中存在 jack 这个 bean 时,才调用该方法

@Bean
@ConditionalOnBean(name = "order")
public User CglibAutoProxyConfiguration {return new User();
}

其他注解类似

@ConditionalOnClass

@ConditionalOnMissingBean

@ConditionalOnMissingClass

@ConditionalOnExpression( " ${spring.datasource.max-idle} == 10")

@ConditionalOnProperty(perfix = “spring.redis”, name = “host”, havingValue = “192.168.10.10”)

自定义 condition

public class CustomCondition implements Condition {@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {System.out.println("=====CustomCondition.matches======");String property = context.getEnvironment().getProperty("spring.redis.jedis.pool.max-active");return !"8".equals(property);}
}

当自定义 CustomCondition 中的 matches 方法返回 true 时才会调用到该方法。 @Conditional 注解作用在类上面也是一样的使用方式

@Component
@Conditional(value = CustomCondition.class)
public class CustomConditionBean {
}

Condition 的原理和源码

从 condition 的使用需求我们知道,这个是单条件满足的时候才实例化 bean 和 加入到 spring 容器,而在 spring 中一个类的实例化必须要变成 beanDefinition 对象。而 ConfigurationClassPostProcessor 是所有 beanDefinition 对象的集散地,所有的 beanDefinition 都会在这个类里面处理。那么我们要完成 Condition 功能也必定在这个类里面。

ConfigurationClassParser类中的 shouldSkip 方法就是做 bean 过滤 的。

protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {return;}...
}
for (Condition condition : conditions) {ConfigurationPhase requiredPhase = null;if (condition instanceof ConfigurationCondition) {requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();}// matches方法,是核心方法if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) {return true;}
}

SpringBootCondition#matches(ConditionContext, AnnotatedTypeMetadata)

public final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {String classOrMethodName = getClassOrMethodName(metadata);try {// 模板方法,不同注解调用实现不一样ConditionOutcome outcome = getMatchOutcome(context, metadata);logOutcome(classOrMethodName, outcome);recordEvaluation(context, classOrMethodName, outcome);return outcome.isMatch();}catch (NoClassDefFoundError ex) {throw new IllegalStateException("...", ex);}catch (RuntimeException ex) {throw new IllegalStateException("Error processing condition on " + getName(metadata), ex);}
}

这里看两个注解的实现

org.springframework.boot.autoconfigure.condition.ConditionalOnBean

对应的注解实现是: org.springframework.boot.autoconfigure.condition.OnBeanCondition

Bean 存在时才掉用方法,这个其实很好理解,判断 bean 是否存在其实就只要从 BeanFactory 中找就行了,源码里面就是从 BeanFactory 中找。

@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {ConditionMessage matchMessage = ConditionMessage.empty();MergedAnnotations annotations = metadata.getAnnotations();if (annotations.isPresent(ConditionalOnBean.class)) {Spec<ConditionalOnBean> spec = new Spec<>(context, metadata, annotations, ConditionalOnBean.class);MatchResult matchResult = getMatchingBeans(context, spec); // 核心逻辑if (!matchResult.isAllMatched()) {String reason = createOnBeanNoMatchReason(matchResult);return ConditionOutcome.noMatch(spec.message().because(reason));}matchMessage = spec.message(matchMessage).found("bean", "beans").items(Style.QUOTE,matchResult.getNamesOfAllMatches());}...return ConditionOutcome.match(matchMessage);
}
protected final MatchResult getMatchingBeans(ConditionContext context, Spec<?> spec) {
...MatchResult result = new MatchResult();Set<String> beansIgnoredByType = getNamesOfBeansIgnoredByType(classLoader, beanFactory, considerHierarchy,spec.getIgnoredTypes(), parameterizedContainers);for (String type : spec.getTypes()) {// 更具注解类型从容器中获取beanCollection<String> typeMatches = getBeanNamesForType(classLoader, considerHierarchy, beanFactory, type, parameterizedContainers);typeMatches.removeAll(beansIgnoredByType);if (typeMatches.isEmpty()) {result.recordUnmatchedType(type);}else {result.recordMatchedType(type, typeMatches);}}...return result;
}

从 BeanFactory 中获取对应的实例,如果有则匹配。

org.springframework.boot.autoconfigure.condition.ConditionalOnClass

对应的注解实现是: org.springframework.boot.autoconfigure.condition.OnClassCondition

当工程上下文中存在该类时才调用方法 实现原理,就是通过反射的方式,如果反射有异常则返回 false,如果反射没异 常返回 true

@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {ClassLoader classLoader = context.getClassLoader();ConditionMessage matchMessage = ConditionMessage.empty();List<String> onClasses = getCandidates(metadata, ConditionalOnClass.class);if (onClasses != null) {// 核心逻辑List<String> missing = filter(onClasses, ClassNameFilter.MISSING, classLoader);if (!missing.isEmpty()) {return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnClass.class).didNotFind("required class", "required classes").items(Style.QUOTE, missing));}matchMessage = matchMessage.andCondition(ConditionalOnClass.class).found("required class", "required classes").items(Style.QUOTE, filter(onClasses, ClassNameFilter.PRESENT, classLoader));}List<String> onMissingClasses = getCandidates(metadata, ConditionalOnMissingClass.class);if (onMissingClasses != null) {List<String> present = filter(onMissingClasses, ClassNameFilter.PRESENT, classLoader);if (!present.isEmpty()) {return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnMissingClass.class).found("unwanted class", "unwanted classes").items(Style.QUOTE, present));}matchMessage = matchMessage.andCondition(ConditionalOnMissingClass.class).didNotFind("unwanted class", "unwanted classes").items(Style.QUOTE, filter(onMissingClasses, ClassNameFilter.MISSING, classLoader));}return ConditionOutcome.match(matchMessage);
}
protected final List<String> filter(Collection<String> classNames, ClassNameFilter classNameFilter,ClassLoader classLoader) {if (CollectionUtils.isEmpty(classNames)) {return Collections.emptyList();}List<String> matches = new ArrayList<>(classNames.size());for (String candidate : classNames) {if (classNameFilter.matches(candidate, classLoader)) { // 一个枚举方法,看入参的实现matches.add(candidate);}}return matches;
}
MISSING {@Overridepublic boolean matches(String className, ClassLoader classLoader) {return !isPresent(className, classLoader);}};
static boolean isPresent(String className, ClassLoader classLoader) {if (classLoader == null) {classLoader = ClassUtils.getDefaultClassLoader();}try {resolve(className, classLoader); // 看是否有异常return true;}catch (Throwable ex) {return false;}
}

8. Redis 自动配置

自动配置类

org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\

为什么可以在程序里面直接依赖注入 RedisTemplate,在自动配置类创建了该实

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisOperations.class) // 更具class决定是否引入该功能
@EnableConfigurationProperties(RedisProperties.class) // 开启配置属性的java bean
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class }) // 导入功能相关类
public class RedisAutoConfiguration {@Bean@ConditionalOnMissingBean(name = "redisTemplate") // 没有bean时候才注入public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)throws UnknownHostException {RedisTemplate<Object, Object> template = new RedisTemplate<>();template.setConnectionFactory(redisConnectionFactory);return template; // 我们使用的RedisTemplate就是通过@Bean方式注入的, 所以@Bean注解在SpringBoot中使用非常频繁}@Bean // 同上@ConditionalOnMissingBeanpublic StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory)throws UnknownHostException {StringRedisTemplate template = new StringRedisTemplate();template.setConnectionFactory(redisConnectionFactory);return template;}}

9. 数据源自动配置

自动配置类

org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@EnableConfigurationProperties(DataSourceProperties.class)
// 导入公共类
@Import({ DataSourcePoolMetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.class })
public class DataSourceAutoConfiguration {@Configuration(proxyBeanMethods = false)@Conditional(EmbeddedDatabaseCondition.class)@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })@Import(EmbeddedDataSourceConfiguration.class) // 导入需要的类protected static class EmbeddedDatabaseConfiguration {}@Configuration(proxyBeanMethods = false)@Conditional(PooledDataSourceCondition.class)@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })@Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class,DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.Generic.class,DataSourceJmxConfiguration.class })protected static class PooledDataSourceConfiguration {}/*** {@link AnyNestedCondition} that checks that either {@code spring.datasource.type}* is set or {@link PooledDataSourceAvailableCondition} applies.*/static class PooledDataSourceCondition extends AnyNestedCondition {PooledDataSourceCondition() {super(ConfigurationPhase.PARSE_CONFIGURATION);}@ConditionalOnProperty(prefix = "spring.datasource", name = "type")static class ExplicitType {}@Conditional(PooledDataSourceAvailableCondition.class)static class PooledDataSourceAvailable {}}/*** {@link Condition} to test if a supported connection pool is available.*/static class PooledDataSourceAvailableCondition extends SpringBootCondition {@Overridepublic ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {ConditionMessage.Builder message = ConditionMessage.forCondition("PooledDataSource");if (DataSourceBuilder.findType(context.getClassLoader()) != null) {return ConditionOutcome.match(message.foundExactly("supported DataSource"));}return ConditionOutcome.noMatch(message.didNotFind("supported DataSource").atAll());}}/*** {@link Condition} to detect when an embedded {@link DataSource} type can be used.* If a pooled {@link DataSource} is available, it will always be preferred to an* {@code EmbeddedDatabase}.*/static class EmbeddedDatabaseCondition extends SpringBootCondition {private final SpringBootCondition pooledCondition = new PooledDataSourceCondition();@Overridepublic ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {ConditionMessage.Builder message = ConditionMessage.forCondition("EmbeddedDataSource");if (anyMatches(context, metadata, this.pooledCondition)) {return ConditionOutcome.noMatch(message.foundExactly("supported pooled data source"));}EmbeddedDatabaseType type = EmbeddedDatabaseConnection.get(context.getClassLoader()).getType();if (type == null) {return ConditionOutcome.noMatch(message.didNotFind("embedded database").atAll());}return ConditionOutcome.match(message.found("embedded database").items(type));}}}

默认是有 Hikari 数据源对象的。

// org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration.Hikari
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(HikariDataSource.class)
@ConditionalOnMissingBean(DataSource.class)
@ConditionalOnProperty(name = "spring.datasource.type", havingValue = "com.zaxxer.hikari.HikariDataSource",matchIfMissing = true)
static class Hikari {@Bean@ConfigurationProperties(prefix = "spring.datasource.hikari")HikariDataSource dataSource(DataSourceProperties properties) {HikariDataSource dataSource = createDataSource(properties, HikariDataSource.class);if (StringUtils.hasText(properties.getName())) {dataSource.setPoolName(properties.getName());}return dataSource;}}

10. JdbcTemplate 自动配置

org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ DataSource.class, JdbcTemplate.class })
@ConditionalOnSingleCandidate(DataSource.class)
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
@EnableConfigurationProperties(JdbcProperties.class)
@Import({ JdbcTemplateConfiguration.class, NamedParameterJdbcTemplateConfiguration.class })
public class JdbcTemplateAutoConfiguration {}

JdbcTemplateConfiguration配置JdbcTemplate的bean

@Configuration(proxyBeanMethods = false) // false, 说明,每次调用都会原始方法,即不代理原始方法
@ConditionalOnMissingBean(JdbcOperations.class)
class JdbcTemplateConfiguration {@Bean@PrimaryJdbcTemplate jdbcTemplate(DataSource dataSource, JdbcProperties properties) {JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);JdbcProperties.Template template = properties.getTemplate();jdbcTemplate.setFetchSize(template.getFetchSize());jdbcTemplate.setMaxRows(template.getMaxRows());if (template.getQueryTimeout() != null) {jdbcTemplate.setQueryTimeout((int) template.getQueryTimeout().getSeconds());}return jdbcTemplate;}}

11. 事务管理器自动配置

org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ JdbcTemplate.class, PlatformTransactionManager.class })
@AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE)
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceTransactionManagerAutoConfiguration {@Configuration(proxyBeanMethods = false) // 不使用代理(CGLIB代理:true), 即每次都调用原始的@Bean方法@ConditionalOnSingleCandidate(DataSource.class)static class DataSourceTransactionManagerConfiguration {@Bean@ConditionalOnMissingBean(PlatformTransactionManager.class)DataSourceTransactionManager transactionManager(DataSource dataSource,ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(dataSource);transactionManagerCustomizers.ifAvailable((customizers) -> customizers.customize(transactionManager));return transactionManager;}}}

12. 事务自动配置

org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\

编程式事务

@Configuration(proxyBeanMethods = false)
@ConditionalOnSingleCandidate(PlatformTransactionManager.class)
public static class TransactionTemplateConfiguration {@Bean@ConditionalOnMissingBean(TransactionOperations.class)public TransactionTemplate transactionTemplate(PlatformTransactionManager transactionManager) {return new TransactionTemplate(transactionManager);}}

事务功能导入注解

@Configuration(proxyBeanMethods = false)
@ConditionalOnBean(TransactionManager.class)
@ConditionalOnMissingBean(AbstractTransactionManagementConfiguration.class)
public static class EnableTransactionManagementConfiguration {@Configuration(proxyBeanMethods = false)@EnableTransactionManagement(proxyTargetClass = false) // 开启事务管理器代理@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false",matchIfMissing = false)public static class JdkDynamicAutoProxyConfiguration {}@Configuration(proxyBeanMethods = false)@EnableTransactionManagement(proxyTargetClass = true) // 开启事务管理器代理@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",matchIfMissing = true)public static class CglibAutoProxyConfiguration {}}

13. DispatcherServlet 自动配置

org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\

DispatcherServlet

@Configuration(proxyBeanMethods = false)
@Conditional(DefaultDispatcherServletCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties({ HttpProperties.class, WebMvcProperties.class })
protected static class DispatcherServletConfiguration {@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)public DispatcherServlet dispatcherServlet(HttpProperties httpProperties, WebMvcProperties webMvcProperties) {DispatcherServlet dispatcherServlet = new DispatcherServlet();dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());dispatcherServlet.setEnableLoggingRequestDetails(httpProperties.isLogRequestDetails());return dispatcherServlet;}@Bean@ConditionalOnBean(MultipartResolver.class)@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)public MultipartResolver multipartResolver(MultipartResolver resolver) {// Detect if the user has created a MultipartResolver but named it incorrectlyreturn resolver;}}

DispatcherServletRegistrationBean

@Configuration(proxyBeanMethods = false)
@Conditional(DispatcherServletRegistrationCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties(WebMvcProperties.class)
@Import(DispatcherServletConfiguration.class)
protected static class DispatcherServletRegistrationConfiguration {@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,webMvcProperties.getServlet().getPath());registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());multipartConfig.ifAvailable(registration::setMultipartConfig);return registration;}}

14. MVC 自动配置

org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\

其实 mvc 的自动配置就是把一些支持 mvc 功能的类创建出来 比如:handlerMapping,HandlerAdapter,ViewResolver 等等实例

@Bean
@Override
public RequestMappingHandlerAdapter requestMappingHandlerAdapter( // 映射处理适配器@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,@Qualifier("mvcConversionService") FormattingConversionService conversionService,@Qualifier("mvcValidator") Validator validator) {RequestMappingHandlerAdapter adapter = super.requestMappingHandlerAdapter(contentNegotiationManager,conversionService, validator);adapter.setIgnoreDefaultModelOnRedirect(this.mvcProperties == null || this.mvcProperties.isIgnoreDefaultModelOnRedirect());return adapter;
}@Override
protected RequestMappingHandlerAdapter createRequestMappingHandlerAdapter() { // 被调用的方法if (this.mvcRegistrations != null && this.mvcRegistrations.getRequestMappingHandlerAdapter() != null) {return this.mvcRegistrations.getRequestMappingHandlerAdapter();}return super.createRequestMappingHandlerAdapter();
}@Bean
@Primary
@Override
public RequestMappingHandlerMapping requestMappingHandlerMapping( // 映射处理器@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,@Qualifier("mvcConversionService") FormattingConversionService conversionService,@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {// Must be @Primary for MvcUriComponentsBuilder to workreturn super.requestMappingHandlerMapping(contentNegotiationManager, conversionService,resourceUrlProvider);
}@Bean // 欢迎页面映射
public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext,FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(new TemplateAvailabilityProviders(applicationContext), applicationContext, getWelcomePage(),this.mvcProperties.getStaticPathPattern());welcomePageHandlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider));return welcomePageHandlerMapping;
}private Optional<Resource> getWelcomePage() { // 获取欢迎页面String[] locations = getResourceLocations(this.resourceProperties.getStaticLocations());return Arrays.stream(locations).map(this::getIndexHtml).filter(this::isReadable).findFirst();
}private Resource getIndexHtml(String location) { // 获取默认的索引页面, index.htmlreturn this.resourceLoader.getResource(location + "index.html");
}@Bean
@Override
public FormattingConversionService mvcConversionService() {WebConversionService conversionService = new WebConversionService(this.mvcProperties.getDateFormat());addFormatters(conversionService);return conversionService;
}@Bean
@ConditionalOnMissingBean
public InternalResourceViewResolver defaultViewResolver() { // 内部视图解析器InternalResourceViewResolver resolver = new InternalResourceViewResolver();resolver.setPrefix(this.mvcProperties.getView().getPrefix());resolver.setSuffix(this.mvcProperties.getView().getSuffix());return resolver;
}

三. SpringBoot原理相关推荐

  1. 深入理解 SpringBoot 原理

    官网:Spring Boot 文章目录 1.SpringBoot简介 回顾Spring Spring简化Java开发 什么是SpringBoot SpringBoot的大时代背景 2.HelloWor ...

  2. ASP.NET Core 中文文档 第三章 原理(5)错误处理

    原文:Error Handling 作者:Steve Smith 翻译:谢炀(Kiler) 校对:高嵩(jack2gs).何镇汐 当你的ASP.NET应用发生错误的时候, 你可以采用本文所述的各种方法 ...

  3. ASP.NET Core 中文文档 第三章 原理(3)静态文件处理

    原文:Working with Static Files 作者:Rick Anderson 翻译:刘怡(AlexLEWIS) 校对:谢炀(kiler398).许登洋(Seay).孟帅洋(书缘) 静态文 ...

  4. 计算机网络实验三 ARP原理与ARP欺骗 实验报告

    实验三 ARP原理与ARP欺骗 目录 [实验名称] [实验目的] [实验要求] [实验环境] 5.参考脚本:构建如图所示虚拟实验网络,可参考如下脚本: [实验步骤] 步骤1:创建虚拟实验网络,验证网络 ...

  5. photoshop中RGB三色原理及颜色相加相减

    From: http://blog1.poco.cn/myBlogDetail-htx-id-5921820-userid-55622677-pri--n-0.xhtml 一.RGB三色原理 在中学的 ...

  6. 数据结构之排序【归并排序和快排的顶级优化和快排的三种原理的实现及分析】 内含动态演示图

    文章目录 引言: 1.归并排序(MergeSort) 2.快速排序的优化(顶级优化) 3.快速排序的三种思路的代码实现及分析 4.归并排序和快排第3原理的测试 引言: 刚刚去回顾了一下递归实现的几个小 ...

  7. SpringBoot原理-SpringBoot核心运行原理

    导语   Spring Boot最为核心的功能就是自动配置,所有功能的实现都是基于"约定优于配置"的原则,但是Spring Boot是如何实现自动配置的功能的,下面就通过源码学习S ...

  8. java idle 机制_深入springboot原理——一步步分析springboot启动机制(starter机制)...

    前言 使用过springboot的同学应该已经知道,springboot通过默认配置了很多框架的使用方式帮我们大大简化了项目初始搭建以及开发过程.本文的目的就是一步步分析springboot的启动过程 ...

  9. springboot原理解析

    Java SpringBoot SpringBoot01:Hello,World! 回顾什么是Spring Spring是一个开源框架,2003 年兴起的一个轻量级的Java 开发框架,作者:Rod ...

最新文章

  1. python中数据类型为list_python_数据类型_list
  2. java 滚轮页面缩放_急..JAVA 在画布上画拖动滚动条可扩大缩小的长方形
  3. 网络编程学习笔记(recvfrom和sendto函数)
  4. 16进制颜色透明_PPT | 如何快速提取图片颜色之人生太卷
  5. 重构机房收费系统总结1之配置文件+反射+抽象工厂
  6. u3d 逐个点运动,路径运动。 U3d one by one, path motion.
  7. java教程java自学_15必须阅读Java 8教程
  8. mysql常用命令英文词汇_MySQL中文全文索引插件 mysqlcft 1.0.0 安装使用文档
  9. 人人都能有数字替身:量子动力FACEGOOD发布AI交互实时数字人
  10. linkedblockingqueue使用_学会java这几个线程池的使用,会使得你的程序效率提升十倍...
  11. Atiti  attilax主要成果与解决方案与案例rsm版
  12. 华为android手机root,华为手机怎么root
  13. 在一个MAX里面把多个动画混合在一起
  14. chrome浏览器安装infinity插件
  15. CSR蓝牙遥控器OTA固件升级
  16. java使用免费日期API获取当年节假日
  17. 人与人之间的交往艺术
  18. 美容院如何提升员工敬业度?
  19. 湖南大学的计算机网络,林亚平-湖大信息科学与工程学院
  20. c语言中国象棋ai算法,【中国象棋】AI算法中的棋子价值是怎么衡定的?

热门文章

  1. 数字图像处理个人练习02--点运算、空域平滑、空域锐化
  2. java中setMnemonic方法的使用
  3. 【Prism系列】Prism中的命令
  4. 全国计算机二级考试各个科目使用的软件 在这里
  5. os.path.dirname与__file__的搭配运用
  6. GDB调试显示< optimized_out >
  7. 微信小程序+uni-app知识点总结
  8. Apap图像配准算法
  9. 登陆凯叔讲故事显示未链接服务器,凯叔用户暴涨的秘密
  10. 行列式按行(列)展开定理——6种行列式的展开方式