springboot源码分析

Easter79
• 阅读 422

springboot源码分析

一:SpringBoot

1、SpringBoot三大特性

1、帮助开发者快速整合第三方框架(原理Maven依赖封装)

2、内嵌服务器(原理Java语言创建服务器)

3、完全注解形式替代XML(原理包装Spring体系注解)spring-boot-starter-web 整合Spring,SpringMVC

2、SpringBoot与SpringCloud概念

SpringCloud的RPC远程调用依赖SpringMVC编写接口(Http+json)

SpringCloud是微服务一站式解决方案,基于SpringBoot之上搭建起来的

3、常用注解归纳

@EnableAutoConfiguration:启动SpringMVC,启动时,扫包范围当前包下

@ComponentScan:启动时扫包范围

@Configuration:标识当前类为配置类,结合@Bean注入bean

@SpringBootApplication:整合前面三个注解,扫包范围当前同级包及子包

4、SpringBoot整合多数据源

1.分包名(推荐使用)

2.注解形式:

@EnableTransactionManager注解默认开启

多数据源分布式事务问题产生在同一个项目中,有多个不同的数据库连接( jta+automic )两阶段提交协议。将数据源统一交给全局xa事务管理

5、全局捕获异常

@ControllerAdvice:标识为异常切面类

@ExceptionHandler(XXX.class):拦截异常(异常类型.class)

6、多环境版本

本地开发,测试环境,预生产环境,生产环境...

application.yml:指定读取的环境:

    spring:  
        profiles:    
            active: dev #默认为开发环境

二、SpringBoot源码分析

1、自定义starter

@Configuration:等同于xml配置,结合@Bean使用

自定义starter

1.引入autoconfiguration依赖:自动注入

2.META-INF/spring.factories:配置EnableAutoConfiguration=自定义configuration

3.引入process依赖,编写配置文件有提示

4.打入maven仓库

2、源码分析

首先是项目启动类:

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

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

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

一:创建SpringApplication对象过程:new SpringApplication(primarySources)

public SpringApplication(Class<?>... primarySources) {
        this((ResourceLoader)null, primarySources);
    }

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
     this.sources = new LinkedHashSet();
     this.bannerMode = Mode.CONSOLE;
     this.logStartupInfo = true;
     this.addCommandLineProperties = true;
     this.addConversionService = true;
     this.headless = true;
     this.registerShutdownHook = true;
     this.additionalProfiles = new HashSet();
     this.isCustomEnvironment = false;
     this.resourceLoader = resourceLoader;
     Assert.notNull(primarySources, "PrimarySources must not be null");
     this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
     //程序进入这里,选择启动方式
     this.webApplicationType = WebApplicationType.deduceFromClasspath();
     this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
     this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
     this.mainApplicationClass = this.deduceMainApplicationClass();
 }

二:this.webApplicationType = WebApplicationType.deduceFromClasspath();

static WebApplicationType deduceFromClasspath() {
        if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", (ClassLoader)null) && !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", (ClassLoader)null) && !ClassUtils.isPresent("org.glassfish.jersey.servlet.ServletContainer", (ClassLoader)null)) {
            //1.使用响应式web启动
            return REACTIVE;
        } else {
            String[] var0 = SERVLET_INDICATOR_CLASSES;
            int var1 = var0.length;

            for(int var2 = 0; var2 < var1; ++var2) {
                String className = var0[var2];
                if (!ClassUtils.isPresent(className, (ClassLoader)null)) {
                    //2.不会内嵌web服务器,最终通过外部tomcat服务器运行
                    return NONE;
                }
            }
            //程序分支走到这里
            //3.应用程序基于servlet应用程序,并且嵌入web server服务器
            return SERVLET;
        }
    }

//将spring上下文相关类注入到spring容器中
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));

springboot源码分析

//将spring监听相关类注入到spring容器中
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));

springboot源码分析

this.mainApplicationClass = this.deduceMainApplicationClass();

 //获取启动的class
private Class<?> deduceMainApplicationClass() {
        try {
            StackTraceElement[] stackTrace = (new RuntimeException()).getStackTrace();
            StackTraceElement[] var2 = stackTrace;
            int var3 = stackTrace.length;

            for(int var4 = 0; var4 < var3; ++var4) {
                StackTraceElement stackTraceElement = var2[var4];
                if ("main".equals(stackTraceElement.getMethodName())) {
                    return Class.forName(stackTraceElement.getClassName());
                }
            }
        } catch (ClassNotFoundException var6) {
        }

        return null;
    }

return (new SpringApplication(primarySources)).run(args);

 public ConfigurableApplicationContext run(String... args) {
     
        StopWatch stopWatch = new StopWatch();
         //记录启动开启时间
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
        this.configureHeadlessProperty();
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        listeners.starting();

        Collection exceptionReporters;
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
            this.configureIgnoreBeanInfo(environment);
            //打印控制台输出的banner图
            Banner printedBanner = this.printBanner(environment);
            context = this.createApplicationContext();
            exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            this.refreshContext(context);
            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);
        }
    }

/** * 应用启动入口 */
@SpringBootApplication

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan

@EnableAutoConfiguration

//选择器方式注入到我们的IOC容器
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {

protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        } else {
            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
            //这里拿到配置类109个,最终选择性注册到IOC容器中去
            //META-INF/spring.factories下的EnableAutoConfiguration下的109个类,如果引入了,就会加载第三方配置的启动类
            //加载DispatcherServletAutoConfiguration
            //加载ServletWebServerFactoryAutoConfiguration
            List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
            configurations = this.removeDuplicates(configurations);
            Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
            this.checkExcludedClasses(configurations, exclusions);
            configurations.removeAll(exclusions);
            configurations = this.filter(configurations, autoConfigurationMetadata);
            this.fireAutoConfigurationImportEvents(configurations, exclusions);
            return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
        }
    }

springboot源码分析

ServletWebServerFactoryAutoConfiguration

 @Bean
    public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties) {
        return new ServletWebServerFactoryCustomizer(serverProperties);
    }

  public ServletWebServerFactoryCustomizer(ServerProperties serverProperties) {
        this.serverProperties = serverProperties;
    }

@ConfigurationProperties(
    prefix = "server",
    ignoreUnknownFields = true
)
public class ServerProperties {
    private Integer port;
    private InetAddress address;
    @NestedConfigurationProperty
    private final ErrorProperties error = new ErrorProperties();
    private Boolean useForwardHeaders;
    private String serverHeader;
    private DataSize maxHttpHeaderSize = DataSize.ofKilobytes(8L);
    private Duration connectionTimeout;
    @NestedConfigurationProperty
    private Ssl ssl;
    @NestedConfigurationProperty
    private final Compression compression = new Compression();
    @NestedConfigurationProperty
    private final Http2 http2 = new Http2();
    private final ServerProperties.Servlet servlet = new ServerProperties.Servlet();
    private final ServerProperties.Tomcat tomcat = new ServerProperties.Tomcat();
    private final ServerProperties.Jetty jetty = new ServerProperties.Jetty();
    private final ServerProperties.Undertow undertow = new ServerProperties.Undertow();

@Configuration
@AutoConfigureOrder(-2147483648)
@ConditionalOnClass({ServletRequest.class})
@ConditionalOnWebApplication(
    type = Type.SERVLET
)
@EnableConfigurationProperties({ServerProperties.class})
//三个启动配置类,支持三种服务器
//EmbeddedTomcat.class, EmbeddedJetty.class, EmbeddedUndertow.class
@Import({ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, EmbeddedTomcat.class, EmbeddedJetty.class, EmbeddedUndertow.class})
public class ServletWebServerFactoryAutoConfiguration {
    public ServletWebServerFactoryAutoConfiguration() {
    }

    @Bean
    public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties) {
        return new ServletWebServerFactoryCustomizer(serverProperties);
    }

 @Configuration
    @ConditionalOnClass({Servlet.class, Tomcat.class, UpgradeProtocol.class})
    @ConditionalOnMissingBean(
        value = {ServletWebServerFactory.class},
        search = SearchStrategy.CURRENT
    )
    public static class EmbeddedTomcat {
        public EmbeddedTomcat() {
        }

        @Bean
        public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
            return new TomcatServletWebServerFactory();
        }
    }

 public WebServer getWebServer(ServletContextInitializer... initializers) {
         //创建我们的tomcat服务器
        Tomcat tomcat = new Tomcat();
        File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat");
        tomcat.setBaseDir(baseDir.getAbsolutePath());
        Connector connector = new Connector(this.protocol);
        tomcat.getService().addConnector(connector);
        this.customizeConnector(connector);
        tomcat.setConnector(connector);
        tomcat.getHost().setAutoDeploy(false);
        this.configureEngine(tomcat.getEngine());
        Iterator var5 = this.additionalTomcatConnectors.iterator();

        while(var5.hasNext()) {
            Connector additionalConnector = (Connector)var5.next();
            tomcat.getService().addConnector(additionalConnector);
        }

        this.prepareContext(tomcat.getHost(), initializers);
        return this.getTomcatWebServer(tomcat);
    }

DispatcherServletAutoConfiguration

WebMvcProperties

@ConfigurationProperties(
    prefix = "spring.mvc"
)
public class WebMvcProperties {
    private Format messageCodesResolverFormat;
    private Locale locale;
    private WebMvcProperties.LocaleResolver localeResolver;
    private String dateFormat;
    private boolean dispatchTraceRequest;
    private boolean dispatchOptionsRequest;
    private boolean ignoreDefaultModelOnRedirect;
    private boolean throwExceptionIfNoHandlerFound;
    private boolean logResolvedException;
    private String staticPathPattern;
    private final WebMvcProperties.Async async;
    private final WebMvcProperties.Servlet servlet;
    private final WebMvcProperties.View view;
    private final WebMvcProperties.Contentnegotiation contentnegotiation;
    private final WebMvcProperties.Pathmatch pathmatch;


//加载springmvc
@Bean(
            name = {"dispatcherServlet"}
        )
        public DispatcherServlet dispatcherServlet() {
            DispatcherServlet dispatcherServlet = new DispatcherServlet();
            dispatcherServlet.setDispatchOptionsRequest(this.webMvcProperties.isDispatchOptionsRequest());
            dispatcherServlet.setDispatchTraceRequest(this.webMvcProperties.isDispatchTraceRequest());
            dispatcherServlet.setThrowExceptionIfNoHandlerFound(this.webMvcProperties.isThrowExceptionIfNoHandlerFound());
            dispatcherServlet.setEnableLoggingRequestDetails(this.httpProperties.isLogRequestDetails());
            return dispatcherServlet;
        }

三、SpringBoot启动流程分析

//1.创建SpringApplication对象
new SpringApplication(primarySources)
//1.1获取当前启动类型原理:判断当前classpath是否有加载我们的servlet类,返回启动方式,webApplicationType分为三种启动类型:REACTIVE,NONE,SERVLET,默认SERVLET类型启动:嵌入web server服务器启动
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//1.2读取springboot包下的META-INF.spring.factories下的ApplicationContextInitializer装配到集合
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
//读取springboot包下的META-INF.spring.factories下的ApplicationListener装配到
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));

 //2.调用SpringApplication run 实现启动同时返回当前容器的上下文
(new SpringApplication(primarySources)).run(args)

//3.记录springboot启动时间
StopWatch stopWatch = new StopWatch();

//4.读取META-INF/spring.factories下的ApplicationListener装配到集合
SpringApplicationRunListeners listeners = this.getRunListeners(args)

//5.循环调用监听starting方法(监听器初始化操作,做一些回调方法)
listeners.starting();

//6.对参数进赋值
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
//6.1读取配置文件到我们的springboot容器中
listeners.environmentPrepared((ConfigurableEnvironment)environment)
//6.1.1
this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
//6.1.2
this.multicastEvent(event, this.resolveDefaultEventType(event));
//6.1.3
this.invokeListener(listener, event);
//6.1.4
this.doInvokeListener(listener, event);
//6.1.5
listener.onApplicationEvent(event);

springboot源码分析

springboot源码分析

this.onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent)event);

postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());

springboot源码分析

this.addPropertySources(environment, application.getResourceLoader());

//7.读取到配置文件内容,放入springboot容器中
protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
        RandomValuePropertySource.addToEnvironment(environment);
        (new ConfigFileApplicationListener.Loader(environment, resourceLoader)).load();
    }

this.load((ConfigFileApplicationListener.Profile)null, this::getNegativeProfileFilter, this.addToLoaded(MutablePropertySources::addFirst, true));

names.forEach((name) -> {    this.load(location, name, profile, filterFactory, consumer);});

this.load(loader, location, profile, filterFactory.getDocumentFilter(profile), consumer);

locations.addAll(this.asResolvedSet(ConfigFileApplicationListener.this.searchLocations, "classpath:/,classpath:/config/,file:./,file:./config/"));

//8.打印banner图
Banner printedBanner = this.printBanner(environment);

//9.创建SpringBoot上下文AnnotationConfigServletWebServerApplicationContext
context = this.createApplicationContext();
case SERVLET:contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");

 this.refreshContext(context);
  ((AbstractApplicationContext)applicationContext).refresh();
//10.走spring的刷新方法
 public void refresh() throws BeansException, IllegalStateException {
        synchronized(this.startupShutdownMonitor) {
            this.prepareRefresh();
            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
            this.prepareBeanFactory(beanFactory);

            try {
                this.postProcessBeanFactory(beanFactory);
                this.invokeBeanFactoryPostProcessors(beanFactory);
                this.registerBeanPostProcessors(beanFactory);
                this.initMessageSource();
                this.initApplicationEventMulticaster();
                this.onRefresh();
                this.registerListeners();
                this.finishBeanFactoryInitialization(beanFactory);
                this.finishRefresh();
            } catch (BeansException var9) {
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
                }

                this.destroyBeans();
                this.cancelRefresh(var9);
                throw var9;
            } finally {
                this.resetCommonCaches();
            }

        }
    }

//11.开始创建web server服务器

//12.加载springmvc

//13.空方法回调
this.afterRefresh(context, applicationArguments);

//14.开始使用广播和回调机制通知监听器SpringBoot容器启动成功
listeners.started(context);

//15.开始使用广播和回调机制开始运行项目
listeners.running(context);

//16.返回当前上下文
return context;
点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Stella981 Stella981
2年前
Spring5.0源码深度解析之理解Configuration注解
!(https://gss1.bdstatic.com/vo3dSag_xI4khGkpoWK1HF6hhy/baike/w%3D268%3Bg%3D0/sign0c730b84bd19ebc4c078719fba1da8c1/37d12f2eb9389b503a80d4b38b35e5dde6116ed7.jpg)
Stella981 Stella981
2年前
Spring5.0源码深度解析之Spring核心注解
!(https://gss1.bdstatic.com/vo3dSag_xI4khGkpoWK1HF6hhy/baike/w%3D268%3Bg%3D0/sign0c730b84bd19ebc4c078719fba1da8c1/37d12f2eb9389b503a80d4b38b35e5dde6116ed7.jpg)
Stella981 Stella981
2年前
Spring5.0源码深度解析之SpringBean的Aop的使用
!(https://gss1.bdstatic.com/vo3dSag_xI4khGkpoWK1HF6hhy/baike/w%3D268%3Bg%3D0/sign0c730b84bd19ebc4c078719fba1da8c1/37d12f2eb9389b503a80d4b38b35e5dde6116ed7.jpg)
Stella981 Stella981
2年前
Spring5.0源码深度解析之SpringBean的Aop源码分析
!(https://gss1.bdstatic.com/vo3dSag_xI4khGkpoWK1HF6hhy/baike/w%3D268%3Bg%3D0/sign0c730b84bd19ebc4c078719fba1da8c1/37d12f2eb9389b503a80d4b38b35e5dde6116ed7.jpg)SpringAop源码分析
Stella981 Stella981
2年前
Spring5.0源码深度解析之SpringBean声明事务底层实现原理
!(https://gss1.bdstatic.com/vo3dSag_xI4khGkpoWK1HF6hhy/baike/w%3D268%3Bg%3D0/sign0c730b84bd19ebc4c078719fba1da8c1/37d12f2eb9389b503a80d4b38b35e5dde6116ed7.jpg)
Stella981 Stella981
2年前
Spring5.0源码深度解析之SpringBean的Aop通知调用链源码分析
!(https://gss1.bdstatic.com/vo3dSag_xI4khGkpoWK1HF6hhy/baike/w%3D268%3Bg%3D0/sign0c730b84bd19ebc4c078719fba1da8c1/37d12f2eb9389b503a80d4b38b35e5dde6116ed7.jpg)SpringAOP原理探究
Stella981 Stella981
2年前
Spring5.0源码深度解析之SpringBean循环依赖问题解决方案
!(https://gss1.bdstatic.com/vo3dSag_xI4khGkpoWK1HF6hhy/baike/w%3D268%3Bg%3D0/sign0c730b84bd19ebc4c078719fba1da8c1/37d12f2eb9389b503a80d4b38b35e5dde6116ed7.jpg)
Stella981 Stella981
2年前
Spring5.0源码深度解析之容器的功能扩展
!(https://gss1.bdstatic.com/vo3dSag_xI4khGkpoWK1HF6hhy/baike/w%3D268%3Bg%3D0/sign0c730b84bd19ebc4c078719fba1da8c1/37d12f2eb9389b503a80d4b38b35e5dde6116ed7.jpg)版权所有:https:/
Stella981 Stella981
2年前
Spring5.0源码深度解析之SpringBean的生命周期
!(https://gss1.bdstatic.com/vo3dSag_xI4khGkpoWK1HF6hhy/baike/w%3D268%3Bg%3D0/sign0c730b84bd19ebc4c078719fba1da8c1/37d12f2eb9389b503a80d4b38b35e5dde6116ed7.jpg)
Easter79
Easter79
Lv1
今生可爱与温柔,每一样都不能少。
文章
2.8k
粉丝
5
获赞
1.2k