- autoconfig
org.springframework.boot.autoconfigure.AutoConfigurationImportSelector 为autoConfig的实现。其实就是定义了 ImportSelector
SpringFactoriesLoader(其实就是getRource,类似于类的加载器,可以委托父类)加载文件中的资源 org.springframework.context.annotation.ConfigurationClassParser 对资源进行解析,load 到beandefinition中。
2. import
@Import 是用来导入配置类 并 初始化bean 的,而导入方式主要分为以下三种类型。
-
直接导入配置类,被@Configuration修饰的类。 2. ImportSelector接口的实现类,返回一个配置类名称的数组,然后再导入这些配置类。ImportSelector接口的一个实现类AutoConfigurationImportSelector则承包了从ClassPath下各个starter中的META-INF/spring.factories文件中读取需要导入的自动配置类的工作。 3. ImportBeanDefinitionRegistar接口的实现类,直接在接口方法中注册Bean。
-
启动流程
springboot的启动可以理解为就是利用spring的扩展点,定义了一系列的扩展逻辑。
扩展的逻辑分为三种:ApplicationContextInitializer(在容器refresh之前对容器做一些操作)、ApplicationListener(事件的监听,启动过程中定义了一些事件的发布者SpringApplicationRunListeners)、BeanFactoryPostProcessor(对bean definitions做一些修改等,和一些注解)。
根据类路径下的 META-INF/spring.factories 文件解析并获取 ApplicationContextInitializer 接口的所有配置的类路径名称
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
1. new 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));
//application type: web,servlet,or reactive
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//初始化 ApplicationContextInitializer
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//初始化ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//推断主入口类
this.mainApplicationClass = deduceMainApplicationClass();
}
2. run
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
//org.springframework.boot.SpringApplicationRunListener=org.springframework.boot.context.event.EventPublishingRunListener
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
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;
}
A BeanFactoryPostProcessor may interact with and modify bean definitions, but never bean instances. Doing so may cause premature bean
instantiation, violating the container and causing unintended side-effects. If bean instance interaction is required, consider implementing
{@link BeanPostProcessor} instead. BeanFactoryPostProcessor is limit to interact with bean definitions, but not bean instance.
为什么是springboot:
@EnableAutoConfiguration 的原理:
org.springframework.boot.autoconfigure.AutoConfigurationImportSelector 为autoConfig的实现. SpringFactoriesLoader加载文件中的资源
org.springframework.context.annotation.ConfigurationClassParser 对资源进行解析,load 到beandefinition中
@configuration的原理:
org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions
@Import是用来导入配置类的,而导入方式主要分为以下三种类型。
1. 直接导入配置类,被@Configuration修饰的类。
2. ImportSelector接口的实现类,返回一个配置类名称的数组,然后再导入这些配置类。ImportSelector接口的一个实现类AutoConfigurationImportSelector则承包了从ClassPath下各个starter中的META-INF/spring.factories文件中读取需要导入的自动配置类的工作。
3. ImportBeanDefinitionRegistar接口的实现类,直接在接口方法中注册Bean。
https://juejin.im/post/6844903882133340168
org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions 解析所有的configbean
https://www.cnblogs.com/hjwublog/p/10332042.html
http://tengj.top/2017/03/09/springboot3/
https://blog.51cto.com/luecsc/1964056 该文章写的非常不错
遗留问题,spring 容器在什么步骤中启动的呢?
- spring 怎么解决循环依赖的问题?
需要明确的是spring对循环依赖的处理有三种情况: ①构造器的循环依赖:这种依赖spring是处理不了的,直 接抛出BeanCurrentlylnCreationException异常。 ②单例模式下的setter循环依赖:通过“三级缓存”处理循环依赖。 ③非单例循环依赖:无法处理。
spring 的核心扩展就是 BeanPostProcessor 和 BeanFactoryPostProcessor, 其中 ApplicationContextAwareProcessor implements BeanPostProcessor 就是aware的功能实现。
spring boot 可执行的jar包 是 怎么打出来的? 怎么运行的?
依赖maven的插件,构建出来一个包含/BOOT_INFO/lib,org,META_INFO 这样的目录结构, 这样的结构肯定需要定制的classLoad才能加载。然后启动的时候 运行JarLauncher 的main方法, spring boot 对java 的默认 url 做了一个 扩展,抽象了 JarFileArchive, 支持了 jar in jar的协议, all thing in jar,就是 jar:file:/jarlauncher-0.0.1-SNAPSHOT.jar!/lib/spring-boot-1.5.10.RELEASE.jar!/org/springframework/boot/loader/JarLauncher.class 这个协议只有 通过 sping boot 定制的扩展处理器才能解析,会把这个 url的解析器绑定到 classLoader上,这样 当load class的时候, 就可以加载依赖的类。 有了classLoader, 通过反射调用业务的main方法。启动spring的容器等等。
1. spring boot应用打包之后,生成一个fat jar,里面包含了应用依赖的jar包,还有Spring boot loader相关的类
2. Fat jar的启动Main函数是JarLauncher,它负责创建一个LaunchedURLClassLoader来加载/lib下面的jar,并以一个新线程启动应用的Main函数。
spring boot借助容器化,可以如虎添翼,发挥出更大的威力,也只有通过容器化,才能体会到spring boot开发的高效。通过以上的介绍,你可以很顺利的打好一个jar包或者war包,那么可以通过编写dockerfile文件进行镜像的构建 jar 包 本地无需搭建web容器,方便开发和调试。因为自带web容器,可以避免由于web容器的差异造成不同环境结果不一致问题。一个jar包就是全部,方便应用扩展。 借助容器化,可以进行大规模的部署。