diff --git a/README.md b/README.md index 690e013..0b63ea4 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,10 @@ ### SpringBoot - [SpringBoot run方法解析](/docs/SpringBoot/Spring-Boot-Run.md) +- [SpringBoot 配置加载解析](/docs/SpringBoot/SpringBoot-application-load.md) +- [SpringBoot 自动装配](/docs/SpringBoot/SpringBoot-自动装配.md) +- [SpringBoot ConfigurationProperties](/docs/SpringBoot/SpringBoot-ConfigurationProperties.md) +- [SpringBoot 日志系统](/docs/SpringBoot/SpringBoot-LogSystem.md) ## MyBatis diff --git a/docs/SpringBoot/SpringBoot-ConfigurationProperties.md b/docs/SpringBoot/SpringBoot-ConfigurationProperties.md new file mode 100644 index 0000000..292d7ae --- /dev/null +++ b/docs/SpringBoot/SpringBoot-ConfigurationProperties.md @@ -0,0 +1,598 @@ +# SpringBoot ConfigurationProperties +- Author: [HuiFer](https://github.com/huifer) +- 源码阅读仓库: [SourceHot-spring-boot](https://github.com/SourceHot/spring-boot-read) + +- 本文主要对`org.springframework.boot.context.properties.ConfigurationProperties`进行分析 + +## ConfigurationProperties + +- 顶部注释 + +```java + * @see ConfigurationPropertiesScan + * @see ConstructorBinding + * @see ConfigurationPropertiesBindingPostProcessor + * @see EnableConfigurationProperties +``` + + + +看到`ConfigurationPropertiesScan` 去看看这个 + +## ConfigurationPropertiesScan + +```JAVA +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Import(ConfigurationPropertiesScanRegistrar.class) +@EnableConfigurationProperties +public @interface ConfigurationPropertiesScan {} +``` + +- 熟悉的**Import**注解 + + + +## ConfigurationPropertiesScanRegistrar + +![image-20200323094446756](/images/SpringBoot/image-20200323094446756.png) + +- debug没有抓到后续补充 + + + +## EnableConfigurationProperties + +```JAVA +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Import(EnableConfigurationPropertiesRegistrar.class) +public @interface EnableConfigurationProperties { +} +``` + + + + + +## EnableConfigurationPropertiesRegistrar + +- 该类会读取**spring.factories** + +- 中`org.springframework.boot.autoconfigure.EnableAutoConfiguration=\` 这样的 + + ``` + org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration + org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration$DispatcherServletConfiguration + org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration$DispatcherServletRegistrationConfiguration + org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration + org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration + org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter + org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration + org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$Jackson2ObjectMapperBuilderCustomizerConfiguration + org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration$StringHttpMessageConverterConfiguration + org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration + org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration + org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration + org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration + org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration + org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration + ``` + + + + + +```java + @Override + public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { + // 注册bean + registerInfrastructureBeans(registry); + // 配置属性Bean注册器 + ConfigurationPropertiesBeanRegistrar beanRegistrar = new ConfigurationPropertiesBeanRegistrar(registry); + // 循环注册 + getTypes(metadata).forEach(beanRegistrar::register); + } + + +``` + + + +### registerInfrastructureBeans + +```JAVA + static void registerInfrastructureBeans(BeanDefinitionRegistry registry) { + // 属性绑定后置处理器 + ConfigurationPropertiesBindingPostProcessor.register(registry); + // 属性校验器 + ConfigurationPropertiesBeanDefinitionValidator.register(registry); + ConfigurationBeanFactoryMetadata.register(registry); + } +``` + +- 此处操作逻辑基本相同,是否存在这个beanName 存在直接注册,不存在补充 + +#### ConfigurationPropertiesBindingPostProcessor.register(registry) + +```JAVA + public static void register(BeanDefinitionRegistry registry) { + Assert.notNull(registry, "Registry must not be null"); + // 是否存在 + if (!registry.containsBeanDefinition(BEAN_NAME)) { + GenericBeanDefinition definition = new GenericBeanDefinition(); + definition.setBeanClass(ConfigurationPropertiesBindingPostProcessor.class); + definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); + registry.registerBeanDefinition(BEAN_NAME, definition); + } + ConfigurationPropertiesBinder.register(registry); + } + +``` + + + +#### ConfigurationPropertiesBeanDefinitionValidator.register(registry) + +```JAVA + static void register(BeanDefinitionRegistry registry) { + Assert.notNull(registry, "Registry must not be null"); + if (!registry.containsBeanDefinition(BEAN_NAME)) { + GenericBeanDefinition definition = new GenericBeanDefinition(); + definition.setBeanClass(ConfigurationPropertiesBeanDefinitionValidator.class); + definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); + registry.registerBeanDefinition(BEAN_NAME, definition); + } + ConfigurationPropertiesBinder.register(registry); + } + +``` + + + +### getTypes(metadata).forEach(beanRegistrar::register) + +- 先看输入参数 **metadata** + +![image-20200323134135926](/images/SpringBoot/image-20200323134135926.png) + + + +- getTypes结果 + +![image-20200323134325955](/images/SpringBoot/image-20200323134325955.png) + + + + + +- 源码开始,先找出刚才的对象`org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration` + + ```JAVA + @Configuration(proxyBeanMethods = false) + @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) + @ConditionalOnClass(ServletRequest.class) + @ConditionalOnWebApplication(type = Type.SERVLET) + @EnableConfigurationProperties(ServerProperties.class) + @Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, + ServletWebServerFactoryConfiguration.EmbeddedTomcat.class, + ServletWebServerFactoryConfiguration.EmbeddedJetty.class, + ServletWebServerFactoryConfiguration.EmbeddedUndertow.class }) + public class ServletWebServerFactoryAutoConfiguration {} + ``` + + + +```JAVA + /** + * 找出 {@link EnableConfigurationProperties} 注解标记的中的属性值,并且返回值不是void + * @param metadata + * @return + */ + private Set> getTypes(AnnotationMetadata metadata) { + return + + metadata.getAnnotations().stream(EnableConfigurationProperties.class) + .flatMap((annotation) -> Arrays.stream(annotation.getClassArray(MergedAnnotation.VALUE))) + .filter((type) -> void.class != type).collect(Collectors.toSet()); + } + + +``` + +- 这里我们可以直接知道返回的是`@EnableConfigurationProperties(ServerProperties.class)` 的数据值: `ServerProperties.class` + + + +循环注册 + +```java + void register(Class type) { + MergedAnnotation annotation = MergedAnnotations + .from(type, SearchStrategy.TYPE_HIERARCHY).get(ConfigurationProperties.class); + register(type, annotation); + } + +``` + + + + + + + + + +--- + + + +## ConfigurationPropertiesBindingPostProcessor + +![image-20200323095626953](/images/SpringBoot/image-20200323095626953.png) + + + +### postProcessBeforeInitialization + +```JAVA + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + // 绑定 + bind(ConfigurationPropertiesBean.get(this.applicationContext, bean, beanName)); + return bean; + } + +``` + + + +- get + + ```java + public static ConfigurationPropertiesBean get(ApplicationContext applicationContext, Object bean, String beanName) { + // 寻找工厂方法 + Method factoryMethod = findFactoryMethod(applicationContext, beanName); + // 创建 ConfigurationPropertiesBean + return create(beanName, bean, bean.getClass(), factoryMethod); + } + + ``` + + + +```java + private static Method findFactoryMethod(ConfigurableListableBeanFactory beanFactory, String beanName) { + // 判断是否存在这个beanName + if (beanFactory.containsBeanDefinition(beanName)) { + // 获取bean定义 + BeanDefinition beanDefinition = beanFactory.getMergedBeanDefinition(beanName); + // 类型判断 + if (beanDefinition instanceof RootBeanDefinition) { + // 解析方法 + Method resolvedFactoryMethod = ((RootBeanDefinition) beanDefinition).getResolvedFactoryMethod(); + if (resolvedFactoryMethod != null) { + return resolvedFactoryMethod; + } + } + return findFactoryMethodUsingReflection(beanFactory, beanDefinition); + } + return null; + } + +``` + + + +```java + private static Method findFactoryMethodUsingReflection(ConfigurableListableBeanFactory beanFactory, + BeanDefinition beanDefinition) { + // 工厂方法 + String factoryMethodName = beanDefinition.getFactoryMethodName(); + // 工厂bean + String factoryBeanName = beanDefinition.getFactoryBeanName(); + if (factoryMethodName == null || factoryBeanName == null) { + return null; + } + // 转换对象 + Class factoryType = beanFactory.getType(factoryBeanName); + if (factoryType.getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) { + factoryType = factoryType.getSuperclass(); + } + AtomicReference factoryMethod = new AtomicReference<>(); + ReflectionUtils.doWithMethods(factoryType, (method) -> { + // 判断是否是需要的方法 + if (method.getName().equals(factoryMethodName)) { + // 设置方法 + factoryMethod.set(method); + } + }); + // 返回方法 + return factoryMethod.get(); + } + +``` + + + + + +### create + +- `org.springframework.boot.context.properties.ConfigurationPropertiesBean#create` + +```JAVA + private static ConfigurationPropertiesBean create(String name, Object instance, Class type, Method factory) { + // 找注解 + ConfigurationProperties annotation = findAnnotation(instance, type, factory, ConfigurationProperties.class); + if (annotation == null) { + return null; + } + // 找注解 + Validated validated = findAnnotation(instance, type, factory, Validated.class); + // 注解列表 + Annotation[] annotations = (validated != null) ? new Annotation[] { annotation, validated } + : new Annotation[] { annotation }; + // 类型解析 + ResolvableType bindType = (factory != null) ? ResolvableType.forMethodReturnType(factory) + : ResolvableType.forClass(type); + // 绑定结果对象 + Bindable bindTarget = Bindable.of(bindType).withAnnotations(annotations); + if (instance != null) { + bindTarget = bindTarget.withExistingValue(instance); + } + return new ConfigurationPropertiesBean(name, instance, annotation, bindTarget); + } + +``` + + + +- 第一个需要做的类: `org.springframework.boot.autoconfigure.web.ServerProperties` + + + +- `annotation` + +![image-20200323104711545](/images/SpringBoot/image-20200323104711545.png) + +- `bindType` + +![image-20200323104815305](/images/SpringBoot/image-20200323104815305.png) + +- 返回对象 + +![image-20200323105053757](/images/SpringBoot/image-20200323105053757.png) + +- 此时数据还没有进去 + + + + + +#### bind + +- 数据绑定 + +直接看结果 + +![image-20200323105155998](/images/SpringBoot/image-20200323105155998.png) + +- 上述配置和我在配置文件中写的配置一致 + + + +```yml +server: + port: 9999 + +``` + +- 具体方法: `org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor#bind` + +```java + private void bind(ConfigurationPropertiesBean bean) { + if (bean == null || hasBoundValueObject(bean.getName())) { + return; + } + Assert.state(bean.getBindMethod() == BindMethod.JAVA_BEAN, "Cannot bind @ConfigurationProperties for bean '" + + bean.getName() + "'. Ensure that @ConstructorBinding has not been applied to regular bean"); + try { + // 最终的绑定 + this.binder.bind(bean); + } + catch (Exception ex) { + throw new ConfigurationPropertiesBindException(bean, ex); + } + } + +``` + + + +```java +BindResult bind(ConfigurationPropertiesBean propertiesBean) { + // 最后的结果 + Bindable target = propertiesBean.asBindTarget(); + // 注解获取 + ConfigurationProperties annotation = propertiesBean.getAnnotation(); + // 获取处理器 + BindHandler bindHandler = getBindHandler(target, annotation); + // + return getBinder().bind(annotation.prefix(), target, bindHandler); + } +``` + +![image-20200323105830138](/images/SpringBoot/image-20200323105830138.png) + + + +##### findProperty + +```JAVA + private ConfigurationProperty findProperty(ConfigurationPropertyName name, Context context) { + if (name.isEmpty()) { + return null; + } + for (ConfigurationPropertySource source : context.getSources()) { + // 获取具体的一个属性值 + ConfigurationProperty property = source.getConfigurationProperty(name); + if (property != null) { + return property; + } + } + return null; + } + +``` + + + +- `org.springframework.boot.context.properties.source.SpringConfigurationPropertySource#getConfigurationProperty` + + ```JAVA + @Override + public ConfigurationProperty getConfigurationProperty(ConfigurationPropertyName name) { + PropertyMapping[] mappings = getMapper().map(name); + return find(mappings, name); + } + + ``` + + ```JAVA + + protected final ConfigurationProperty find(PropertyMapping[] mappings, ConfigurationPropertyName name) { + for (PropertyMapping candidate : mappings) { + if (candidate.isApplicable(name)) { + ConfigurationProperty result = find(candidate); + if (result != null) { + return result; + } + } + } + return null; + } + + ``` + + ```JAVA + private ConfigurationProperty find(PropertyMapping mapping) { + // 需要读取的配置信息的key + String propertySourceName = mapping.getPropertySourceName(); + // 信息的value + Object value = getPropertySource().getProperty(propertySourceName); + if (value == null) { + return null; + } + // 创建对象 + ConfigurationPropertyName configurationPropertyName = mapping.getConfigurationPropertyName(); + Origin origin = PropertySourceOrigin.get(this.propertySource, propertySourceName); + // 包装返回 + return ConfigurationProperty.of(configurationPropertyName, value, origin); + } + + ``` + + + + + + + +![image-20200323115408877](/images/SpringBoot/image-20200323115408877.png) + + + +![image-20200323115701118](/images/SpringBoot/image-20200323115701118.png) + + + +![image-20200323115711826](/images/SpringBoot/image-20200323115711826.png) + + + + + +##### getBindHandler + +```java +private BindHandler getBindHandler(Bindable target, ConfigurationProperties annotation) { + // 获取校验接口列表 + List validators = getValidators(target); + // 处理器 + BindHandler handler = new IgnoreTopLevelConverterNotFoundBindHandler(); + if (annotation.ignoreInvalidFields()) { + // 忽略错误的绑定处理器 + handler = new IgnoreErrorsBindHandler(handler); + } + if (!annotation.ignoreUnknownFields()) { + UnboundElementsSourceFilter filter = new UnboundElementsSourceFilter(); + // 未绑定元素处理器 + handler = new NoUnboundElementsBindHandler(handler, filter); + } + if (!validators.isEmpty()) { + // 校验绑定处理器 + handler = new ValidationBindHandler(handler, validators.toArray(new Validator[0])); + } + for (ConfigurationPropertiesBindHandlerAdvisor advisor : getBindHandlerAdvisors()) { + // handler + handler = advisor.apply(handler); + } + return handler; + } +``` + +- 最终获取得到的处理器 + +![image-20200323110603959](/images/SpringBoot/image-20200323110603959.png) + + + +- 最后的bind + +```java + private Object bindObject(ConfigurationPropertyName name, Bindable target, BindHandler handler, + Context context, boolean allowRecursiveBinding) { + // 获取属性 + ConfigurationProperty property = findProperty(name, context); + if (property == null && containsNoDescendantOf(context.getSources(), name) && context.depth != 0) { + return null; + } + AggregateBinder aggregateBinder = getAggregateBinder(target, context); + if (aggregateBinder != null) { + return bindAggregate(name, target, handler, context, aggregateBinder); + } + if (property != null) { + try { + return bindProperty(target, context, property); + } + catch (ConverterNotFoundException ex) { + // We might still be able to bind it using the recursive binders + Object instance = bindDataObject(name, target, handler, context, allowRecursiveBinding); + if (instance != null) { + return instance; + } + throw ex; + } + } + return bindDataObject(name, target, handler, context, allowRecursiveBinding); + } + +``` + + + + + +![image-20200323112945449](/images/SpringBoot/image-20200323112945449.png) + + + + + +配置信息到此绑定成功,关于如何处理集合相关的配置请各位读者自行学习 + +---- + diff --git a/docs/SpringBoot/SpringBoot-LogSystem.md b/docs/SpringBoot/SpringBoot-LogSystem.md new file mode 100644 index 0000000..2fa0c15 --- /dev/null +++ b/docs/SpringBoot/SpringBoot-LogSystem.md @@ -0,0 +1,511 @@ +# SpringBoot 日志系统 +- Author: [HuiFer](https://github.com/huifer) +- 源码阅读仓库: [SourceHot-spring-boot](https://github.com/SourceHot/spring-boot-read) + +- 包路径: `org.springframework.boot.logging` + +## 日志级别 +- 日志级别: `org.springframework.boot.logging.LogLevel` +```java +public enum LogLevel { + TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF +} +``` + + + + + +## java 日志实现 + +- `org.springframework.boot.logging.java.JavaLoggingSystem` + +![image-20200323144523848](/images/SpringBoot/image-20200323144523848.png) + +```JAVA + static { + // KEY : springBoot 定义的日志级别, value: jdk 定义的日志级别 + LEVELS.map(LogLevel.TRACE, Level.FINEST); + LEVELS.map(LogLevel.DEBUG, Level.FINE); + LEVELS.map(LogLevel.INFO, Level.INFO); + LEVELS.map(LogLevel.WARN, Level.WARNING); + LEVELS.map(LogLevel.ERROR, Level.SEVERE); + LEVELS.map(LogLevel.FATAL, Level.SEVERE); + LEVELS.map(LogLevel.OFF, Level.OFF); + } +``` + +- LEVELS 对象 + + ```java + protected static class LogLevels { + + /** + * key : SpringBoot 中定义的日志级别, value: 其他日志框架的日志级别 + */ + private final Map systemToNative; + + /** + * key : 其他日志框架的日志级别 , value: springBoot 中定义中定义的日志级别 + */ + private final Map nativeToSystem; + } + ``` + + + +## LoggingSystem + +- 抽象类 +- `org.springframework.boot.logging.LoggingSystem` + +- 一个map对象: `SYSTEMS` + +```JAVA + /** + * key: 第三方日志框架的类 value: springBoot 中的处理类 + */ + private static final Map SYSTEMS; + + static { + Map systems = new LinkedHashMap<>(); + systems.put("ch.qos.logback.core.Appender", "org.springframework.boot.logging.logback.LogbackLoggingSystem"); + systems.put("org.apache.logging.log4j.core.impl.Log4jContextFactory", + "org.springframework.boot.logging.log4j2.Log4J2LoggingSystem"); + systems.put("java.util.logging.LogManager", "org.springframework.boot.logging.java.JavaLoggingSystem"); + SYSTEMS = Collections.unmodifiableMap(systems); + } + +``` + + + +- 各个抽象方法 + + + +| 方法名称 | 作用 | +| ----------------------- | ---------------------------------- | +| beforeInitialize | 初始化之前调用,目的是减少日志输出 | +| initialize | 初始化日志 | +| cleanUp | 清除日志 | +| getShutdownHandler | | +| getSupportedLogLevels | 获取支持的日志级别 | +| setLogLevel | 设置日志级别 | +| getLoggerConfigurations | 获取日志配置 | + + + +### get + +```java +public static LoggingSystem get(ClassLoader classLoader) { + // 获取系统属性 + String loggingSystem = System.getProperty(SYSTEM_PROPERTY); + + if (StringUtils.hasLength(loggingSystem)) { + // 是不是NONE + if (NONE.equals(loggingSystem)) { + // 空的日志系统 + return new NoOpLoggingSystem(); + } + return get(classLoader, loggingSystem); + } + // 循环所有日志, + return SYSTEMS.entrySet().stream().filter((entry) -> ClassUtils.isPresent(entry.getKey(), classLoader)) + .map((entry) -> + // 实例化具体日志 + get(classLoader, entry.getValue())).findFirst() + .orElseThrow(() -> new IllegalStateException("No suitable logging system located")); + } +``` + +- 实例化日志系统 + +```java + private static LoggingSystem get(ClassLoader classLoader, String loggingSystemClass) { + try { + Class systemClass = ClassUtils.forName(loggingSystemClass, classLoader); + Constructor constructor = systemClass.getDeclaredConstructor(ClassLoader.class); + constructor.setAccessible(true); + return (LoggingSystem) constructor.newInstance(classLoader); + } + catch (Exception ex) { + throw new IllegalStateException(ex); + } + } + +``` + + + + + +![image-20200323151409473](/images/SpringBoot/image-20200323151409473.png) + + + +- 默认日志: `org.springframework.boot.logging.logback.LogbackLoggingSystem` + +### beforeInitialize + +- 初始化之前 + + ![image-20200323154205484](/images/SpringBoot/image-20200323154205484.png) + + - 链路 + 1. `org.springframework.boot.context.logging.LoggingApplicationListener#onApplicationEvent` + 2. `org.springframework.boot.context.logging.LoggingApplicationListener#onApplicationStartingEvent` + 3. `org.springframework.boot.logging.LoggingSystem#beforeInitialize` + +- 因为前文中我们已知对象是:`org.springframework.boot.logging.logback.LogbackLoggingSystem` 直接看这个类的**`beforeInitialize`**方法 + + ```JAVA + @Override + public void beforeInitialize() { + // 日志上下文 + LoggerContext loggerContext = getLoggerContext(); + // 是否初始化 + if (isAlreadyInitialized(loggerContext)) { + return; + } + // 父类方法 + super.beforeInitialize(); + // 添加过滤器 + loggerContext.getTurboFilterList().add(FILTER); + } + + ``` + + + + + +- 初始化之前的的操作完成了初始化方法开始 + +### initialize + +- `org.springframework.boot.context.logging.LoggingApplicationListener#onApplicationEnvironmentPreparedEvent` + + ```JAVA + private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) { + if (this.loggingSystem == null) { + this.loggingSystem = LoggingSystem.get(event.getSpringApplication().getClassLoader()); + } + initialize(event.getEnvironment(), event.getSpringApplication().getClassLoader()); + } + + ``` + + + +- `org.springframework.boot.context.logging.LoggingApplicationListener#initializeSystem` + + ```JAVA + protected void initialize(ConfigurableEnvironment environment, ClassLoader classLoader) { + new LoggingSystemProperties(environment).apply(); + this.logFile = LogFile.get(environment); + if (this.logFile != null) { + this.logFile.applyToSystemProperties(); + } + this.loggerGroups = new LoggerGroups(DEFAULT_GROUP_LOGGERS); + // 早期 的日志级别 + initializeEarlyLoggingLevel(environment); + // 初始化日志系统 + initializeSystem(environment, this.loggingSystem, this.logFile); + // 初始化日志级别 + initializeFinalLoggingLevels(environment, this.loggingSystem); + registerShutdownHookIfNecessary(environment, this.loggingSystem); + } + + ``` + + ```JAVA + private void initializeSystem(ConfigurableEnvironment environment, LoggingSystem system, LogFile logFile) { + LoggingInitializationContext initializationContext = new LoggingInitializationContext(environment); + String logConfig = environment.getProperty(CONFIG_PROPERTY); + if (ignoreLogConfig(logConfig)) { + // 日志系统初始化 + system.initialize(initializationContext, null, logFile); + } + else { + try { + ResourceUtils.getURL(logConfig).openStream().close(); + system.initialize(initializationContext, logConfig, logFile); + } + catch (Exception ex) { + // NOTE: We can't use the logger here to report the problem + System.err.println("Logging system failed to initialize using configuration from '" + logConfig + "'"); + ex.printStackTrace(System.err); + throw new IllegalStateException(ex); + } + } + } + + ``` + + + +- `org.springframework.boot.logging.logback.LogbackLoggingSystem#initialize` + +```java + @Override + public void initialize(LoggingInitializationContext initializationContext, String configLocation, LogFile logFile) { + LoggerContext loggerContext = getLoggerContext(); + if (isAlreadyInitialized(loggerContext)) { + return; + } + // 日志初始化 + super.initialize(initializationContext, configLocation, logFile); + loggerContext.getTurboFilterList().remove(FILTER); + markAsInitialized(loggerContext); + if (StringUtils.hasText(System.getProperty(CONFIGURATION_FILE_PROPERTY))) { + getLogger(LogbackLoggingSystem.class.getName()).warn("Ignoring '" + CONFIGURATION_FILE_PROPERTY + + "' system property. Please use 'logging.config' instead."); + } + } + +``` + + + +- `org.springframework.boot.logging.AbstractLoggingSystem#initializeWithConventions` + + ```JAVA + private void initializeWithConventions(LoggingInitializationContext initializationContext, LogFile logFile) { + String config = getSelfInitializationConfig(); + if (config != null && logFile == null) { + // self initialization has occurred, reinitialize in case of property changes + reinitialize(initializationContext); + return; + } + if (config == null) { + config = getSpringInitializationConfig(); + } + if (config != null) { + loadConfiguration(initializationContext, config, logFile); + return; + } + // 加载默认配置 + loadDefaults(initializationContext, logFile); + } + + ``` + + - `org.springframework.boot.logging.logback.LogbackLoggingSystem#loadDefaults` + + ```JAVA + @Override + protected void loadDefaults(LoggingInitializationContext initializationContext, LogFile logFile) { + LoggerContext context = getLoggerContext(); + stopAndReset(context); + boolean debug = Boolean.getBoolean("logback.debug"); + if (debug) { + StatusListenerConfigHelper.addOnConsoleListenerInstance(context, new OnConsoleStatusListener()); + } + LogbackConfigurator configurator = debug ? new DebugLogbackConfigurator(context) + : new LogbackConfigurator(context); + Environment environment = initializationContext.getEnvironment(); + context.putProperty(LoggingSystemProperties.LOG_LEVEL_PATTERN, + environment.resolvePlaceholders("${logging.pattern.level:${LOG_LEVEL_PATTERN:%5p}}")); + context.putProperty(LoggingSystemProperties.LOG_DATEFORMAT_PATTERN, environment.resolvePlaceholders( + "${logging.pattern.dateformat:${LOG_DATEFORMAT_PATTERN:yyyy-MM-dd HH:mm:ss.SSS}}")); + context.putProperty(LoggingSystemProperties.ROLLING_FILE_NAME_PATTERN, environment + .resolvePlaceholders("${logging.pattern.rolling-file-name:${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz}")); + new DefaultLogbackConfiguration(initializationContext, logFile).apply(configurator); + context.setPackagingDataEnabled(true); + } + + ``` + + + + + +```JAVA + @Override + public void initialize(LoggingInitializationContext initializationContext, String configLocation, LogFile logFile) { + LoggerContext loggerContext = getLoggerContext(); + // 是否加载过 + if (isAlreadyInitialized(loggerContext)) { + return; + } + // 日志初始化 + super.initialize(initializationContext, configLocation, logFile); + // 删除 FILTER + loggerContext.getTurboFilterList().remove(FILTER); + // 初始化标记 + markAsInitialized(loggerContext); + if (StringUtils.hasText(System.getProperty(CONFIGURATION_FILE_PROPERTY))) { + getLogger(LogbackLoggingSystem.class.getName()).warn("Ignoring '" + CONFIGURATION_FILE_PROPERTY + + "' system property. Please use 'logging.config' instead."); + } + } +``` + + + +标记`markAsInitialized` + +```JAVA + private void markAsInitialized(LoggerContext loggerContext) { + loggerContext.putObject(LoggingSystem.class.getName(), new Object()); + } + +``` + + + +此时日志初始化完成 + + + + + +### 默认配置文件 + +- `getStandardConfigLocations` 这个方法定义了默认配置文件有哪些 + +```java + @Override + protected String[] getStandardConfigLocations() { + return new String[] { "logback-test.groovy", "logback-test.xml", "logback.groovy", "logback.xml" }; + } + +``` + + + + + +- 切回`org.springframework.boot.logging.AbstractLoggingSystem#initializeWithConventions`方法 + + + +- 添加依赖 + +```XML + + org.springframework.boot + spring-boot-starter-logging + ${revision} + + +``` + +- 添加配置文件 + +![image-20200323161442058](/images/SpringBoot/image-20200323161442058.png) + + + + + +![image-20200323161522570](/images/SpringBoot/image-20200323161522570.png) + +- 此时配置文件地址出现了 + + + +```JAVA + protected String getSelfInitializationConfig() { + // 寻找配置文件 + return findConfig(getStandardConfigLocations()); + } + +``` + +```JAVA + @Override + protected String[] getStandardConfigLocations() { + return new String[] { "logback-test.groovy", "logback-test.xml", "logback.groovy", "logback.xml" }; + } + +``` + +```JAVA + private String findConfig(String[] locations) { + for (String location : locations) { + ClassPathResource resource = new ClassPathResource(location, this.classLoader); + if (resource.exists()) { + return "classpath:" + location; + } + } + return null; + } + +``` + +- 此时自定义配置文件如何获取的已经明了 + + + +#### reinitialize + +```JAVA + @Override + protected void reinitialize(LoggingInitializationContext initializationContext) { + // 日志上下文重新设置 + getLoggerContext().reset(); + getLoggerContext().getStatusManager().clear(); + // 加载配置文件 + loadConfiguration(initializationContext, getSelfInitializationConfig(), null); + } + +``` + + + +```JAVA + @Override + protected void loadConfiguration(LoggingInitializationContext initializationContext, String location, + LogFile logFile) { + // 父类方法 + super.loadConfiguration(initializationContext, location, logFile); + // 获取上下文 + LoggerContext loggerContext = getLoggerContext(); + // 停止并且重启 + stopAndReset(loggerContext); + try { + // 配置文件加载 + configureByResourceUrl(initializationContext, loggerContext, ResourceUtils.getURL(location)); + } + catch (Exception ex) { + throw new IllegalStateException("Could not initialize Logback logging from " + location, ex); + } + List statuses = loggerContext.getStatusManager().getCopyOfStatusList(); + StringBuilder errors = new StringBuilder(); + for (Status status : statuses) { + if (status.getLevel() == Status.ERROR) { + errors.append((errors.length() > 0) ? String.format("%n") : ""); + errors.append(status.toString()); + } + } + if (errors.length() > 0) { + throw new IllegalStateException(String.format("Logback configuration error detected: %n%s", errors)); + } + } + +``` + + + +```JAVA + private void configureByResourceUrl(LoggingInitializationContext initializationContext, LoggerContext loggerContext, + URL url) throws JoranException { + if (url.toString().endsWith("xml")) { + // logback 日志操作 + JoranConfigurator configurator = new SpringBootJoranConfigurator(initializationContext); + // 设置上下文 + configurator.setContext(loggerContext); + // 执行配置 + configurator.doConfigure(url); + } + else { + new ContextInitializer(loggerContext).configureByResource(url); + } + } + +``` + +- 执行配置属于logback 操作源码不在此进行分析 \ No newline at end of file diff --git a/docs/SpringBoot/SpringBoot-application-load.md b/docs/SpringBoot/SpringBoot-application-load.md new file mode 100644 index 0000000..c7d6cf3 --- /dev/null +++ b/docs/SpringBoot/SpringBoot-application-load.md @@ -0,0 +1,280 @@ +# Spring Boot application 文件加载 +- Author: [HuiFer](https://github.com/huifer) +- 源码阅读仓库: [SourceHot-spring-boot](https://github.com/SourceHot/spring-boot-read) + + + + + +## 如何找到这个加载的过程 + +1. 创建配置文件`application.yml` + +2. 全局搜索yml + + ![image-20200319083048849](/images/SpringBoot/image-20200319083048849.png) + +3. 换成`properties`搜索 + + ![image-20200319083140225](/images/SpringBoot/image-20200319083140225.png) + +4. 我们以`yml`为例打上断点开始源码追踪 + + + + + +看到调用堆栈 + +![image-20200319083345067](/images/SpringBoot/image-20200319083345067.png) + + + +- 一步一步回上去看如何调用具体方法的 + +## ConfigFileApplicationListener + +- 配置文件监听器 + + + +### 调用过程 + +![image-20200319082131146](/images/SpringBoot/image-20200319082131146.png) + +![image-20200319082544653](/images/SpringBoot/image-20200319082544653.png) + + + +`org.springframework.boot.context.config.ConfigFileApplicationListener#addPropertySources` + +```java +protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) { + RandomValuePropertySource.addToEnvironment(environment); + // 加载器加载信息 + new Loader(environment, resourceLoader).load(); + } +``` + + + + + + + +### Loader + +- 配置资源加载器 + + + +构造方法 + +```java + Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) { + // 环境配置 + this.environment = environment; + // 占位符处理器 + this.placeholdersResolver = new PropertySourcesPlaceholdersResolver(this.environment); + // 资源加载器 + this.resourceLoader = (resourceLoader != null) ? resourceLoader : new DefaultResourceLoader(); + // 配置信息加载器初始化 + this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class, + getClass().getClassLoader()); + } + +``` + + + +- 熟悉的老朋友`this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class, getClass().getClassLoader())`, 看看**`spring.factories`**有什么 + + - 搜索目标: `org.springframework.boot.env.PropertySourceLoader` + + ![image-20200319084141748](/images/SpringBoot/image-20200319084141748.png) + + + +![image-20200319084151997](/images/SpringBoot/image-20200319084151997.png) + +观察发现里面有一个`YamlPropertySourceLoader`和我们之前找yml字符串的时候找到的类是一样的。说明搜索方式没有什么问题。 + +![image-20200319084357652](/images/SpringBoot/image-20200319084357652.png) + +初始化完成,后续进行解析了 + + + + + +### load 方法 + +```java + void load() { + FilteredPropertySource.apply(this.environment, DEFAULT_PROPERTIES, LOAD_FILTERED_PROPERTY, + (defaultProperties) -> { + this.profiles = new LinkedList<>(); + this.processedProfiles = new LinkedList<>(); + this.activatedProfiles = false; + this.loaded = new LinkedHashMap<>(); + // 初始化配置文件 + initializeProfiles(); + while (!this.profiles.isEmpty()) { + Profile profile = this.profiles.poll(); + if (isDefaultProfile(profile)) { + addProfileToEnvironment(profile.getName()); + } + load(profile, this::getPositiveProfileFilter, + addToLoaded(MutablePropertySources::addLast, false)); + this.processedProfiles.add(profile); + } + load(null, this::getNegativeProfileFilter, addToLoaded(MutablePropertySources::addFirst, true)); + addLoadedPropertySources(); + applyActiveProfiles(defaultProperties); + }); + } + +``` + +### initializeProfiles + +- 初始化`private Deque profiles;` 属性 +- ![image-20200319084902957](/images/SpringBoot/image-20200319084902957.png) + + + + + +### load + +- `org.springframework.boot.context.config.ConfigFileApplicationListener.Loader#load(org.springframework.boot.context.config.ConfigFileApplicationListener.Profile, org.springframework.boot.context.config.ConfigFileApplicationListener.DocumentFilterFactory, org.springframework.boot.context.config.ConfigFileApplicationListener.DocumentConsumer)` + + + +```JAVA +private void load(Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) { + getSearchLocations().forEach( + // 本地路径 + (location) -> { + // 是不是文件夹 + boolean isFolder = location.endsWith("/"); + // 文件名,默认application + Set names = isFolder ? getSearchNames() : NO_SEARCH_NAMES; + // 循环加载 + names.forEach((name) -> { + load(location, name, profile, filterFactory, consumer); + }); + }); + } +``` + +- 资源路径可能性 + +![image-20200319085446640](/images/SpringBoot/image-20200319085446640.png) + + + +该方法采用循环每个路径下面都去尝试一遍 + + + +- 中间过程省略,我们直接看最后的加载行为 + - `org.springframework.boot.context.config.ConfigFileApplicationListener.Loader#loadDocuments` + +```java + private List loadDocuments(PropertySourceLoader loader, String name, Resource resource) + throws IOException { + // 文档的缓存key + DocumentsCacheKey cacheKey = new DocumentsCacheKey(loader, resource); + // 文档信息 + List documents = this.loadDocumentsCache.get(cacheKey); + if (documents == null) { + // 执行加载,将配置文件读取返回 + List> loaded = loader.load(name, resource); + // 数据转换 + documents = asDocuments(loaded); + // 缓存设置 + this.loadDocumentsCache.put(cacheKey, documents); + } + return documents; + } + +``` + + + +此处的`loader.load()`调用具体的loader实现类进行执行方法 + + + + + +### yml 解析 + +```java + @Override + public List> load(String name, Resource resource) throws IOException { + if (!ClassUtils.isPresent("org.yaml.snakeyaml.Yaml", null)) { + throw new IllegalStateException( + "Attempted to load " + name + " but snakeyaml was not found on the classpath"); + } + // 将资源转换成集合对象 + List> loaded = new OriginTrackedYamlLoader(resource).load(); + if (loaded.isEmpty()) { + return Collections.emptyList(); + } + List> propertySources = new ArrayList<>(loaded.size()); + for (int i = 0; i < loaded.size(); i++) { + String documentNumber = (loaded.size() != 1) ? " (document #" + i + ")" : ""; + // 放入返回结果中 + propertySources.add(new OriginTrackedMapPropertySource(name + documentNumber, + Collections.unmodifiableMap(loaded.get(i)), true)); + } + return propertySources; + } + +``` + + + +![image-20200319090446231](/images/SpringBoot/image-20200319090446231.png) + + + + + +- `PropertiesPropertySourceLoader`解析同理不在次展开描述了 + + + +### asDocuments + +```JAVA + /** + * 将 {@link PropertySource} 转换成 {@link Document} + * @param loaded + * @return + */ + private List asDocuments(List> loaded) { + if (loaded == null) { + return Collections.emptyList(); + } + return loaded.stream().map( + // 循环创建新对象 + (propertySource) -> { + // 对象创建 + Binder binder = new Binder(ConfigurationPropertySources.from(propertySource), + this.placeholdersResolver); + /** + * 通过 {@link Binder} 将数据进行绑定,创建 {@link Document}进行返回 + */ + return new Document(propertySource, binder.bind("spring.profiles", STRING_ARRAY).orElse(null), + getProfiles(binder, ACTIVE_PROFILES_PROPERTY), + getProfiles(binder, INCLUDE_PROFILES_PROPERTY)); + }).collect(Collectors.toList()); + } + +``` + +----- + diff --git a/docs/SpringBoot/SpringBoot-自动装配.md b/docs/SpringBoot/SpringBoot-自动装配.md new file mode 100644 index 0000000..8591687 --- /dev/null +++ b/docs/SpringBoot/SpringBoot-自动装配.md @@ -0,0 +1,631 @@ +# Spring Boot 自动装配 +- Author: [HuiFer](https://github.com/huifer) +- 源码阅读仓库: [SourceHot-spring-boot](https://github.com/SourceHot/spring-boot-read) + + +- `org.springframework.boot.autoconfigure.SpringBootApplication` +```java +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +@SpringBootConfiguration +@EnableAutoConfiguration +@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), + @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) +public @interface SpringBootApplication { + + @AliasFor(annotation = EnableAutoConfiguration.class) + Class[] exclude() default {}; + + @AliasFor(annotation = EnableAutoConfiguration.class) + String[] excludeName() default {}; + + @AliasFor(annotation = ComponentScan.class, attribute = "basePackages") + String[] scanBasePackages() default {}; + + @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses") + Class[] scanBasePackageClasses() default {}; + + @AliasFor(annotation = Configuration.class) + boolean proxyBeanMethods() default true; + +} + +``` + +## EnableAutoConfiguration + +```java +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +@AutoConfigurationPackage +@Import(AutoConfigurationImportSelector.class) +public @interface EnableAutoConfiguration { + +} +``` + +## AutoConfigurationImportSelector + +- 类图 + +![image-20200320150642022](/images/SpringBoot/image-20200320150642022.png) + +## getAutoConfigurationMetadata() + +```JAVA + @Override + public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) { + Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector, + () -> String.format("Only %s implementations are supported, got %s", + AutoConfigurationImportSelector.class.getSimpleName(), + deferredImportSelector.getClass().getName())); + AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector) + .getAutoConfigurationEntry( + // 加载配置元数据 + getAutoConfigurationMetadata(), annotationMetadata); + this.autoConfigurationEntries.add(autoConfigurationEntry); + for (String importClassName : autoConfigurationEntry.getConfigurations()) { + this.entries.putIfAbsent(importClassName, annotationMetadata); + } + } + + + private AutoConfigurationMetadata getAutoConfigurationMetadata() { + if (this.autoConfigurationMetadata == null) { + // 加载配置信息 + this.autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader); + } + return this.autoConfigurationMetadata; + } + +``` + +- `org.springframework.boot.autoconfigure.AutoConfigurationMetadataLoader#loadMetadata(java.lang.ClassLoader)` + + ```JAVA + static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) { + try { + + // 获取资源路径 + Enumeration urls = (classLoader != null) ? classLoader.getResources(path) + : ClassLoader.getSystemResources(path); + Properties properties = new Properties(); + while (urls.hasMoreElements()) { + properties.putAll(PropertiesLoaderUtils.loadProperties(new UrlResource(urls.nextElement()))); + } + return loadMetadata(properties); + } + catch (IOException ex) { + throw new IllegalArgumentException("Unable to load @ConditionalOnClass location [" + path + "]", ex); + } + } + + ``` + + ![image-20200320160423991](/images/SpringBoot/image-20200320160423991.png) + + + +- `protected static final String PATH = "META-INF/spring-autoconfigure-metadata.properties";` + + 注意: 这个文件在**target**编译后的文件夹中 + + 相关 Issues : https://github.com/spring-projects/spring-boot/issues/11282 + + + +- 自动装配 + + `spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories` + + 该文件内存有: + + ``` + # Auto Configure + org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ + org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\ + org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\ + org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\ + org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\ + org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\ + ``` + + + + + +![image-20200320162835665](/images/SpringBoot/image-20200320162835665.png) + +同样找一下redis + +![image-20200320163001728](/images/SpringBoot/image-20200320163001728.png) + + + + + +- 仔细看`org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration`类 + + + +先说注解 + +```JAVA +@Configuration(proxyBeanMethods = false) +@ConditionalOnClass(RedisOperations.class) +@EnableConfigurationProperties(RedisProperties.class) +@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class }) +``` + + + +### EnableConfigurationProperties + +`自动映射一个POJO到Spring Boot配置文件(默认是application.properties文件)的属性集。` + +- `org.springframework.boot.autoconfigure.data.redis.RedisProperties` +- 部分redis配置属性 + +```JAVA +@ConfigurationProperties(prefix = "spring.redis") +public class RedisProperties { + + /** + * Database index used by the connection factory. + */ + private int database = 0; + + /** + * Connection URL. Overrides host, port, and password. User is ignored. Example: + * redis://user:password@example.com:6379 + */ + private String url; + + /** + * Redis server host. + */ + private String host = "localhost"; + + /** + * Login password of the redis server. + */ + private String password; + + /** + * Redis server port. + */ + private int port = 6379; + + /** + * Whether to enable SSL support. + */ + private boolean ssl; + + /** + * Connection timeout. + */ + private Duration timeout; + + /** + * Client name to be set on connections with CLIENT SETNAME. + */ + private String clientName; + + + +} +``` + + + +- 找到一个我们用相同方式去寻找到别的一些属性处理如`org.springframework.boot.autoconfigure.jdbc.JdbcProperties` 具体展开请各位读者自行了解了 + + + + + + + +### AnnotationMetadata + +回过头继续我们的主要流程 + +- `org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#process` + + ![image-20200320163806852](/images/SpringBoot/image-20200320163806852.png) + +再此之前我们看过了`getAutoConfigurationMetadata()`的相关操作 + +关注 `AnnotationMetadata annotationMetadata` 存储了一些什么 + +![image-20200320164145286](/images/SpringBoot/image-20200320164145286.png) + +这里简单理解 + +1. mergedAnnotations 类相关的注解信息 +2. annotationTypes 在启动类上的注解列表 + + + + + +### getAutoConfigurationEntry + +```JAVA + protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, + AnnotationMetadata annotationMetadata) { + if (!isEnabled(annotationMetadata)) { + return EMPTY_ENTRY; + } + // 获取注解属性值 + AnnotationAttributes attributes = getAttributes(annotationMetadata); + // 获取候选配置信息 + List configurations = getCandidateConfigurations(annotationMetadata, attributes); + // 删除重复配置 + configurations = removeDuplicates(configurations); + // 获取 exclude 属性 + Set exclusions = getExclusions(annotationMetadata, attributes); + // 校验 exclude 类 + checkExcludedClasses(configurations, exclusions); + // 配置中删除 exclude 的属性值 + configurations.removeAll(exclusions); + // 过滤 + configurations = filter(configurations, autoConfigurationMetadata); + // 触发自动配置事件 + fireAutoConfigurationImportEvents(configurations, exclusions); + // 返回 + return new AutoConfigurationEntry(configurations, exclusions); + } + +``` + + + + + +### getAttributes + +```java + protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) { + // name = org.springframework.boot.autoconfigure.EnableAutoConfiguration , 这是一个固定的值 + String name = getAnnotationClass().getName(); + // 获取注解的属性 + AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name, true)); + Assert.notNull(attributes, () -> "No auto-configuration attributes found. Is " + metadata.getClassName() + + " annotated with " + ClassUtils.getShortName(name) + "?"); + return attributes; + } + +``` + + + +![image-20200320171138431](/images/SpringBoot/image-20200320171138431.png) + + + +### getCandidateConfigurations + +- 读取`spring.factories`数据 + +```java + protected List getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { + // 读取 org.springframework.boot.autoconfigure.EnableAutoConfiguration 相关配置 + List configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), + getBeanClassLoader()); + Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you " + + "are using a custom packaging, make sure that file is correct."); + return configurations; + } + +``` + +![image-20200320171734270](/images/SpringBoot/image-20200320171734270.png) + +- 第一个是我自己写的一个测试用 + + ```properties + org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ + org.sourcehot.service.HelloServiceAutoConfiguration + ``` + + + + + + + +### removeDuplicates + +- new 两个对象直接做数据转换,去重 + +```JAVA + protected final List removeDuplicates(List list) { + return new ArrayList<>(new LinkedHashSet<>(list)); + } + +``` + + + + + +### getExclusions + +```JAVA + protected Set getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) { + Set excluded = new LinkedHashSet<>(); + // 获取属性 exclude 值转换成list + excluded.addAll(asList(attributes, "exclude")); + // 获取属性 excludeName 值转换成list + excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName"))); + // 获取 SpringBoot 本身的忽略配置属性 + excluded.addAll(getExcludeAutoConfigurationsProperty()); + return excluded; + } + +``` + + + +### getExcludeAutoConfigurationsProperty + +```JAVA + private List getExcludeAutoConfigurationsProperty() { + if (getEnvironment() instanceof ConfigurableEnvironment) { + Binder binder = Binder.get(getEnvironment()); + // 取出 "spring.autoconfigure.exclude" 转换成list + return binder.bind(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class).map(Arrays::asList) + .orElse(Collections.emptyList()); + } + String[] excludes = getEnvironment().getProperty(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class); + return (excludes != null) ? Arrays.asList(excludes) : Collections.emptyList(); + } + +``` + + + +![image-20200323080611527](/images/SpringBoot/image-20200323080611527.png) + + + +- 修改启动类 + + ```JAVA + @SpringBootApplication(excludeName = { "org.sourcehot.service.HelloServiceAutoConfiguration" }) + + ``` + + ![image-20200323081009823](/images/SpringBoot/image-20200323081009823.png) + +### checkExcludedClasses + + + +```JAVA + private void checkExcludedClasses(List configurations, Set exclusions) { + List invalidExcludes = new ArrayList<>(exclusions.size()); + for (String exclusion : exclusions) { + // + if (ClassUtils.isPresent(exclusion, getClass().getClassLoader()) && !configurations.contains(exclusion)) { + invalidExcludes.add(exclusion); + } + } + if (!invalidExcludes.isEmpty()) { + // 处理忽略的类 + handleInvalidExcludes(invalidExcludes); + } + } + +``` + + + +- `configurations.removeAll(exclusions)` + + 移除忽略的类 + +### filter + +```JAVA + private List filter(List configurations, AutoConfigurationMetadata autoConfigurationMetadata) { + long startTime = System.nanoTime(); + String[] candidates = StringUtils.toStringArray(configurations); + boolean[] skip = new boolean[candidates.length]; + boolean skipped = false; + // 获取 AutoConfigurationImportFilter 相关配置 + for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) { + // 执行 aware 相关接口 + invokeAwareMethods(filter); + // 比较 + boolean[] match = filter.match(candidates, autoConfigurationMetadata); + for (int i = 0; i < match.length; i++) { + if (!match[i]) { + skip[i] = true; + candidates[i] = null; + skipped = true; + } + } + } + if (!skipped) { + return configurations; + } + List result = new ArrayList<>(candidates.length); + for (int i = 0; i < candidates.length; i++) { + if (!skip[i]) { + result.add(candidates[i]); + } + } + if (logger.isTraceEnabled()) { + int numberFiltered = configurations.size() - result.size(); + logger.trace("Filtered " + numberFiltered + " auto configuration class in " + + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms"); + } + return new ArrayList<>(result); + } + +``` + + + +- `getAutoConfigurationImportFilters()` 从`spring.factories` 获取 `AutoConfigurationImportFilter`的接口 + + + +![image-20200323081903145](/images/SpringBoot/image-20200323081903145.png) + + + +- 循环内执行`Aware`系列接口 + + + +`match`方法: `org.springframework.boot.autoconfigure.AutoConfigurationImportFilter#match` + +- `filter.match(candidates, autoConfigurationMetadata)` 比较判断哪些是需要自动注入的类 + + + + + +![image-20200323082553595](/images/SpringBoot/image-20200323082553595.png) + + + + + + + +### fireAutoConfigurationImportEvents + +```JAVA + private void fireAutoConfigurationImportEvents(List configurations, Set exclusions) { + // 获取自动配置的监听器列表 + List listeners = getAutoConfigurationImportListeners(); + if (!listeners.isEmpty()) { + // 创建 自动配置事件 + AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions); + for (AutoConfigurationImportListener listener : listeners) { + // 执行 Aware 相关接口 + invokeAwareMethods(listener); + // 监听器执行自动配置事件 + listener.onAutoConfigurationImportEvent(event); + } + } + } + +``` + +![image-20200323083149737](/images/SpringBoot/image-20200323083149737.png) + + + +- `AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);` + +![image-20200323083247061](/images/SpringBoot/image-20200323083247061.png) + +- `org.springframework.boot.autoconfigure.AutoConfigurationImportListener#onAutoConfigurationImportEvent` 在执行自动配置时触发 , 实现类只有 **`ConditionEvaluationReportAutoConfigurationImportListener`** + + ```JAVA + @Override + public void onAutoConfigurationImportEvent(AutoConfigurationImportEvent event) { + if (this.beanFactory != null) { + ConditionEvaluationReport report = ConditionEvaluationReport.get(this.beanFactory); + // 记录需要加载的配置 + report.recordEvaluationCandidates(event.getCandidateConfigurations()); + // 记录不需要加载的配置 + report.recordExclusions(event.getExclusions()); + } + } + + ``` + + + +![image-20200323083656670](/images/SpringBoot/image-20200323083656670.png) + + + + + +- 初始化完 + + + + + +## process + +- `org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#process` + + + +![image-20200323084922159](/images/SpringBoot/image-20200323084922159.png) + +- 后续的一些行为相对简单,直接放个源码了. + +```JAVA +@Override + public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) { + Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector, + () -> String.format("Only %s implementations are supported, got %s", + AutoConfigurationImportSelector.class.getSimpleName(), + deferredImportSelector.getClass().getName())); + // 自动装配信息 + AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector) + .getAutoConfigurationEntry( + // 加载配置元数据 + getAutoConfigurationMetadata(), annotationMetadata); + this.autoConfigurationEntries.add(autoConfigurationEntry); + // 循环需要自动注入的类 + for (String importClassName : autoConfigurationEntry.getConfigurations()) { + // 继续放入k,v + this.entries.putIfAbsent(importClassName, annotationMetadata); + } + } +``` + + + + + + + +## selectImports + +```java + @Override + public Iterable selectImports() { + if (this.autoConfigurationEntries.isEmpty()) { + return Collections.emptyList(); + } + // 获取忽略的类 + Set allExclusions = this.autoConfigurationEntries.stream() + .map(AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet()); + + // 获取需要注入的类 + Set processedConfigurations = this.autoConfigurationEntries.stream() + .map(AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream) + .collect(Collectors.toCollection(LinkedHashSet::new)); + + // 把不需要自动注入的类从需要注入的类中移除 + processedConfigurations.removeAll(allExclusions); + + // 排序 + return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream() + .map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName)) + .collect(Collectors.toList()); + } + +``` + + + + + +后续由spring进行不再继续跟踪 \ No newline at end of file diff --git a/images/SpringBoot/image-20200319082131146.png b/images/SpringBoot/image-20200319082131146.png new file mode 100644 index 0000000..88c932c Binary files /dev/null and b/images/SpringBoot/image-20200319082131146.png differ diff --git a/images/SpringBoot/image-20200319082544653.png b/images/SpringBoot/image-20200319082544653.png new file mode 100644 index 0000000..9c4ca27 Binary files /dev/null and b/images/SpringBoot/image-20200319082544653.png differ diff --git a/images/SpringBoot/image-20200319083048849.png b/images/SpringBoot/image-20200319083048849.png new file mode 100644 index 0000000..5dba060 Binary files /dev/null and b/images/SpringBoot/image-20200319083048849.png differ diff --git a/images/SpringBoot/image-20200319083140225.png b/images/SpringBoot/image-20200319083140225.png new file mode 100644 index 0000000..ae87e41 Binary files /dev/null and b/images/SpringBoot/image-20200319083140225.png differ diff --git a/images/SpringBoot/image-20200319083345067.png b/images/SpringBoot/image-20200319083345067.png new file mode 100644 index 0000000..9af94eb Binary files /dev/null and b/images/SpringBoot/image-20200319083345067.png differ diff --git a/images/SpringBoot/image-20200319084141748.png b/images/SpringBoot/image-20200319084141748.png new file mode 100644 index 0000000..cee9722 Binary files /dev/null and b/images/SpringBoot/image-20200319084141748.png differ diff --git a/images/SpringBoot/image-20200319084151997.png b/images/SpringBoot/image-20200319084151997.png new file mode 100644 index 0000000..1e2354b Binary files /dev/null and b/images/SpringBoot/image-20200319084151997.png differ diff --git a/images/SpringBoot/image-20200319084357652.png b/images/SpringBoot/image-20200319084357652.png new file mode 100644 index 0000000..e4595c4 Binary files /dev/null and b/images/SpringBoot/image-20200319084357652.png differ diff --git a/images/SpringBoot/image-20200319084902957.png b/images/SpringBoot/image-20200319084902957.png new file mode 100644 index 0000000..decba93 Binary files /dev/null and b/images/SpringBoot/image-20200319084902957.png differ diff --git a/images/SpringBoot/image-20200319085446640.png b/images/SpringBoot/image-20200319085446640.png new file mode 100644 index 0000000..a9cd4b4 Binary files /dev/null and b/images/SpringBoot/image-20200319085446640.png differ diff --git a/images/SpringBoot/image-20200319090446231.png b/images/SpringBoot/image-20200319090446231.png new file mode 100644 index 0000000..d206752 Binary files /dev/null and b/images/SpringBoot/image-20200319090446231.png differ diff --git a/images/SpringBoot/image-20200320160423991.png b/images/SpringBoot/image-20200320160423991.png new file mode 100644 index 0000000..2979b07 Binary files /dev/null and b/images/SpringBoot/image-20200320160423991.png differ diff --git a/images/SpringBoot/image-20200320162835665.png b/images/SpringBoot/image-20200320162835665.png new file mode 100644 index 0000000..50fdbb0 Binary files /dev/null and b/images/SpringBoot/image-20200320162835665.png differ diff --git a/images/SpringBoot/image-20200320163001728.png b/images/SpringBoot/image-20200320163001728.png new file mode 100644 index 0000000..ba1cbe7 Binary files /dev/null and b/images/SpringBoot/image-20200320163001728.png differ diff --git a/images/SpringBoot/image-20200320163806852.png b/images/SpringBoot/image-20200320163806852.png new file mode 100644 index 0000000..3e8e35b Binary files /dev/null and b/images/SpringBoot/image-20200320163806852.png differ diff --git a/images/SpringBoot/image-20200320164145286.png b/images/SpringBoot/image-20200320164145286.png new file mode 100644 index 0000000..fc98a74 Binary files /dev/null and b/images/SpringBoot/image-20200320164145286.png differ diff --git a/images/SpringBoot/image-20200320171138431.png b/images/SpringBoot/image-20200320171138431.png new file mode 100644 index 0000000..94e2b40 Binary files /dev/null and b/images/SpringBoot/image-20200320171138431.png differ diff --git a/images/SpringBoot/image-20200320171734270.png b/images/SpringBoot/image-20200320171734270.png new file mode 100644 index 0000000..c213030 Binary files /dev/null and b/images/SpringBoot/image-20200320171734270.png differ diff --git a/images/SpringBoot/image-20200323080611527.png b/images/SpringBoot/image-20200323080611527.png new file mode 100644 index 0000000..3d59fff Binary files /dev/null and b/images/SpringBoot/image-20200323080611527.png differ diff --git a/images/SpringBoot/image-20200323081009823.png b/images/SpringBoot/image-20200323081009823.png new file mode 100644 index 0000000..2192c03 Binary files /dev/null and b/images/SpringBoot/image-20200323081009823.png differ diff --git a/images/SpringBoot/image-20200323081903145.png b/images/SpringBoot/image-20200323081903145.png new file mode 100644 index 0000000..55f0d0d Binary files /dev/null and b/images/SpringBoot/image-20200323081903145.png differ diff --git a/images/SpringBoot/image-20200323082553595.png b/images/SpringBoot/image-20200323082553595.png new file mode 100644 index 0000000..1ab6012 Binary files /dev/null and b/images/SpringBoot/image-20200323082553595.png differ diff --git a/images/SpringBoot/image-20200323083149737.png b/images/SpringBoot/image-20200323083149737.png new file mode 100644 index 0000000..3828ee7 Binary files /dev/null and b/images/SpringBoot/image-20200323083149737.png differ diff --git a/images/SpringBoot/image-20200323083247061.png b/images/SpringBoot/image-20200323083247061.png new file mode 100644 index 0000000..8534688 Binary files /dev/null and b/images/SpringBoot/image-20200323083247061.png differ diff --git a/images/SpringBoot/image-20200323083656670.png b/images/SpringBoot/image-20200323083656670.png new file mode 100644 index 0000000..48d19fc Binary files /dev/null and b/images/SpringBoot/image-20200323083656670.png differ diff --git a/images/SpringBoot/image-20200323084922159.png b/images/SpringBoot/image-20200323084922159.png new file mode 100644 index 0000000..e417154 Binary files /dev/null and b/images/SpringBoot/image-20200323084922159.png differ diff --git a/images/SpringBoot/image-20200323094446756.png b/images/SpringBoot/image-20200323094446756.png new file mode 100644 index 0000000..8090af1 Binary files /dev/null and b/images/SpringBoot/image-20200323094446756.png differ diff --git a/images/SpringBoot/image-20200323095626953.png b/images/SpringBoot/image-20200323095626953.png new file mode 100644 index 0000000..fb5a628 Binary files /dev/null and b/images/SpringBoot/image-20200323095626953.png differ diff --git a/images/SpringBoot/image-20200323104711545.png b/images/SpringBoot/image-20200323104711545.png new file mode 100644 index 0000000..fad1fb0 Binary files /dev/null and b/images/SpringBoot/image-20200323104711545.png differ diff --git a/images/SpringBoot/image-20200323104815305.png b/images/SpringBoot/image-20200323104815305.png new file mode 100644 index 0000000..dca576b Binary files /dev/null and b/images/SpringBoot/image-20200323104815305.png differ diff --git a/images/SpringBoot/image-20200323105053757.png b/images/SpringBoot/image-20200323105053757.png new file mode 100644 index 0000000..15d5a58 Binary files /dev/null and b/images/SpringBoot/image-20200323105053757.png differ diff --git a/images/SpringBoot/image-20200323105155998.png b/images/SpringBoot/image-20200323105155998.png new file mode 100644 index 0000000..a5bf0a9 Binary files /dev/null and b/images/SpringBoot/image-20200323105155998.png differ diff --git a/images/SpringBoot/image-20200323105830138.png b/images/SpringBoot/image-20200323105830138.png new file mode 100644 index 0000000..c15768a Binary files /dev/null and b/images/SpringBoot/image-20200323105830138.png differ diff --git a/images/SpringBoot/image-20200323110603959.png b/images/SpringBoot/image-20200323110603959.png new file mode 100644 index 0000000..43fe531 Binary files /dev/null and b/images/SpringBoot/image-20200323110603959.png differ diff --git a/images/SpringBoot/image-20200323112945449.png b/images/SpringBoot/image-20200323112945449.png new file mode 100644 index 0000000..9ce8cf5 Binary files /dev/null and b/images/SpringBoot/image-20200323112945449.png differ diff --git a/images/SpringBoot/image-20200323115401750.png b/images/SpringBoot/image-20200323115401750.png new file mode 100644 index 0000000..0acbe08 Binary files /dev/null and b/images/SpringBoot/image-20200323115401750.png differ diff --git a/images/SpringBoot/image-20200323115408877.png b/images/SpringBoot/image-20200323115408877.png new file mode 100644 index 0000000..0acbe08 Binary files /dev/null and b/images/SpringBoot/image-20200323115408877.png differ diff --git a/images/SpringBoot/image-20200323115701118.png b/images/SpringBoot/image-20200323115701118.png new file mode 100644 index 0000000..b93ae15 Binary files /dev/null and b/images/SpringBoot/image-20200323115701118.png differ diff --git a/images/SpringBoot/image-20200323115711826.png b/images/SpringBoot/image-20200323115711826.png new file mode 100644 index 0000000..28e6a5d Binary files /dev/null and b/images/SpringBoot/image-20200323115711826.png differ diff --git a/images/SpringBoot/image-20200323134135926.png b/images/SpringBoot/image-20200323134135926.png new file mode 100644 index 0000000..ffbb30c Binary files /dev/null and b/images/SpringBoot/image-20200323134135926.png differ diff --git a/images/SpringBoot/image-20200323134325955.png b/images/SpringBoot/image-20200323134325955.png new file mode 100644 index 0000000..b6f1576 Binary files /dev/null and b/images/SpringBoot/image-20200323134325955.png differ diff --git a/images/SpringBoot/image-20200323144523848.png b/images/SpringBoot/image-20200323144523848.png new file mode 100644 index 0000000..81b806f Binary files /dev/null and b/images/SpringBoot/image-20200323144523848.png differ diff --git a/images/SpringBoot/image-20200323151409473.png b/images/SpringBoot/image-20200323151409473.png new file mode 100644 index 0000000..4b11414 Binary files /dev/null and b/images/SpringBoot/image-20200323151409473.png differ diff --git a/images/SpringBoot/image-20200323154205484.png b/images/SpringBoot/image-20200323154205484.png new file mode 100644 index 0000000..1975ce9 Binary files /dev/null and b/images/SpringBoot/image-20200323154205484.png differ diff --git a/images/SpringBoot/image-20200323161442058.png b/images/SpringBoot/image-20200323161442058.png new file mode 100644 index 0000000..f58262d Binary files /dev/null and b/images/SpringBoot/image-20200323161442058.png differ diff --git a/images/SpringBoot/image-20200323161522570.png b/images/SpringBoot/image-20200323161522570.png new file mode 100644 index 0000000..6ecf3a5 Binary files /dev/null and b/images/SpringBoot/image-20200323161522570.png differ