springboot原理(一)
Spring Boot特点
- springboot 是一个快速整合第三方框架 (maven子父项目依赖)
- 简化XML配置(完全采用注解化)
- 内置Http服务器(Jetty、undertow,Tomcat)
- 最终以java应用程序进行执行(当然也可以打成war包)
完全采用注解化
- 传统的WEB-INF/web.xml
@EnableWebMvc(Spring mvc) -- 用java代码操作spingmvc初始化过程
内置Http服务器
SPI机制
来源
- jdk 为了方便应用程序进行扩展,提供了默认的 SPI (Service Provider Interface) 实现(ServiceLoader)
- SPI机制主要针对厂商或者插件,有点类似IOC的思想,就是将装配的控制权移到程序之外,在模块化设计中这个机制尤其重要
约定
当服务的提供者,提供了服务接口的一种实现之后,在jar包的META-INF/services/目录里同时创建一个以服务接口命名的文件。该文件里就是实现该服务接口的具体实现类。而当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。 基于这样一个约定就能很好的找到服务接口的实现类,而不需要再代码里制定。jdk提供服务实现查找的一个工具类:java.util.ServiceLoader
DEMO
// 来源于网络
// 定义搜索接口(会有不同实现方式)
package my.xyz.spi;
import java.util.List;
public interface Search {
public List serch(String keyword);
}
/*
* A公司实现
在jar包中META-INF/services/my.xyz.spi.Search文件中写下如下内容:
com.A.spi.impl.FileSearch
* B公司实现
在jar包中META-INF/services/my.xyz.spi.Search文件中写下如下内容:
com.B.spi.impl.DatabaseSearch
*/
package com.xyz.factory;
import java.util.Iterator;
import java.util.ServiceLoader;
import my.xyz.spi.Search;
public class SearchFactory {
private SearchFactory() {
}
public static Search newSearch() {
Search search = null;
ServiceLoader<Search> serviceLoader = ServiceLoader.load(Search.class);
Iterator<Search> searchs = serviceLoader.iterator();
if (searchs.hasNext()) {
search = searchs.next();
}
return search;
}
}
package my.xyz.test;
import java.util.Iterator;
import java.util.ServiceLoader;
import com.xyz.factory.SearchFactory;
import my.xyz.spi.Search;
public class SearchTest {
public static void main(String[] args) {
Search search = SearchFactory.newSearch();
search.search("java spi test");
}
}
Spring SPI
-
spring 也为我们提供了spi实现
SpringFactoriesLoader, 允许开发人员通过META-INF/spring.factories文件进行扩展 -
spring boot 正是利用这个扩展点,在 spring-framework 的基础上为我们集成了常用的开源框架
- spring spi 内置tomcat
- java 代码创建tomcat实例
- tomcat 加载.class ..
内置tomcat
-
其实就是一个jar包 tomcat-embed-core.jar (springboot2.0依赖tomcat8.5+)
-
然后用java代码创建tomcat实例
-
可以用tomcat加载servlet
new Tomcat - > tomcatServer setPort(); new StandardContext StandardContext.setPath //监听上下文 StandardContext.addLifecycleListener(new FixContextListener()) //把context添加到server-host tomcatServer.getHost.addChild(StandardContext); tomcatServer.addServlet(new XXXServlet()); tomcatServer.addServletMapping tomcatServer.start(); tomcatServer.await(); -
用tomcat加载springmvc注释启动方式 ,就是springboot启动的大致原理了
-
实现 AbstractAnnotationConfigDispatcherServletInitializer初始化dispatcherservlet
-
实现以下方法
-
加载spring 根配置信息
// RootConfig componentScan("packages") class[] getRootConfigClasses() -
加载springmvc config
// 用java代码方式配置springmvc 实现WebMvcConfigurerAdapter // @componentScan("packages"),@enableWebMvc,@Config class[] getServletConfigClasses() -
url mapping,拦截所有请求
String [] getServletMappings(new String[]{"/"})
-
-
用tomcat加载target/classes下面的class
-
Spring Boot启动原理
@Target({ElementType.TYPE}) //注解的适用范围,其中TYPE用于描述类,接口(包括包注解类型)活enum声明
@Retention(RetentionPolicy.RUNTIME)//注解的生命周期,保留到class文件中(三个生命周期)
@Documented // 表明这个注解应该被javadoc记录
@Inherited // 子类可以继承该注解
// 以上四个注解是java注解的元注解,不做说明
@SpringBootConfiguration // 继承了Configuration,表示当前是注解类
@EnableAutoConfiguration // 开启springboot 的注解功能,springboot的四大神器之一,其中借助了@import的帮助
@ComponentScan( // 扫描路径设置
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
...
}
@Configuration
springIOC容器的配置类,任何一个标注了@Configuration的java类都是一个javaConfig配置类
任何一个标注了@Bean的方法,其返回值将作为一个bean定义注册到Spring的IOC容器中,方法名默认为该bean定义的id。如果一个bean的定义依赖其他bean,则直接调用对应的javaConfig类中依赖bean的创建方法就可以了。
@ComponentScan
@ComponentScan这个注解就是自动扫描加载符合条件的组件(比如@Component和@Repository等)或者bean定义,最终将这些bean定义加载到Ioc容器中。我们可以通过basePackages等属性来细粒度的定制@ComponentScan自动扫描的范围,如果不指定,则默认spring框架实现会从声明@ComponentScan所在类的package进行扫描。(这也就是为什么springboot 的启动类最好放在root package路径下的原因,因为默认不指定basePackages)
@EnableAutoConfiguration
借助@Import,将所有符合自动配置条件的bean定义加载到Ioc容器中。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
// 以上四个注解是java注解的元注解,不做说明
@AutoConfigurationPackage // 自动配置包
@Import({AutoConfigurationImportSelector.class}) // 导入自动配置的组件
public @interface EnableAutoConfiguration {
...
}
@Import({AutoConfigurationImportSelector.class}) ,借助AutoConfigurationImportSelector,
@EnableAutoConfiguration可以帮助springboot应用将所有符合条件的配置都加载到当前SpringBoot创建并使用的Ioc容器。借助SpringFactoriesLoader工具类的支持,@EnableAutoConfiguration可以智能的自动配置
由以上的分析可知,自动配置的幕后英雄是SpringFactoriesLoader
SpringFactoriesLoader
先来看看该类定义是什么样子的
public final class SpringFactoriesLoader {
// 最耀眼的属性, 定义了一个文件路径 META-INF/spring.factories
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
...
public static <T> List<T> loadFactories(Class<T> factoryClass, @Nullable ClassLoader classLoader) {
...
}
private static <T> T instantiateFactory(String instanceClassName, Class<T> factoryClass, ClassLoader classLoader) {
...
}
}
配置文件META-INF/spring.factories中最后的类名都是XXXAutoConfiguration,然后就是配合@EnableAutoConfiguration注解,拿着配置文件中定义好的类名通过反射实例化为对应的标注了@Configuration的JavaConfig形式的IoC容器配置类,然后汇总为一个并加载到IoC容器。
执行流程
通过查看源码,我们知道SpringBoot应用的执行入口是run方法。我们也是从该方法入手的。大体的流程如下:
-
如果我们使用的是SpringApplication的静态run方法,那么,这个方法里面首先要创建一个SpringApplication
的实例,然后调用这个创建好的SpringApplication是实例方法。
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);
}
根据classpath里面是否存在某个特征类(org.springframework.web.context.ConfigurableWebApplicationContext)来决定是否应该创建一个为Web应用使用的ApplicationContext类型。
-
使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationContextInitializer。
-
使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationListener。
推断并设置main方法的定义类。
- SpringApplication实例初始化完成并且完成设置后,就开始执行run方法的逻辑了,方法执行伊始,首先遍历执行所有通过SpringFactoriesLoader可以查找到并加载的SpringApplicationRunListener。调用它们的started()方法,告诉这些SpringApplicationRunListener,“嘿,SpringBoot应用要开始执行咯!”。
- 创建并配置当前Spring Boot应用将要使用的Environment(包括配置要使用的PropertySource以及Profile)。
- 遍历调用所有SpringApplicationRunListener的environmentPrepared()的方法,告诉他们:“当前SpringBoot应用使用的Environment准备好了咯!”。
- 如果SpringApplication的showBanner属性被设置为true,则打印banner。
- 根据用户是否明确设置了applicationContextClass类型以及初始化阶段的推断结果,决定该为当前SpringBoot应用创建什么类型的ApplicationContext并创建完成,然后根据条件决定是否添加ShutdownHook,决定是否使用自定义的BeanNameGenerator,决定是否使用自定义的ResourceLoader,当然,最重要的,将之前准备好的Environment设置给创建好的ApplicationContext使用。
- ApplicationContext创建好之后,SpringApplication会再次借助Spring-FactoriesLoader,查找并加载classpath中所有可用的ApplicationContext-Initializer,然后遍历调用这些ApplicationContextInitializer的initialize(applicationContext)方法来对已经创建好的ApplicationContext进行进一步的处理。(回调)
- 遍历回调所有SpringApplicationRunListener的contextPrepared()方法。
- 我们可以按需实现 ApplicationContextInitializer , SpringApplicationRunListener
- 最核心的一步,将之前通过
@EnableAutoConfiguration获取的所有配置以及其他形式的IoC容器配置加载到已经准备完毕的ApplicationContext。 - 遍历调用所有SpringApplicationRunListener的contextLoaded()方法。
- 调用ApplicationContext的refresh()方法,完成IoC容器可用的最后一道工序。
- 查找当前ApplicationContext中是否注册有CommandLineRunner,如果有,则遍历执行它们。
以上就是SpringBoot的启动流程。
自定义Starter
考虑使用到的依赖
编写自动配置
// 参考WebMvcAutoConfiguartion.java // springmvc自动配置
@Configuration //指定这个类为配置类
@ConditionalOnXXX // 满足条件配置类生效
@AutoConfigureAfter // 配置顺序
@Bean // 向容器中添加组件
@ConfigurationProperties
@EnableConfigurationProperties // 让xxxProperties生效并加入到容器
// 自动配置类要想能够加载,需要把它配置到 META-INF/spring.factories
定义模式
-
(启动器)xxx-starter : 一个空的jar包
-
提供依赖管理,这些依赖可用于自动装配或者其它类库
-
如果他人要使用此启动器,直接依赖xxx-starter.jar就可以
-
推荐命名规则
- 官方的:spring-boot-starter-模块名 ,eg. spring-boot-starter-web , spring-boot-starter-jdbc
- 自定义:模块名-spring-boot-starter ,eg. mybatis-spring-boot-starter
-
-
(自动配置模块)xxx-starter-autoconfigurer
- 被启动器依赖

浙公网安备 33010602011771号