理解springboot

279 阅读4分钟
  1. autoconfig

org.springframework.boot.autoconfigure.AutoConfigurationImportSelector 为autoConfig的实现。其实就是定义了 ImportSelector

SpringFactoriesLoader(其实就是getRource,类似于类的加载器,可以委托父类)加载文件中的资源 org.springframework.context.annotation.ConfigurationClassParser 对资源进行解析,load 到beandefinition中。

2.  import

@Import 是用来导入配置类 并 初始化bean 的,而导入方式主要分为以下三种类型。

  1. 直接导入配置类,被@Configuration修饰的类。 2. ImportSelector接口的实现类,返回一个配置类名称的数组,然后再导入这些配置类。ImportSelector接口的一个实现类AutoConfigurationImportSelector则承包了从ClassPath下各个starter中的META-INF/spring.factories文件中读取需要导入的自动配置类的工作。 3. ImportBeanDefinitionRegistar接口的实现类,直接在接口方法中注册Bean。

  2. 启动流程

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包就是全部,方便应用扩展。 借助容器化,可以进行大规模的部署。