spring boot启动过程
2017-08-22 17:25
471 查看
spring已经成为实时上的J2EE标准,spring boot并没有提供太多新的特性,而是发现了大部分的模板配置,没必要重复的配置,而且现在脚本语言大行其道,并且微服务的诞生让更多项目的构建和部署,spring这些大量的配置文件带来很多不必要的工作量。
spring boot顾名思义能够自动化的启动一个应用。以spring-boot-starter-web为例,它其实就是引入了一个组合pom.xml。它引入的pom如下:
<!-- spring web 相关jar--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <!-- 内置 tomcat 支持 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </dependency> <!-- 默认orm 采用hibernate --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> </dependency>
spring-boot-starter能够引入spring核心的包,包括core, webmvc, aop, context,orm 等等。默认采用了hibernate去完成JPA相关工作。整个自动化过程主要再spring-boot-*包下面。
我们看下spring-boot-starter主要引入了:
<!-- spring boot 核心支持 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot</artifactId> </dependency> <!-- spring boot默认配置支持 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-autoconfigure</artifactId> </dependency> <!-- 默认日志支持 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <exclusions> <exclusion> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> </exclusion> </exclusions> </dependency> <!-- yaml格式支持 --> <dependency> <groupId>org.yaml</groupId> <artifactId>snakeyaml</artifactId> <scope>runtime</scope> </dependency>
整个自动化启动需要这几个组件。由于篇幅有限,我们通过应用的启动过程来跟踪包的作用。
一般我们应用的入口是SpringApplication,这个在根包下面。
我们启动一个应用如下:
public static void main(String[] args) { new SpringApplication(ApplicationBoot.class).run(args); }
它的构造函数:
/** * Create a new {@link SpringApplication} instance. The application context will load * beans from the specified sources (see {@link SpringApplication class-level} * documentation for details. The instance can be customized before calling * {@link #run(String...)}. * @param sources the bean sources * @see #run(Object, String[]) * @see #SpringApplication(ResourceLoader, Object...) */ public SpringApplication(Object... sources) { initialize(sources); }
构造一个spring应用实例,从指定的源加载。接着调用initialize()初始化整个应用。源可以是一个或者多个。初始化:
private void initialize(Object[] sources) { if (sources != null && sources.length > 0) { this.sources.addAll(Arrays.asList(sources)); } this.webEnvironment = deduceWebEnvironment(); setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class)); // 这里获取所有的ApplicationListener(框架自带或则用户自定义) setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass(); }
这配置初始化的监听器,能够让用户可以跟踪整个应用的加载过程。准备好了就可以调用应用的run(args)函数。
/** * Run the Spring application, creating and refreshing a new * {@link ApplicationContext}. * @param args the application arguments (usually passed from a Java main method) * @return a running {@link ApplicationContext} */ public ConfigurableApplicationContext run(String... args) { // 计时器,能够跟踪应用的运行时间(主要开发环境用) StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; FailureAnalyzers analyzers = null; configureHeadlessProperty(); // 获取所有监听器,能够在应用的启动阶段收到通知(类似观察者模式) SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments( args); ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); // 这里是一个彩蛋,可以在resources文件夹自定义banner.txt来覆盖默认启动的spring LOGO... Banner printedBanner = printBanner(environment); // 反射创建createApplicationContext容器。spring容器核心,注意这里这个ApplicationContext还是一个不能用的容器,等待后面的启动工作 context = createApplicationContext(); analyzers = new FailureAnalyzers(context); // 做容器的初始化工作 prepareContext(context, environment, listeners, applicationArguments, printedBanner); // 这里是spring容器启动 refreshContext(context); afterRefresh(context, applicationArguments); listeners.finished(context, null); stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass) .logStarted(getApplicationLog(), stopWatch); } return context; } catch (Throwable ex) { handleRunFailure(context, listeners, analyzers, ex); throw new IllegalStateException(ex); } }
上面有两个过程是比较重要的,一个是prepareContext(context, environment, listeners, applicationArguments,printedBanner);一个是refreshContext(context);
对spring加载过程熟悉的读者肯定很熟悉refresh()函数。这个函数定义了整个容器加载的流程,其中包含一些模板方法,但是整个流程的骨架已经清晰可见。我们还是先讲讲prepareContext
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { context.setEnvironment(environment); // 这里能够干预容器的加载,这是一个protected函数,意思就是可以继承并重写这个函数,如果有必要,你可以在这里做任何容器加载前的事情 postProcessApplicationContext(context); // 调用定义的初始化类,这个在前面的initialize设置的 applyInitializers(context); // 通知监听器 listeners.contextPrepared(context); if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } context.getBeanFactory().registerSingleton("springApplicationArguments", applicationArguments); if (printedBanner != null) { context.getBeanFactory().registerSingleton("springBootBanner", printedBanner); } // Load the sources Set<Object> sources = getSources(); Assert.notEmpty(sources, "Sources must not be empty"); // 加载bean读取组件,让应用能够读取xml javaConfig等不同形式定义的bean load(context, sources.toArray(new Object[sources.size()])); // 加载完毕通知监听器 listeners.contextLoaded(context); }
接下来开始加载bean并完成容器的启动工作:refreshContext(context); 可以参考博主另一文章:容器加载
好了,然后整个过程结束,等等,说好的spring boot呢,请不要急。我们一般在source,也就是启动应用的类前面加一个@SpringBootApplication,打开它的源码:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication { // ... }
它是一个组合注解,还包含了EnableAutoConfiguration。它的字面意思就是自动配置。我很想把它的JavaDoc放出来:
/**
* Enable auto-configuration of the Spring Application Context, attempting to guess and
* configure beans that you are likely to need. Auto-configuration classes are usually
* applied based on your classpath and what beans you have defined. For example, If you
* have {@code tomcat-embedded.jar} on your classpath you are likely to want a
* {@link TomcatEmbeddedServletContainerFactory} (unless you have defined your own
* {@link EmbeddedServletContainerFactory} bean).
*
* Auto-configuration tries to be as intelligent as possible and will back-away as you
* define more of your own configuration. You can always manually {@link #exclude()} any
* configuration that you never want to apply (use {@link #excludeName()} if you don’t
* have access to them). You can also exclude them via the
* {@code spring.autoconfigure.exclude} property. Auto-configuration is always applied
* after user-defined beans have been registered.
*
* The package of the class that is annotated with {@code @EnableAutoConfiguration} has
* specific significance and is often used as a ‘default’. For example, it will be used
* when scanning for {@code @Entity} classes. It is generally recommended that you place
* {@code @EnableAutoConfiguration} in a root package so that all sub-packages and classes
* can be searched.
*
* Auto-configuration classes are regular Spring {@link Configuration} beans. They are
* located using the {@link SpringFactoriesLoader} mechanism (keyed against this class).
* Generally auto-configuration beans are {@link Conditional @Conditional} beans (most
* often using {@link ConditionalOnClass @ConditionalOnClass} and
* {@link ConditionalOnMissingBean @ConditionalOnMissingBean} annotations).
*
* @author Phillip Webb
* @author Stephane Nicoll
* @see ConditionalOnBean
* @see ConditionalOnMissingBean
* @see ConditionalOnClass
* @see AutoConfigureAfter
*/
开启自动配置spring应用,能够尝试去“猜测“你可能需要的配置,自动配置的类会作用在你的类路径基础上。举个例子,如果你有tomcat-embedded.jar,在你的类路径里,你可能会需要TomcatEmbeddedServletContainerFactory和EmbeddedServletContainerFactory这两个bean。
自动配置会尽可能智能的帮助你完成默认配置,你也可能会不希望使用很多默认配置,你可以通过excelude去移除自动配置支持。
注解本身不起任何作用,需要有对应的“注解处理器“。那我们就来找处理@EnableAutoConfiguration的处理器。自动化配置总是在bean定义被注册之后才会应用。在你标注了@EnableAutoConfiguration这个注解的类所在的包(包含子包)能够尽量的使用默认值。例如你扫描包的时候会以这个注解所在的包为根包开始扫描。自动配置类通常也是个spring bean。可以使用@Conditional来控制装配。
注解本身没有什么作用,只是个元数据而已,要配上注解处理器才能真正的起作用。AutoConfigurationImportSelector就是这个处理器.它的selectImports重写了ImportSelector,能够自动引入这些自动化配置的类,这些类会自动的初始化很多默认值。就此完成了自动化配置工作。
相关文章推荐
- 深入讲解spring boot中servlet的启动过程与原理
- Spring Boot启动过程及回调接口汇总
- Spring Boot 2.x 启动全过程源码分析(上)入口类剖析
- SpringBoot应用启动过程分析
- springboot启动过程
- SpringBoot 之 启动过程
- SpringBoot内嵌的Tomcat启动过程及其做过的工作
- [Spring Boot] 2. Spring Boot 启动过程定制化
- Spring Boot启动过程(六):内嵌Tomcat中StandardHost、StandardContext和StandardWrapper的启动
- Spring Boot启动过程完全解析(一)
- Spring Boot启动过程(六)之内嵌Tomcat中StandardHost、StandardContext和StandardWrapper的启动教程详解
- spring boot注解及启动过程
- [Spring Boot] 1. Spring Boot启动过程源码分析
- springboot的tomcat启动过程
- SpringBoot 应用程序启动过程探秘
- Spring Boot启动过程(三)
- Spring Boot学习笔记03--深入了解SpringBoot的启动过程
- Spring boot 启动过程解析 logback
- Spring Boot启动过程(四)之Spring Boot内嵌Tomcat启动