前言
在很久很久以前看走读过比较旧的spring core,context,beans的代码由于当时比较年轻,决定再系统化复习一下Spring Boot全家桶。
配合官方文档和某度,并实现相关例子。
记忆中的Spring 核心的主要流程
入口 :ApplicationContext ->
通常使用: ClassPathXmlApplicationContext ->
Bean工厂:DefaultListableBeanFactory ->
创建类: AbstractBeanFactory.doGetBean ->
知道了主要流程和类后,开始跟踪Spring Boot 主要针对: 配置文件加载、注解加载、创建类的过程(Cglib)、IoC、AOP。
Spring boot 启动过程
1.入口
public static void main(String[] args) {
SpringApplication.run(SpringDemoApplication.class);
}
2.初始化SpringApplication
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
//默认null
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
//用于读取包目录,猜:是用于读取这个包下面的配置级注解
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//猜:用于加载WEB 输出方式
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//跟踪进去代码,获取一个java的类加载器
//加载xxx-starter 里面的META-INF/spring.factories-> ApplicationContextInitializer 相关
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//加载xxx-starter 里面的META-INF/spring.factories-> ApplicationListener 相关
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//通过运行时堆栈,找到运行时的main 方法相关信息.
this.mainApplicationClass = deduceMainApplicationClass();
}
3.加载META-INF/spring.factories 配置类,监听器和需要初始化的类 SpringFactoriesLoader
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
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()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
里面有我们感兴趣的初始化内容
4.开始启动 SpringApplication.run
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();//运行计时器,非常好用,可以用于多个。
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty(); //是否开启图片设备动作支持等,spring默认是开启的。
SpringApplicationRunListeners listeners = getRunListeners(args); //EventPublishingRunListener
//通知所有SpringApplicationListeners 应用开始启动了
listeners.starting();
try {
//把args以 ApplicationArguments的形式放入context
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
//划重点,初始化BeanFactory的地方
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) {
//打印stopWatch 日志。
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
//运行ApplicationRunner,和CommanderRunner
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
//通知listeners开始运行
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
ok ,经过上面代码的读取,知道了Spring Boot 大致启动过程,初始化-加载各种starter 的spring.factories 需要加载的监听器及Starter 相关的ApplicationListener、ApplicationContextInitializer然后各种通知日志初始化 ,异常监听。
那么开始接下来进入我们最关心的部分。
何时读取注解配置及BeanFactory初始化及初始化的时候做了什么
SpringApplication.load(context, sources.toArray(new Object[0]))
->AbstractApplicationContext.prepareBeanFactory(beanFactory)
-> invokeBeanFactoryPostProcessors
->PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors
->invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
-> ConfigurationClassPostProcessor.invokeBeanDefinitionRegistryPostProcessors
->ConfigurationClassParser.doProcessConfigurationClass
->ClassPathBeanDefinitionScanner.doScan
->findCandidateComponents
->scanCandidateComponents
->PathMatchingResourcePatternResolver
利用各种套娃得到编译后的 main入口及子包目录 例如cn.fobe.spring.SpringDemoApplication
返回 URL [file:/Users/daji/IdeaProjects/van/examples/spring/target/classes/cn/fobe/spring/]
如果是jar会使用jar的读取模式。
使用dir.listFiles();读取所有class文件
CachingMetadataReaderFactory.getMetadataReader()
终于来到了高潮org.springframework.asm.ClassReader
其实就是一个InpuStream 然后吧class文件读取进来配合ClassVisitor把.class转MetadataReader换成SimpleAnnotationMetadata ->最终生成BeanDefinition
然后进行各种通知,实例化非懒加载的类,默认都加载。
Spring 到底经历了什么。套娃套的如此厉害.
创建类的过程、IoC、AOP
经过上面代码的走读发现,类默认都是直接加载,为了代码跟踪简化,弄个Lazy的类,然后使用BeanFactory弄出来,跟踪
ConfigurableApplicationContext context = SpringApplication.run(SpringDemoApplication.class,args);
HiService hiService = (HiService) context.getBeanFactory().getBean("hiServiceImpl");
AbstractAutowireCapableBeanFactory
.createBean
->doCreateBean
->createBeanInstance
->instantiateBean
->CglibSubclassingInstantiationStrategy
->SimpleInstantiationStrategy
->BeanUtils.instantiateClass
->使用BeanWrapperImpl 对实例化的bean 进行加工
java.lang.reflect.field.set(bean, value); 对需要注入的属性进行填充
AbstractAutowireCapableBeanFactory
BeanPostProcessor 对Bean进行再次加工AOP
TIP:AnnotationAwareAspectJAutoProxyCreator(BeanPostProcessor)
在pom 导入后添加了这个BeanPostProcessor 但是没看到
Spring-boot-starter-aop 里面有spring.factories文件
image.png
其实spring.factories都在他的子依赖里面着点比较骚气。
何时加载了application.yml
在spring boot启动是加载的PropertySourceLoader
YamlPropertySourceLoader 进行加载。
附:spring-boot-starter-jpa
关注点:接口类Repository 为何可以直接执行、HibernateSessionFactory getSession 发生了什么事情、是否通过线程来规定
附: 自己实现一个starter 来巩固一下知识
https://www.jianshu.com/p/dc1ff2102bc5
附:spring-boot-starter-web
都复习到这个程度了,顺便复习一下最最常用的Web加载过程,关注点:内嵌Tomcat、URL过程、WebApplicationContext 与 ApplicationContext的关系、Spring MVC如何生产Servlet。(注:其它FilterChain 和 Servlet 的执行过程,直接去看Servlet的文档即可)
网友评论