Spring -- 源码分析-- springboot 启动过程
2019-06-18 17:24
507 查看
1、初步构造
1、实例化过程初始化了 ApplicationContextInitializer 和 ApplicationListener ,同时判断了当前是否处于 web 环境中
[code]//SpringApplication.java 257 //1.根据 { "javax.servlet.Servlet", // "org.springframework.web.context.ConfigurableWebApplicationContext" } 是否存在, 决定web环境 this.webEnvironment = deduceWebEnvironment(); //2.加载所有的 ApplicationContextInitializer (META-INF/spring.factories) setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); //3.加载所有的 ApplicationListener (META-INF/spring.factories) setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); //4.定义 main 方法 this.mainApplicationClass = deduceMainApplicationClass();
2、核心文件 META-INF/spring.factories ,使用 SpringFactoriesLoader.loadFactoryNames(type, classLoader) 读取
[code]//SpringFactoriesLoader.java 109行 //读取 "META-INF/spring.factories" 文件 Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); while (urls.hasMoreElements()) { URL url = urls.nextElement(); Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url)); String factoryClassNames = properties.getProperty(factoryClassName); result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray( factoryClassNames))); }
2、启动过程
1、开启时间记录
[code]StopWatch stopWatch = new StopWatch(); stopWatch.start(); //...省略 stopWatch.stop();
2、初始化所有监听器 ,使用一个类来管理所有的监听器,负责发布所有的事件
[code]SpringApplicationRunListeners listeners = getRunListeners(args); listeners.started(); //getRunListeners 方法,读取的也是上面的文件,加载所有的 SpringApplicationRunListener return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances( SpringApplicationRunListener.class, types, this, args));
3、 配置环境 environment
[code]//1.启动参数 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); //2.此处配置的是 StandardServletEnvironment,并合并启动参数到环境中,并发布 SpringApplicationRunListeners 事件 ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);
4、打印 banner
[code]Banner printedBanner = printBanner(environment);
5、创建容器,并预处理容器
[code]//1.创建的是 AnnotationConfigEmbeddedWebApplicationContext context = createApplicationContext(); prepareContext(context, environment, listeners, applicationArguments,printedBanner);
[code]//1.设置 environment context.setEnvironment(environment); //2.调用所有的 ApplicationContextInitializer applyInitializers(context); //3.发布 SpringApplicationRunListeners 事件 listeners.contextPrepared(context); //4.注册当前 Application 类到容器中 load(context, sources.toArray(new Object[sources.size()])); //5.发布 SpringApplicationRunListeners 事件 listeners.contextLoaded(context);
6、刷新容器,其实执行的就是 refresh
[code]//1.最终调用的是 AbstractApplicationContext 的 refresh 方法 refreshContext(context);
7、调用 runners
[code]//调用 ApplicationRunner 和 CommandLineRunner 的 run 方法,可以用来启动其他的服务 List<Object> runners = new ArrayList<Object>(); runners.addAll(context.getBeansOfType(ApplicationRunner.class).values()); runners.addAll(context.getBeansOfType(CommandLineRunner.class).values()); AnnotationAwareOrderComparator.sort(runners); for (Object runner : new LinkedHashSet<Object>(runners)) { if (runner instanceof ApplicationRunner) { callRunner((ApplicationRunner) runner, args); } if (runner instanceof CommandLineRunner) { callRunner((CommandLineRunner) runner, args); } }
3、@Configuration 注解加载过程
1、加载过程,使用的是 ConfigurationClassPostProcessor 类来进行处理的
[code]//1.获取所有的bean String[] candidateNames = registry.getBeanDefinitionNames(); for(...){ //2.判断对应bean是否为配置类,如果是,则加入到configCandidates } //3.对configCandidates 进行 排序,按照@Order 配置的值进行排序 Collections.sort(configCandidates, new Comparator<BeanDefinitionHolder>() { @Override public int compare(BeanDefinitionHolder bd1, BeanDefinitionHolder bd2) { int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition()); int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition()); return (i1 < i2) ? -1 : (i1 > i2) ? 1 : 0; } }); //4.调用ConfigurationClassParser#parse进行解析 parser.parse(candidates); parser.validate(); //5.将解析过的配置类加入到configClasses,并去重 Set<ConfigurationClass> configClasses = new LinkedHashSet<ConfigurationClass>(parser.getConfigurationClasses()); configClasses.removeAll(alreadyParsed); //6.解析bean this.reader.loadBeanDefinitions(configClasses);
2、@Configuration 注解类的解析过程
[code]//ConfigurationClassBeanDefinitionReader.java 124行 //1.解析每个方法,寻找 @Bean 注解 for (BeanMethod beanMethod : configClass.getBeanMethods()) { loadBeanDefinitionsForBeanMethod(beanMethod); } //2.解析 import 标签,按照 xml 方式去解析 bean loadBeanDefinitionsFromImportedResources(configClass.getImportedResources()); loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
4、@Import 注解加载过程
1、ConfigurationClassPostProcessor 在 调用 ConfigurationClassParser.parse 中去解析的
[code]public void parse(Set<BeanDefinitionHolder> configCandidates) { //...省略 processDeferredImportSelectors(); }
2、举例:@EnableAutoConfiguration
该注解使用到了 @Import(EnableAutoConfigurationImportSelector.class)
[code]public String[] selectImports(AnnotationMetadata metadata) { if (!isEnabled(metadata)) { return NO_IMPORTS; } try { AnnotationAttributes attributes = getAttributes(metadata); //1.读取 META-INF/spring.factories 文件下所有 EnableAutoConfiguration 类型的类 List<String> configurations = getCandidateConfigurations(metadata,attributes); configurations = removeDuplicates(configurations); Set<String> exclusions = getExclusions(metadata, attributes); configurations.removeAll(exclusions); configurations = sort(configurations); recordWithConditionEvaluationReport(configurations, exclusions); return configurations.toArray(new String[configurations.size()]); } catch (IOException ex) { throw new IllegalStateException(ex); } }
3、@import 使用的几种方式
- 最简单的方式,直接将类引入
[code]@Configuration @Import(value={UserServiceImpl.class}) public class Config {}
- 使用 ImportBeanDefinitionRegistrar 方式
[code]public class UserServiceBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { //通过registry就可以注入到容器里 } }
- 使用 @ImportSelector 方式
[code]@Configuration() @Import(UserServiceImportSelect.class) public class Config { } public class UserServiceImportSelect implements ImportSelector{ public String[] selectImports(AnnotationMetadata importingClassMetadata) { } }
相关文章推荐
- [Spring Boot] 1. Spring Boot启动过程源码分析
- Spring Boot 2.x 启动全过程源码分析(上)入口类剖析
- Spring Boot启动过程源码分析
- Spring Boot启动过程源码分析(二)事件监听器
- U-Boot 启动过程和源码分析(第二阶段)
- 嵌入式linux开发uboot移植(三)——uboot启动过程源码分析
- SpringBoot-Loader源码分析系列1:启动&读取MANIFEST.MF文件
- spring启动component-scan类扫描加载过程---源码分析
- U-Boot 启动过程和源码分析(第二阶段)-main_loop分析
- SpringBoot应用启动过程分析
- U-Boot启动过程源码分析(3)-启动Linux
- spring源码分析-应用启动过程
- 【一】Uboot-2017.11源码分析启动过程之汇编部分
- Spring boot源码分析-AnnotationConfigApplicationContext非web环境下的启动容器(2)
- U-Boot启动过程源码分析(2)-第二阶段
- U-Boot的启动过程源码分析
- spring启动component-scan类扫描加载过程---源码分析
- Spring原理与源码分析系列(三)- Spring IoC容器启动过程分析(下)
- springboot源码分析8-环境属性构造过程(下)
- springboot源码分析之环境属性构造过程1