springboot源码学习2 自动装配原理学习 一 构建基础项目 二 源码跟踪 三 测试
1 构建依赖:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.4.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.cxy</groupId> <artifactId>register-sever</artifactId> <version>0.0.1-SNAPSHOT</version> <name>register-sever</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
2 查看启动类:
@SpringBootApplication //@Import({ImportTest.class, SelfImportSelector.class, SelfImportBeanDefinitionRegistrar.class}) public class RegisterSeverApplication { public static void main(String[] args) { SpringApplication.run(RegisterSeverApplication.class, args); } }
3 编写测试接口:
先忽略注释的代码
@RestController public class Rest { /* @Autowired ImportTest importTest; @Autowired ImportTestC importTestC; @Autowired ImportTestD importTestD;*/ @RequestMapping("/hello") public String get(){ // importTest.test(); // importTestC.test(); // importTestD.test(); return "hello"; } }
4 启动main方法
调用http://localhost:8080/hello 获取结果为hello 就可以了 很多在想直接源码上不就好了么,其实可以看到代码中有很多注释的东西,这个就是为啥要这么做的原因,下面会继续讲
二 源码跟踪
1构造方法
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { // resourceLoader == null this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); //primarySources 为基础类 this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); this.webApplicationType = WebApplicationType.deduceFromClasspath(); // 设置初始化器 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); // 设置监听器 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass(); }
2 入口
所有的方法入口都是main方法所以我们可以跟进main方法进去:
public ConfigurableApplicationContext run(String... args) {
// 创建计时器
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// 设置handlerless为true
configureHeadlessProperty();
// 获取监听器 启动
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 预处理环境信息
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
// 设置configureIgnoreBeanInfo 为true
configureIgnoreBeanInfo(environment);
// 打印banner
Banner printedBanner = printBanner(environment);
// 创建上下文ConfigurableApplicationContext
context = createApplicationContext();
// 获取异常报信息
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 预处理context
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 刷新context 调用spring得refresh方法
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;
}
3 getRunListeners(args)方法
是获取 META-INF/spring.factories中配置的内容核心方法,配置的监听器 内容如下
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
// 获取类加载器
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
// 通过传入参数的class类型,来获取具体内容的集合 获取listener时候 传入 SpringApplicationRunListener
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 创建实例
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
4 prepareEnvironment(listeners, applicationArguments)方法
这个方法是进行环境的预处理,就是激活相关配置文件,例如**.yml 核心方法 configureEnvironment
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
if (this.addConversionService) {
ConversionService conversionService = ApplicationConversionService.getSharedInstance();
environment.setConversionService((ConfigurableConversionService) conversionService);
}
// 处理配置文件
configurePropertySources(environment, args);
// 激活
configureProfiles(environment, args);
}
5 createApplicationContext方法
protected ConfigurableApplicationContext createApplicationContext() { Class<?> contextClass = this.applicationContextClass; //根据类型进行判断加载哪个ConfigurableApplicationContext if (contextClass == null) { try { switch (this.webApplicationType) { case SERVLET: // servlet contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS); break; // REACTIVE case REACTIVE: contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS); break; default: // 普通main方法 contextClass = Class.forName(DEFAULT_CONTEXT_CLASS); } } catch (ClassNotFoundException ex) { throw new IllegalStateException( "Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex); } } return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass); }
6 getSpringFactoriesInstances方法处理实例
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { // 获取类加载器 ClassLoader classLoader = getClassLoader(); // Use names and ensure unique to protect against duplicates // 通过传入参数的class类型,来获取具体内容的集合 获取listener时候 传入 SpringApplicationRunListener Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); // 创建实例 List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; }
7 prepareContext预处理方法
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { context.setEnvironment(environment); // 对容器得一个后处理 beanNameGenerator resourceLoader ConversionService postProcessApplicationContext(context); // 执行初始化器 applyInitializers(context); listeners.contextPrepared(context); if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); // 打印profile信息 logStartupProfileInfo(context); } // Add boot specific singleton beans //获取beanfactory 后续到beanfactory进行必要设置 ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); // 封装参数注册到获取beanfactory beanFactory.registerSingleton("springApplicationArguments", applicationArguments); if (printedBanner != null) { //如果beanfactory存在 注册单例bean beanFactory.registerSingleton("springBootBanner", printedBanner); } if (beanFactory instanceof DefaultListableBeanFactory) { // 设置bean对象是否可以重载 ((DefaultListableBeanFactory) beanFactory) .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } if (this.lazyInitialization) { // 懒加载 context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor()); } // Load the sources // 找到所有得配置源 Set<Object> sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty"); // 进入load方法 load(context, sources.toArray(new Object[0])); // 触发contextLoaded的完成 listeners.contextLoaded(context); }
8 refreshContext
这个方法是最复杂的方法,会加载AutoConfigurationImportSelector内容 会调用spring的refresh方法 最终解析配置文件的方法在processImports方法中
for (SourceClass candidate : importCandidates) { if (candidate.isAssignable(ImportSelector.class)) { // Candidate class is an ImportSelector -> delegate to it to determine imports Class<?> candidateClass = candidate.loadClass(); ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class, this.environment, this.resourceLoader, this.registry); Predicate<String> selectorFilter = selector.getExclusionFilter(); if (selectorFilter != null) { exclusionFilter = exclusionFilter.or(selectorFilter); }
// 这一步是加载自动配置的关键 获取元数据加载 if (selector instanceof DeferredImportSelector) { this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector); } else { String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata()); Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter); processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false); } }
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered
可以看到EnableAutoConfiguration 注解上类的加载
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; /** * Exclude specific auto-configuration classes such that they will never be applied. * @return the classes to exclude */ Class<?>[] exclude() default {}; /** * Exclude specific auto-configuration class names such that they will never be * applied. * @return the class names to exclude * @since 1.3.0 */ String[] excludeName() default {}; }
AutoConfigurationImportSelector中核心方法:
@Override public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); }
实现了这个方法就会被加载到springboot
9 handleRunFailure 异常处理方法
private void handleRunFailure(ConfigurableApplicationContext context, Throwable exception, Collection<SpringBootExceptionReporter> exceptionReporters, SpringApplicationRunListeners listeners) { try { try { // 处理退出code handleExitCode(context, exception); if (listeners != null) { listeners.failed(context, exception); } } finally { // 报告异常 reportFailure(exceptionReporters, exception); if (context != null) { context.close(); } } } catch (Exception ex) { logger.warn("Unable to close ApplicationContext", ex); } // 通过反射 报告具体类型 ReflectionUtils.rethrowRuntimeException(exception); }
三 测试
1 新建三个类:
public class ImportTest { public void test(){ System.out.println("我是中国人"); } }
public class ImportTestC { public void test(){ System.out.println("测试testc"); } }
public class ImportTestD { public void test(){ System.out.println("测试testD"); } }
public class SelfImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { return new String[]{"com.cxy.registersever.study.ImportTestC"}; } }
public class SelfImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { RootBeanDefinition root = new RootBeanDefinition(ImportTestD.class); registry.registerBeanDefinition("importTestD", root); } }
2 引入:
@SpringBootApplication @Import({ImportTest.class, SelfImportSelector.class, SelfImportBeanDefinitionRegistrar.class}) public class RegisterSeverApplication { public static void main(String[] args) { SpringApplication.run(RegisterSeverApplication.class, args); } }
3 测试:
@RestController public class Rest { @Autowired ImportTest importTest; @Autowired ImportTestC importTestC; @Autowired ImportTestD importTestD; @RequestMapping("/hello") public String get(){ importTest.test(); importTestC.test(); importTestD.test(); return "hello"; } }
idea可能会报importTestC ,importTestD找不到,但是不影响,执行方法控制台会打印相关内容