我们在以前开发web应用中,需要各种配置,就是配置炼狱。springboot的出现让开发变得简单,约定大于配置,内嵌servlet容器,提供各种starter尽可能的自动装配。无需xml文件开箱即用。springboot的入门简单,但是想要精通我们就要去探究springboot的原理。
1、启动原理
重点关注对象:
- ApplicationContextInitializer:应用上下文初始化
- SpringApplicationRunListener:应用上下文运行监听
(1)springboot2.0开发web应用:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
(2)编写启动类
@SpringBootApplication
public class SpringbootMybatisApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootMybatisApplication.class, args);
}
}
这是我们一个web应用就跑起来了,**SpringApplication.run()**方法在下面又做了什么呢?
(3)启动原理
我们用run方法,定位到:
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
1、new SpringApplication(primarySources)分析
我们生成了一个SpringApplication对象,传入primarySources数组,随后再看run方法:
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader; //resourceLoader==null;
Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//将primarySources赋值 并且传入我们的primarySources,primarySources为{'com.dl.SpringbootMybatisApplication'}
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//判断是什么应用
setInitializers((Collection)getSpringFactoriesInstances(ApplicationContextInitializer.class));
//加载applicationcontent初始化类,将Initializer属性赋值,都是通过META-INF/spring.factories配置得到的
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); //加载监听器,将所需的Listener赋值
this.mainApplicationClass = deduceMainApplicationClass();
//
}
我们分析:
1、
this.webApplicationType = WebApplicationType.deduceFromClasspath();
2、
setInitializers((Collection)getSpringFactoriesInstances(ApplicationContextInitializer.class));
我们通过**(Collection)getSpringFactoriesInstances(ApplicationContextInitializer.class)**跟踪源码到:
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// 拿到当前线程的类加载
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//我们通过loadFactoryNames方法
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
通过loadFactoryNames方法:
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
3 、loadSpringFactories(classLoader)
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader); //result为空
if (result != null) {
return result;
}
try {
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) { //size=2
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
//resource 的两个值:1、spring-boot-autoconfigure-2.1.8.RELEASE.jar!/META-INF/spring.factories 2、spring-boot:2.1.8.RELEASE.jar!/META-INF/spring.factories
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryClassName = ((String) entry.getKey()).trim();
for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryClassName, factoryName.trim());
}
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
两个jar包下的**/META-INF/spring.factories**是所有springboot启动需要加载的所有类路径信息。
1、run方法执行
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
//加载时间类
stopWatch.start();
//开始计时
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
//开启headless模式,这个可以具体了解
SpringApplicationRunListeners listeners = getRunListeners(args);
//生成 应用上下文运行监听器
listeners.starting();
//监听开始
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//封装args数组
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
//环境准备,创建我们基于servlet的web应用所需的环境,并且listenter监听调用environmentPrepared方法,环境已经准备好
configureIgnoreBeanInfo(environment);
//系统设置
Banner printedBanner = printBanner(environment);
//打印banner图
context = createApplicationContext();
//通过反射生成ConfigurableApplicationContext对象
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//对象生成,用于启动出错问题的回调
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//准备上下文环境;将environment保存到ioc中;而且applyInitializers(); //applyInitializers():回调之前保存的所有的ApplicationContextInitializer的initialize方法
//回调所SpringApplicationRunListener监听的contextPrepared()方法;
refreshContext(context);
//prepareContext运行完成以后回调所有的SpringApplicationRunListener的contextLoaded();
//刷新容器ioc容器初始化(如果是web应用还会创建嵌入式的Tomcat);
//扫描,创建,加载所有组件的地方;(配置类,组件,自动配置)
afterRefresh(context, applicationArguments);
//所有的SpringApplicationRunListener回调finished方法
stopWatch.stop();
//整个过程使用时间停止
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
2、自动配置
很多东西我们都不需要自己去实现,而是springboot会自动的帮我们实现,这个是怎么做到的呢?
@SpringBootApplication
public class SpringbootMybatisApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootMybatisApplication.class, args);
}
}
我们查看@SpringBootApplication是一个组合注解,下面有一个**@EnableAutoConfiguration**,我们在向下探究:
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
1、@AutoConfigurationPackage分析:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}
经过调试我们可以发先,我们自动配置扫描包就是我么启动类的所在包,所以我们写代码的时候要写在springboot的启动类下面才可以扫描到。
2、@Import(AutoConfigurationImportSelector.class)分析:
AutoConfigurationImportSelector有一个selectImports方法,返回我们那些类需要自动导入,会扫描META-INF/spring.factories文件下EnableAutoConfiguration.class下的所有类名
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
//拿到所有
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
至此我们得到了那些需要自动装配,通过那些类存在判断最终需要自动装配的类实例。注册容器中。