# 概述
Spring Boot 是由 Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。
# 发展历程
- 2013年,Pivotal团队开始研发SpringBoot
- 2014年4月,发布springBoot的第一个版本
# 特点
- 开箱即用
指在开发过程中,通过MAVEN项目的pom文件添加相关依赖包,然后使用对应注解来代替繁琐的XML配置文件,以管理对象的生命周期; - 约定优于配置
一种由SpringBoot本身来配置目标结构,由开发者在结构中添加信息的软件设计范式。
# 核心流程
public class SpringApplication {
/*xxx: 静态run方法,跟内部的run方法有着本质区别*/
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
/*xxx: 进行spring应员工的实例化,并调用内部run方法*/
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
/*xxx: spring应用的实例化*/
/*xxx: springApplication实例化时,会自动做一些初始化操作*/
/*xxx: 实例化参数为 可变长参数 ,可以指定多个配置源*/
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
/*xxx: resourceLoader 用于加载默认的banner信息,默认采用的是 DefaultResourceLoader*/
/*xxx: primarySources 默认传入SpringBoot的入口类,如果作为springBoot项目引导类,需要被注解 @EnableAutoConfiguration标注*/
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
/*xxx: 资源加载器*/
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
/*xxx: 推断web应用类型*/
this.webApplicationType = WebApplicationType.deduceFromClasspath();
/*xxx: 通过spring工厂,加载 bootstrap注册表初始化器*/
this.bootstrapRegistryInitializers = getBootstrapRegistryInitializersFromSpringFactories();
/*xxx: 通过spring工厂,加载并初始化 ApplicationContextInitializer及相关实现类*/
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
/*xxx: 通过spring工厂,加载并初始化 ApplicationContextListener及相关实现类*/
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
/*xxx: 推断main方法 Class类*/
this.mainApplicationClass = deduceMainApplicationClass();
}
public ConfigurableApplicationContext run(String... args) {
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
Banner printedBanner = printBanner(environment);
/*xxx: 通过工厂创建上下文容器*/
context = createApplicationContext();
/*xxx: 准备容器,组件对象之间进行关联*/
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
/*xxx: 初始化容器,通常情况下,只是把最原始的启动bean给加载进来*/
refreshContext(context);
/*xxx: 初始化之后执行,默认实现为空*/
afterRefresh(context, applicationArguments);
/*xxx: 执行所有的runner接口,实现springBoot上下文启动完成后的后置动作*/
callRunners(context, applicationArguments);
}
}
可以简单的概括为这样的几步:
- spring应用的初始化 (通过spirng工厂加载BootStrapRegistryInitializer,应用上下文初始化器(ApplicationContextInitializer),应用监听器(ApplicationListener))
- spring应用的启动;
- 保证启动过程的监听机制生效(SpringApplicationRunListener)
- 启动的参数准备(所以说参数不是上下文本身准备的,而是在上下文创建之前就已经准备好);
- 创建上下文
- 准备上下文
- 启动上下文(刷新上下文)
- spring应用启动反馈(执行spring应用的运行器,实现上下文创建完成后的后置动作)
在springBoot环境中,大量的使用到了工厂加载器(SpringFactoriesLoader),这个类在spring源码中进行定义,但是用的比较少。
# 应用初始化
# primarySources
它是spring应用中,唯一不可缺少的参数,实际上表明了,上下文中的bean应该从何处开始得来.
# 推断应用类型,通过枚举类的静态方法(该用法还比较少见)
public enum WebApplicationType {
NONE,
SERVLET,
REACTIVE;
static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : SERVLET_INDICATOR_CLASSES) {
/*xxx: 基于classPath的web应用类型推断,核心实现方法为 ClassUtils.isPresent()*/
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
}
# 加载应用上下文初始化器,并缓存在本地
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
void initialize(C applicationContext);
}
# 加载应用监听器,并缓存在本地
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
void onApplicationEvent(E event);
}
# 应用启动
# 运行过程监听机制的生效(SpringApplicationRunListener)
- 抽象定义
public interface SpringApplicationRunListener {
/*xxx: 当run方法第一次被执行时,会被立即调用,可用于非常早期的初始化工作*/
default void starting(ConfigurableBootstrapContext bootstrapContext) {
}
/*xxx: 当environment准备完成,在 ApplicationContext创建之前,该方法被调用*/
default void environmentPrepared(ConfigurableBootstrapContext bootstrapContext,
ConfigurableEnvironment environment) {
}
/*xxx: 当ApplicationContext构建完成,资源还未被加载时,该方法被调用*/
default void contextPrepared(ConfigurableApplicationContext context) {
}
/*xxx: 当ApplicationContext加载完成,未被刷新之前,该方法被调用*/
default void contextLoaded(ConfigurableApplicationContext context) {
}
/*xxx: 当ApplicationContext刷新并启动之后,CommandLindeRunner 和 ApplicationRunner未被调用之前,该方法被调用*/
default void started(ConfigurableApplicationContext context) {
}
}
- 实现
/*xxx: 是 springBoot针对 SpringApplicationRunListener接口的唯一内建实现*/
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
/*xxx: 事件广播器 */
private final SimpleApplicationEventMulticaster initialMulticaster;
//xxx: 它的作用是,将spring应用的所有监听器进行收集,当接收到事件通知后,进行广播
//xxx: 实际上就是将所有的事件,派发给经由spring工厂注册的 应用监听器
}
# 上下文初始化前的参数准备
public class SpringApplication {
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
/*xxx: 获取或创建环境 */
ConfigurableEnvironment environment = getOrCreateEnvironment();
/*xxx: 通知环境已经准备完成*/
listeners.environmentPrepared(bootstrapContext, environment);
return environment;
}
}
- 加载正常的配置文件(通过环境后置处理器监听器进行委派-EnvironmentPostProcessorApplicationListener)
public class EnvironmentPostProcessorApplicationListener implements SmartApplicationListener, Ordered {
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationEnvironmentPreparedEvent) {
/*xxx: 环境准备完成,后置动作*/
onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
}
}
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
ConfigurableEnvironment environment = event.getEnvironment();
SpringApplication application = event.getSpringApplication();
/*xxx: 委派所有的 环境后置处理器进行处理器 */
for (EnvironmentPostProcessor postProcessor : getEnvironmentPostProcessors(event.getBootstrapContext())) {
postProcessor.postProcessEnvironment(environment, application);
}
}
}
/*xxx: 顶级接口,环境后置处理器*/
public interface EnvironmentPostProcessor {
/*xxx: 后置处理环境信息*/
void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application);
}
实现(ConfigDataEnvironmentPostProcessor)
/*xxx: 配置数据环境后置处理器,用于加载 启动参数 */
public class ConfigDataEnvironmentPostProcessor implements EnvironmentPostProcessor, Ordered {
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
/*xxx: 变量后置处理器*/
postProcessEnvironment(environment, application.getResourceLoader(), application.getAdditionalProfiles());
}
/*xxx: 配置参数的本质: configDataEnvironment.processAndApply */
void postProcessEnvironment(ConfigurableEnvironment environment, ResourceLoader resourceLoader,
Collection<String> additionalProfiles) {
getConfigDataEnvironment(environment, resourceLoader, additionalProfiles).processAndApply();
}
ConfigDataEnvironment getConfigDataEnvironment(ConfigurableEnvironment environment, ResourceLoader resourceLoader,
Collection<String> additionalProfiles) {
return new ConfigDataEnvironment(this.logFactory, this.bootstrapContext, environment, resourceLoader,
additionalProfiles, this.environmentUpdateListener);
}
}
核心实现
/*xxx: 配置数据环境,用于读取.yml,以及.properties配置文件*/
class ConfigDataEnvironment {
/*xxx: 上下文配置数据的 默认搜索路径*/
static final ConfigDataLocation[] DEFAULT_SEARCH_LOCATIONS;
static {
/*xxx: 首先从 classpath中加载,其次还会从相对路径的 config目录下加载*/
List<ConfigDataLocation> locations = new ArrayList<>();
/*xxx: 将路径封装为 特定的 location,optional标明该配置是否必须存在*/
locations.add(ConfigDataLocation.of("optional:classpath:/"));
locations.add(ConfigDataLocation.of("optional:classpath:/config/"));
locations.add(ConfigDataLocation.of("optional:file:./"));
locations.add(ConfigDataLocation.of("optional:file:./config/"));
locations.add(ConfigDataLocation.of("optional:file:./config/*/"));
DEFAULT_SEARCH_LOCATIONS = locations.toArray(new ConfigDataLocation[0]);
}
/*xxx: 处理所有的属性源,并将新引入的配置应用于 environment */
void processAndApply() {
/*xxx: 在该阶段,将会读取 默认的配置项,如 application.yml,application.properties等*/
ConfigDataEnvironmentContributors contributors = processInitial(this.contributors, importer);
/*xxx: 将contributor应用于环境中 */
applyToEnvironment(contributors, activationContext);
}
private ConfigDataEnvironmentContributors processInitial(ConfigDataEnvironmentContributors contributors,
ConfigDataImporter importer) {
contributors = contributors.withProcessedImports(importer, null);
return contributors;
}
private void applyToEnvironment(ConfigDataEnvironmentContributors contributors,
ConfigDataActivationContext activationContext) {
MutablePropertySources propertySources = this.environment.getPropertySources();
for (ConfigDataEnvironmentContributor contributor : contributors) {
PropertySource<?> propertySource = contributor.getPropertySource();
/*xxx: 省略若干抽象...*/
/*xxx: 如果是 active 的状态,则将其加入 environment中 */
propertySources.addLast(propertySource);
}
}
}
class ConfigDataEnvironmentContributors implements Iterable<ConfigDataEnvironmentContributor> {
ConfigDataEnvironmentContributors withProcessedImports(ConfigDataImporter importer,
ConfigDataActivationContext activationContext) {
while (true) {
ConfigDataEnvironmentContributor contributor = getNextToProcess(result, activationContext, importPhase);
/*xxx: 省略其他抽象...*/
List<ConfigDataLocation> imports = contributor.getImports();
/*xxx: 实际加载配置的动作由 此方法完成 */
Map<ConfigDataResolutionResult, ConfigData> imported = importer.resolveAndLoad(activationContext,
locationResolverContext, loaderContext, imports);
/*xxx: 省略其他抽象...*/
}
}
}
class ConfigDataImporter {
private final ConfigDataLocationResolvers resolvers;
/*xxx: 实际加载配置的动作由 此方法完成 */
Map<ConfigDataResolutionResult, ConfigData> resolveAndLoad(ConfigDataActivationContext activationContext,
ConfigDataLocationResolverContext locationResolverContext, ConfigDataLoaderContext loaderContext,
List<ConfigDataLocation> locations) {
/*xxx: 加载配置文件的时候,会根据profiles一起去加载 该配置文件 */
Profiles profiles = (activationContext != null) ? activationContext.getProfiles() : null;
List<ConfigDataResolutionResult> resolved = resolve(locationResolverContext, profiles, locations);
return load(loaderContext, resolved);
}
//xxx: resole方法进行多层次的多态,这里直接贴最核心的方法
private List<ConfigDataResolutionResult> resolve(ConfigDataLocationResolverContext locationResolverContext,
Profiles profiles, ConfigDataLocation location) {
/*xxx: 加载配置文件,最终还是委派给 解析器完成的 */
return this.resolvers.resolve(locationResolverContext, location, profiles);
}
}
/*xxx: 根据位置获取配置数据解析器 */
class ConfigDataLocationResolvers {
/*xxx: 实际上配置文件的加载分为了两步 */
private List<ConfigDataResolutionResult> resolve(ConfigDataLocationResolver<?> resolver,
ConfigDataLocationResolverContext context, ConfigDataLocation location, Profiles profiles) {
/*xxx: 第一次,首先将application.yml,properties等加载,之后再来处理 profiles的情况*/
List<ConfigDataResolutionResult> resolved = resolve(location, false, () -> resolver.resolve(context, location));
/*xxx: 如果没有指定profiles,则直接返回 */
if (profiles == null) {
return resolved;
}
List<ConfigDataResolutionResult> profileSpecific = resolve(location, true,
() -> resolver.resolveProfileSpecific(context, location, profiles));
return merge(resolved, profileSpecific);
}
//xxx: resolve方法,实际上是对解析器加载后的后置处理
private List<ConfigDataResolutionResult> resolve(ConfigDataLocation location, boolean profileSpecific,
Supplier<List<? extends ConfigDataResource>> resolveAction) {
List<ConfigDataResource> resources = nonNullList(resolveAction.get());
List<ConfigDataResolutionResult> resolved = new ArrayList<>(resources.size());
for (ConfigDataResource resource : resources) {
resolved.add(new ConfigDataResolutionResult(location, resource, profileSpecific));
}
return resolved;
}
}
/*xxx: 实际配置的解析加载,都是由该类完成的 */
public class StandardConfigDataLocationResolver
implements ConfigDataLocationResolver<StandardConfigDataResource>, Ordered {
@Override
public List<StandardConfigDataResource> resolve(ConfigDataLocationResolverContext context,
ConfigDataLocation location) throws ConfigDataNotFoundException {
/*xxx: 在解析前,首先将配置整合到一起,并构造为 引用配置信息*/
return resolve(getReferences(context, location));
}
private Set<StandardConfigDataReference> getReferences(ConfigDataLocationResolverContext context,
ConfigDataLocation configDataLocation) {
String resourceLocation = getResourceLocation(context, configDataLocation);
/*xxx: 省略其他抽象...*/
return getReferencesForDirectory(configDataLocation, resourceLocation, NO_PROFILE);
}
/*xxx: 从目录中加载配置,如果有profile,则会进行相应的处理 */
private Set<StandardConfigDataReference> getReferencesForDirectory(ConfigDataLocation configDataLocation,
String directory, String profile) {
/*xxx: 将spring.config.name 的名字,与路径相互结合,获取初始化配置*/
Set<StandardConfigDataReference> references = new LinkedHashSet<>();
for (String name : this.configNames) {
Deque<StandardConfigDataReference> referencesForName = getReferencesForConfigName(name, configDataLocation,
directory, profile);
references.addAll(referencesForName);
}
return references;
}
private Deque<StandardConfigDataReference> getReferencesForConfigName(String name,
ConfigDataLocation configDataLocation, String directory, String profile) {
Deque<StandardConfigDataReference> references = new ArrayDeque<>();
for (PropertySourceLoader propertySourceLoader : this.propertySourceLoaders) {
for (String extension : propertySourceLoader.getFileExtensions()) {
/*xxx: 默认情况下,加载顺序的双端队列情况为: yaml,yml,xml,properties */
/*xxx: 加载好后,后续有一步加载时,出队的顺序是从队尾开始出队*/
StandardConfigDataReference reference = new StandardConfigDataReference(configDataLocation, directory,
directory + name, profile, extension, propertySourceLoader);
if (!references.contains(reference)) {
references.addFirst(reference);
}
}
}
return references;
}
/*xxx: 加载引用资源里面的实际内容 */
private List<StandardConfigDataResource> resolve(StandardConfigDataReference reference) {
if (!this.resourceLoader.isPattern(reference.getResourceLocation())) {
return resolveNonPattern(reference);
}
return resolvePattern(reference);
}
/*xxx: 实际加载资源的动作。 */
private List<StandardConfigDataResource> resolveNonPattern(StandardConfigDataReference reference) {
Resource resource = this.resourceLoader.getResource(reference.getResourceLocation());
/*xxx: 如果资源不存在,则返回空*/
if (!resource.exists() && reference.isSkippable()) {
logSkippingResource(reference);
return Collections.emptyList();
}
/*xxx: 根据实际加载到的资源,封装 StandardConfigDataResource */
return Collections.singletonList(createConfigResourceLocation(reference, resource));
}
}
- 配置优先级
# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader
/*xxx: 实际配置的解析加载,都是由该类完成的 */
public class StandardConfigDataLocationResolver
implements ConfigDataLocationResolver<StandardConfigDataResource>, Ordered {
private Deque<StandardConfigDataReference> getReferencesForConfigName(String name,
ConfigDataLocation configDataLocation, String directory, String profile) {
Deque<StandardConfigDataReference> references = new ArrayDeque<>();
for (PropertySourceLoader propertySourceLoader : this.propertySourceLoaders) {
for (String extension : propertySourceLoader.getFileExtensions()) {
/*xxx: 默认情况下,加载顺序的双端队列情况为: yaml,yml,xml,properties */
/*xxx: 加载好后,后续有一步加载时,出队的顺序是从队尾开始出队*/
StandardConfigDataReference reference = new StandardConfigDataReference(configDataLocation, directory,
directory + name, profile, extension, propertySourceLoader);
if (!references.contains(reference)) {
references.addFirst(reference);
}
}
}
return references;
}
}
class ConfigDataImporter {
/*xxx: 从资源句柄中,加载内容*/
private Map<ConfigDataResolutionResult, ConfigData> load(ConfigDataLoaderContext loaderContext,
List<ConfigDataResolutionResult> candidates) throws IOException {
Map<ConfigDataResolutionResult, ConfigData> result = new LinkedHashMap<>();
/*xxx: 正常情况下,资源句柄的序列为: yaml,yml,xml,properties*/
/*xxx: 这里由队尾开始处理,使得优先级从队尾依次排开 */
for (int i = candidates.size() - 1; i >= 0; i--) {
if (this.loaded.add(resource)) {
/*xxx: 省略其他抽象...*/
}
}
}
}
- 加载远程配置(属于是springCloud部分的内容,此处可以先简单的接触下)
public class BootstrapApplicationListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered {
@Override
/*xxx: bootstrap父类应用监听器,会以bootstrap为名称,实例化一个轻量级的容器 */
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
ConfigurableApplicationContext context = bootstrapServiceContext(environment, event.getSpringApplication(), configName);
apply(context, event.getSpringApplication(), environment);
}
/*xxx: 配置并创建 bootstrap上下文容器 */
private ConfigurableApplicationContext bootstrapServiceContext(ConfigurableEnvironment environment,
final SpringApplication application, String configName) {
/*xxx: 实例化父级上下文 */
SpringApplicationBuilder builder = new SpringApplicationBuilder().profiles(environment.getActiveProfiles())
.bannerMode(Mode.OFF).environment(bootstrapEnvironment)
.registerShutdownHook(false).logStartupInfo(false).web(WebApplicationType.NONE);
builder.sources(BootstrapImportSelectorConfiguration.class);
/*xxx: 创建bootstrap上下文容器 */
final ConfigurableApplicationContext context = builder.run();
return context;
}
private void apply(ConfigurableApplicationContext context, SpringApplication application,
ConfigurableEnvironment environment) {
Set target = new LinkedHashSet<>(application.getInitializers());
target.addAll(getOrderedBeansOfType(context, ApplicationContextInitializer.class));
application.setInitializers(target);
/*xxx: 配置解密的初始化器,略*/
addBootstrapDecryptInitializer(application);
}
}
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(PropertySourceBootstrapProperties.class)
public class PropertySourceBootstrapConfiguration
implements ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
@Autowired(required = false)
/*xxx: 属性源,该属性源 由 springCloud提供*/
private List<PropertySourceLocator> propertySourceLocators = new ArrayList<>();
@Override
/*xxx: 核心初始化流程,其主要作用就是基于特定规则,将多个配置合并*/
public void initialize(ConfigurableApplicationContext applicationContext) {
List<PropertySource<?>> composite = new ArrayList<>();
/*xxx: 对所有属性源进行排序*/
AnnotationAwareOrderComparator.sort(this.propertySourceLocators);
/*xxx: 筛选有效属性源配置,并收集*/
for (PropertySourceLocator locator : this.propertySourceLocators) {
Collection<PropertySource<?>> source = locator.locateCollection(environment);
List<PropertySource<?>> sourceList = new ArrayList<>();
for (PropertySource<?> p : source) {
sourceList.add(new SimpleBootstrapPropertySource(p));
}
/*xxx: 省略了部分抽象...*/
composite.addAll(sourceList);
}
MutablePropertySources propertySources = environment.getPropertySources();
/*xxx: 配置属性源*/
insertPropertySources(propertySources, composite);
}
}
# springCloud本身的配置, nacos 与 spring-cloud-config
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration,\
com.alibaba.cloud.nacos.NacosConfigBootstrapConfiguration,\
org.springframework.cloud.config.client.ConfigServiceBootstrapConfiguration
#spirng-config基于ConfigServicePropertySourceLocator,nacos基于 NacosPropertySourceLocator
需要注意的是,BootstrapApplicationListener优于EnvironmentPostProcessorApplicationListener而率先实例化,所以application中的属性,是不能为bootstrap使用的,反之则可以
public class EnvironmentPostProcessorApplicationListener implements SmartApplicationListener, Ordered {
/**
* The default order for the processor.
*/
public static final int DEFAULT_ORDER = Ordered.HIGHEST_PRECEDENCE + 10;
}
public class BootstrapApplicationListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered {
/**
* The default order for this listener.
*/
public static final int DEFAULT_ORDER = Ordered.HIGHEST_PRECEDENCE + 5;
}
# 上下文的创建
public class SpringApplication {
protected ConfigurableApplicationContext createApplicationContext() {
return this.applicationContextFactory.create(this.webApplicationType);
}
}
/*xxx: 顶级接口,应用上下文工厂*/
public interface ApplicationContextFactory {
/*xxx: 应用上下文工厂的默认实现,包括servlet应用,reactive应用 以及 常规应用 */
ApplicationContextFactory DEFAULT = (webApplicationType) -> {
try {
switch (webApplicationType) {
case SERVLET:
/*xxx: 当环境为 servlet环境时,使用的是 annotationConfigServletWebServer上下文 */
return new AnnotationConfigServletWebServerApplicationContext();
case REACTIVE:
return new AnnotationConfigReactiveWebServerApplicationContext();
default:
/*xxx: 非web环境下,使用的是 annotationConfig上下文*/
return new AnnotationConfigApplicationContext();
}
}
catch (Exception ex) {
throw new IllegalStateException("Unable create a default ApplicationContext instance, "
+ "you may need a custom ApplicationContextFactory", ex);
}
};
/*xxx: 根据给定的应用类型,为springApplication 创建相应的上下文工厂*/
ConfigurableApplicationContext create(WebApplicationType webApplicationType);
}
可以知道的结论是:
- 上下文的创建,是根据spring应用的对于当前环境的感知判断而来;
- 不论是web环境,还是非web环境,这些上下文都具有扫描注解包注册bean的功能;
# 上下文的准备
public class SpringApplication {
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
/*xxx: 上下文装配阶段*/
/*xxx: 通知上下文装配完成*/
listeners.contextPrepared(context);
/*xxx: 上下文加载阶段*/
/*xxx: 通知上下文加载完成*/
listeners.contextLoaded(context);
}
}
# 上下文装配阶段
- 绑定环境配置信息
public class SpringApplication {
/*xxx: 准备上下文,由springBoot提供, spring中也提供了 prepareBeanFactory方法*/
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
/*xxx: 设置上下文的配置环境 */
context.setEnvironment(environment);
}
}
- 装配基础设施类
public class SpringApplication {
/*xxx: 准备上下文,由springBoot提供, spring中也提供了 prepareBeanFactory方法*/
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
/*xxx: 应用上下文后置处理*/
postProcessApplicationContext(context);
}
protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
if (this.beanNameGenerator != null) {
context.getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
this.beanNameGenerator);
}
if (this.resourceLoader != null) {
if (context instanceof GenericApplicationContext) {
((GenericApplicationContext) context).setResourceLoader(this.resourceLoader);
}
if (context instanceof DefaultResourceLoader) {
((DefaultResourceLoader) context).setClassLoader(this.resourceLoader.getClassLoader());
}
}
if (this.addConversionService) {
context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
}
}
}
- 上下文定制化配置
public class SpringApplication {
/*xxx: 准备上下文,由springBoot提供, spring中也提供了 prepareBeanFactory方法*/
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
/*xxx: 应用上下文刷新之前,ApplicationContextInitializer初始化context*/
applyInitializers(context);
}
/*xxx: 通过上下文初始化器,可以对上下文进行定制化修改*/
protected void applyInitializers(ConfigurableApplicationContext context) {
for (ApplicationContextInitializer initializer : getInitializers()) {
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
ApplicationContextInitializer.class);
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
initializer.initialize(context);
}
}
}
# 上下文加载阶段
public class SpringApplication {
/*xxx: 准备上下文,由springBoot提供, spring中也提供了 prepareBeanFactory方法*/
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
/*xxx: 获取全部配置源,其中包含 primarySources 和 sources */
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
/*xxx: 将sources中的bean加载到context中*/
load(context, sources.toArray(new Object[0]));
}
protected void load(ApplicationContext context, Object[] sources) {
BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
loader.load();
}
}
# 上下文的启动(上下文的刷新)
public class SpringApplication {
private void refreshContext(ConfigurableApplicationContext context) {
refresh(context);
}
protected void refresh(ConfigurableApplicationContext applicationContext) {
applicationContext.refresh();
}
}
- 可以看出,上下文的启动阶段,由spring本身完成;
# 应用启动反馈
public class SpringApplication {
public ConfigurableApplicationContext run(String... args) {
/*xxx: 通知监听器,容器启动完成,出发回调 */
listeners.started(context);
/*xxx: 执行所有的runner接口,实现springBoot上下文启动完成后的后置动作*/
callRunners(context, applicationArguments);
}
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList<>();
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
AnnotationAwareOrderComparator.sort(runners);
/*xxx: 提供了两个层面的runner,都会被调用*/
for (Object runner : new LinkedHashSet<>(runners)) {
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
}
}
# 自动装配
# 流程
/*xxx: 被该注解注释后,当前的包路径会作为扫描的根路径*/
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
/*xxx: 根据类排除指定的自动配置*/
Class<?>[] exclude() default {};
/*xxx: 根据类名,排除指定的自动配置*/
String[] excludeName() default {};
}
/*xxx: 具有 资源加载器,bean工厂,环境,类加载器的感知能力*/
/*xxx: order可以自定义加载的顺序*/
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
/*xxx: 根据条件以及配置,spring.factories 获取需要配置的bean */
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
/*xxx: 通过SpringFactoriesLoader类提供的方法,加载类路径中 META-INF 目录下的 spring.factories文件中,针对EnableAutoConfiguration的注册配置类*/
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
/*xxx: 对获得的注册配置类集合进行去重处理,防止 多个模块引入相同的配置类*/
configurations = removeDuplicates(configurations);
/*xxx: 获取注解中被 exclude 或 excludeName 所排除的类的集合*/
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
/*xxx: 检查排除的类是否可实例化,是否被自动注册配置所使用,不符合条件则抛异常*/
checkExcludedClasses(configurations, exclusions);
/*xxx: 从自动配置类集合中去除被排除的类*/
configurations.removeAll(exclusions);
/*xxx: 检查配置类的注解,是否符合 spring.factories文件中 AutoConfigurationImportFilter指定的注解检查条件*/
configurations = getConfigurationClassFilter().filter(configurations);
/*xxx: 将筛选完成的配置类和排查了的配置类构建为事件类,并传入监听器 监听器的配置位于 spring.factories文件的 AutoConfigurationImportListener项*/
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
private static class AutoConfigurationGroup
implements DeferredImportSelector.Group, BeanClassLoaderAware, BeanFactoryAware, ResourceLoaderAware {
@Override
/*xxx: 填充entries*/
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
/*xxx: 通过spring工厂,将自动配置项配置,进行加载,以及根据配置进行过滤 */
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
.getAutoConfigurationEntry(annotationMetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
}
@Override
/*xxx: 获取entries*/
public Iterable<Entry> selectImports() {
/*xxx: 去重*/
Set<String> processedConfigurations = this.autoConfigurationEntries.stream()
.map(AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream)
.collect(Collectors.toCollection(LinkedHashSet::new));
processedConfigurations.removeAll(allExclusions);
/*xxx: 将自动配置项进行 排序 */
return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()
.map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName))
.collect(Collectors.toList());
}
/*xxx: 对autoConfiguration进行排序,如果有些配置项有先后顺序,则采用该方式能够满足需求*/
private List<String> sortAutoConfigurations(Set<String> configurations,
AutoConfigurationMetadata autoConfigurationMetadata) {
return new AutoConfigurationSorter(getMetadataReaderFactory(), autoConfigurationMetadata)
.getInPriorityOrder(configurations);
}
}
}
# 自动装配的优先级排序
class AutoConfigurationSorter {
AutoConfigurationSorter(MetadataReaderFactory metadataReaderFactory,
AutoConfigurationMetadata autoConfigurationMetadata) {
Assert.notNull(metadataReaderFactory, "MetadataReaderFactory must not be null");
this.metadataReaderFactory = metadataReaderFactory;
this.autoConfigurationMetadata = autoConfigurationMetadata;
}
/*xxx: 进行优先顺序排序 */
List<String> getInPriorityOrder(Collection<String> classNames) {
AutoConfigurationClasses classes = new AutoConfigurationClasses(this.metadataReaderFactory,
this.autoConfigurationMetadata, classNames);
List<String> orderedClassNames = new ArrayList<>(classNames);
// Initially sort alphabetically
/*xxx: 首先进行 自然序排序 */
Collections.sort(orderedClassNames);
// Then sort by order
/*xxx: 其次,通过 order 进行优先级排序 */
orderedClassNames.sort((o1, o2) -> {
int i1 = classes.get(o1).getOrder();
int i2 = classes.get(o2).getOrder();
return Integer.compare(i1, i2);
});
// Then respect @AutoConfigureBefore @AutoConfigureAfter
/*xxx: 最后 根据 @AutoConfigureBefore 以及 @AutoConfigureAfter 的注解 进行排序 */
orderedClassNames = sortByAnnotation(classes, orderedClassNames);
return orderedClassNames;
}
}
- 根据 @AutoConfigureBefore 以及 @AutoConfigureAfter 排序的实现 (本质上,是图算法的 深度图遍历)
class AutoConfigurationSorter {
private List<String> sortByAnnotation(AutoConfigurationClasses classes, List<String> classNames) {
List<String> toSort = new ArrayList<>(classNames);
/*xxx: toSort里面,存储了两遍数据 */
toSort.addAll(classes.getAllNames());
/*xxx: 存储已经被排好序的数据*/
Set<String> sorted = new LinkedHashSet<>();
/*xxx: 存储某次处理过程中,正在处理的数据*/
Set<String> processing = new LinkedHashSet<>();
while (!toSort.isEmpty()) {
/*xxx: 查找某个配置元素的 直接 后继元素 */
doSortByAfterAnnotation(classes, toSort, sorted, processing, null);
}
sorted.retainAll(classNames);
return new ArrayList<>(sorted);
}
private void doSortByAfterAnnotation(AutoConfigurationClasses classes, List<String> toSort, Set<String> sorted,
Set<String> processing, String current) {
if (current == null) {
/*xxx: 进行遍历 */
current = toSort.remove(0);
}
/*xxx: 正在处理元素入队*/
processing.add(current);
/*xxx: 根据某一个配置元素 的直接前驱 以及 直接后继元素,进行深度树检索。 检索过程中,会进行循环依赖的判断*/
for (String after : classes.getClassesRequestedAfter(current)) {
checkForCycles(processing, current, after);
if (!sorted.contains(after) && toSort.contains(after)) {
doSortByAfterAnnotation(classes, toSort, sorted, processing, after);
}
}
processing.remove(current);
sorted.add(current);
}
/*xxx: 查找某一个元素的直接前驱以及后继元素 */
Set<String> getClassesRequestedAfter(String className) {
/*xxx: 查找这个类的 前驱配置 */
Set<String> classesRequestedAfter = new LinkedHashSet<>(get(className).getAfter());
this.classes.forEach((name, autoConfigurationClass) -> {
/*xxx: 查找这个类的 直接后继 配置*/
if (autoConfigurationClass.getBefore().contains(className)) {
classesRequestedAfter.add(name);
}
});
return classesRequestedAfter;
}
}
# 参数配置
# 集中配置的使用
- 使用
@Configuration
使某一个类成为配置类 - 使用
@ConfigurationProperties
使某前缀配置生效(@EnableConfigurationProperties 可以与扫描注册configurationProperties,也可指定注册)
# 配置元数据的生成
- 引用模块:
spring-boot-configuration-processor
- 配置元数据文件:
META-INF/spring-configuration-metadata.json
,也可通过IDE使用 Annotation Processors自动生成配置元数据
# 元数据的作用
在使用配置项进行配置时,IDE会自动进行提示;
# 参数绑定
# @Confiration参数绑定
- 松散绑定
- 参数绑定时,把中划线-,下划线_都去掉,且不区分大小写,称为松散绑定(宽松绑定)
- @ConfigurationProperties默认提供宽松绑定
- 虽然松散绑定很随意,但是推荐使用烤肉串模式如super-admin
- @Value不支持松散绑定
@ConfigurationProperties
的属性prefix
前缀命名规范仅支持纯小写字母、数字、下划线、中划线
# 绑定规则
- Map绑定
- key只能包含大小写字母、数字和短横线、下划线
- 非以上字符将会被去掉
- 如果key需要出现其它字符,需要包含在中括号里
demo:
map:
key3: va1
/key4: dafd
FDS: dsfds
DK-FJD_Ss/df: sdfdsf
"[/sf#jd]": sdfd
"[dskf_1_]": fdsf
#显示结果如下(为了防止语法冲突,根节点后加了个1)
demo1:
map:
key3: va1
key4: dafd
FDS: dsfds
DK-FJD_Ssdf: sdfdsf
"[/sf#jd]": sdfd
"[dskf_1_]": fdsf
- 数组绑定
user:
girlFriends:
- 1
- 2
- 3
- 4
- 参数优先级
- 详见springCloud相关笔记;
# @Value参数绑定
略
# 命令行参数绑定
- 通过使用
--
开头的参数,会自动被识别为springBoot参数,如spirng.config.location=./application.yml - 命令行参数的优先级最高(因为其排在队列最前面)
# @PropertySource参数引入
- 只支持配置
*.properties
配置文件到容器中 - 该参数用于指定配置来源,主要是为辅助参数绑定
# Binder参数绑定工具
# 使用示例
class Test{
static{
Map result= Binder.get(environment).bind("demo.map", Bindable.mapOf(String.class,Object.class)).get();
}
}
# 使用指南
绑定到对象实例:
Bindable.of(T instance)
绑定到对象:
Bindable.of(Class<T> clazz)
绑定到数组:
Bindable.Listof(Class<T> clazz)
绑定到集合:
Bindable.Setof(Class<T> clazz)
绑定到map字典:
Bindable.Mapof(Class<T> clazz)
# 条件装配
# 条件装配的注释事项
- 条件装配,需要定义在**@Configuration配置类**上
# 条件解析流程
/*xxx: 配置类解析器 */
class ConfigurationClassParser {
/*xxx: 条件评估器*/
private final ConditionEvaluator conditionEvaluator;
public ConfigurationClassParser(MetadataReaderFactory metadataReaderFactory,
ProblemReporter problemReporter, Environment environment, ResourceLoader resourceLoader,
BeanNameGenerator componentScanBeanNameGenerator, BeanDefinitionRegistry registry) {
//xxx: 省略其它抽象...
this.conditionEvaluator = new ConditionEvaluator(registry, environment, resourceLoader);
}
/*xxx: 实际解析配置类的方法*/
protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
/*xxx: 首先,如果当前的 配置类,不符合配置要求,则跳过*/
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
//xxx: 省略其它抽象...
}
}
class ConditionEvaluator {
public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) {
/*xxx: 没有Conditional注解,则肯定不会跳过解析 */
if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) {
return false;
}
if (phase == null) {
if (metadata instanceof AnnotationMetadata &&
ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) {
//xxx: 解析 配置类的条件判定
return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION);
}
//xxx: 注册bean的条件判定
return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN);
}
List<Condition> conditions = new ArrayList<>();
/*xxx: 实例化条件对象实例*/
for (String[] conditionClasses : getConditionClasses(metadata)) {
for (String conditionClass : conditionClasses) {
Condition condition = getCondition(conditionClass, this.context.getClassLoader());
conditions.add(condition);
}
}
/*xxx: 对条件进行排序*/
AnnotationAwareOrderComparator.sort(conditions);
/*xxx: 判定条件 */
for (Condition condition : conditions) {
ConfigurationPhase requiredPhase = null;
/*xxx: 对于配置条件,判定其phase是否一致 */
if (condition instanceof ConfigurationCondition) {
requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();
}
/*xxx: 对于通用条件,判定其matches 方法是否匹配, 只要有一个不匹配,则会跳过配置 */
if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) {
return true;
}
}
return false;
}
//xxx: 省略其它抽象...
}
# SpringBoot的条件体系
- 配置定义
@Conditional(OnBeanCondition.class)
/*xxx: 在容器中有指定bean的条件下满足*/
public @interface ConditionalOnBean {
//xxx: 内部配置可以自定义,并无强制约束
}
@Conditional(OnBeanCondition.class)
/*xxx: 当容器中没有指定bean时,满足条件*/
public @interface ConditionalOnMissingBean {}
@Conditional(OnClassCondition.class)
/*xxx: 在classpath类路径下有指定类满足条件*/
public @interface ConditionalOnClass {}
@Conditional(OnClassCondition.class)
/*xxx: 当类路径中没有指定类时满足条件*/
public @interface ConditionalOnMissingClass {}
- 条件定义
/*xxx: springBoot条件抽象*/
public abstract class SpringBootCondition implements Condition {
/*xxx: 重写 matches方法 */
public final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
/*xxx: 获取匹配结果*/
ConditionOutcome outcome = getMatchOutcome(context, metadata);
return outcome.isMatch();
}
/*xxx: 获取匹配结果,本质上就是一个 true or false,只是同时存储了 匹配信息*/
public abstract ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata);
}
@Order(Ordered.LOWEST_PRECEDENCE)
/*xxx: 判定bean是否存在的条件 */
class OnBeanCondition extends FilteringSpringBootCondition implements ConfigurationCondition {
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
ConditionMessage matchMessage = ConditionMessage.empty();
//xxx: 可以处理多个条件注解
if (annotations.isPresent(ConditionalOnBean.class)) {
//xxx: 省略抽象..., 不匹配时直接返回
}
if (metadata.isAnnotated(ConditionalOnSingleCandidate.class.getName())) {
//xxx: 省略抽象... 不匹配时直接返回
}
if (metadata.isAnnotated(ConditionalOnMissingBean.class.getName())) {
//xxx: 省略抽象... 不匹配时直接返回
}
return ConditionOutcome.match(matchMessage);
}
}
@Order(Ordered.HIGHEST_PRECEDENCE)
/*xxx: 过滤逻辑是,使用类加载器去加载指定的类*/
/*xxx: 根据指定的类加载的条件 */
class OnClassCondition extends FilteringSpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
/*xxx: 获取类加载器 */
ClassLoader classLoader = context.getClassLoader();
ConditionMessage matchMessage = ConditionMessage.empty();
List<String> onClasses = getCandidates(metadata, ConditionalOnClass.class);
if (onClasses != null) {
//xxx: 省略抽象..., 不匹配时直接返回
}
List<String> onMissingClasses = getCandidates(metadata, ConditionalOnMissingClass.class);
if (onMissingClasses != null) {
//xxx: 省略抽象..., 不匹配时直接返回
}
return ConditionOutcome.match(matchMessage);
}
}
# cache自动装备
# cache自动装配的条件
@ConditionalOnBean(CacheAspectSupport.class)
:上下文中开启了cache功能,即使用了EnableCaching@ConditionalOnMissingBean(value = CacheManager.class, name = "cacheResolver")
:不存在自定义的CacheManager,以及自定义的cacheResolver
# 源码解析
@ConditionalOnClass(CacheManager.class)
@ConditionalOnBean(CacheAspectSupport.class)
@ConditionalOnMissingBean(value = CacheManager.class, name = "cacheResolver")
/*xxx: 缓存自动装配的要求: 1.使用了 @EnableCaching注解 2.未自定义CacheManager以及cacheResolver*/
@Import({ CacheConfigurationImportSelector.class })
public class CacheAutoConfiguration {
static class CacheConfigurationImportSelector implements ImportSelector {
@Override
/*xxx: 此处通过 ImportSelector,同时引入多个配置类*/
/*xxx: 类型包括: generic,ehcache,hazelcast,infinispan,cache,couchbase,redis,caffeine,simple,none
xxx: 如果同时满足多个条件,位置在前的配置,具有更高的优先级
* */
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
CacheType[] types = CacheType.values();
String[] imports = new String[types.length];
for (int i = 0; i < types.length; i++) {
imports[i] = CacheConfigurations.getConfigurationClass(types[i]);
}
return imports;
}
}
}
@ConditionalOnBean(Cache.class)
@ConditionalOnMissingBean(CacheManager.class)
@Conditional(CacheCondition.class)
/*xxx: 收集cache进行管理*/
class GenericCacheConfiguration {
@Bean
SimpleCacheManager cacheManager(CacheManagerCustomizers customizers, Collection<Cache> caches) {
SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager.setCaches(caches);
return customizers.customize(cacheManager);
}
}
@ConditionalOnMissingBean(CacheManager.class)
@Conditional(CacheCondition.class)
/*xxx: 默认的Cache管理器实现*/
class SimpleCacheConfiguration {
@Bean
ConcurrentMapCacheManager cacheManager(CacheProperties cacheProperties,
CacheManagerCustomizers cacheManagerCustomizers) {
ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager();
List<String> cacheNames = cacheProperties.getCacheNames();
/*xxx: 如果设置了缓存名称,则是静态的,不可新增其它的缓存*/
if (!cacheNames.isEmpty()) {
cacheManager.setCacheNames(cacheNames);
}
return cacheManagerCustomizers.customize(cacheManager);
}
}
class CacheCondition extends SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
//xxx: 可以不配置,但不能乱配置
BindResult<CacheType> specified = Binder.get(environment).bind("spring.cache.type", CacheType.class);
if (!specified.isBound()) {
/*xxx: 使用默认的 缓存类型返回 */
return ConditionOutcome.match(message.because("automatic cache type"));
}
}
}
# SpringWebMVC自动装配
# 自动装配的条件
- 项目为web项目,且存在
spring-webmvc
模块 - 用户未自定义
WebMvcConfigurationSupport
配置类
# 自动装配抽象层级
- web容器层面的自动装配;
- SpringWeb基础设施层面的自动装配
- SpringWebMvc应用层面的自动装配
# web容器自动装配
- 略,依赖于web容器的选型,如果一个容器都没有,则不会装配;
# 基础设施层面自动装配
- 生效条件是,没有自定义
DispatcherServlet
- 可通过配置项,配置
DispatcherServlet
的匹配路径,配置项为spring.mvc.servlet.path
- Dispatcher本身具有一定的应用配置功能,详细可看其
initStrategies
方法
public class DispatcherServletAutoConfiguration {
/*xxx: 主要用来将 DispatcherServlet注册到系统中。 本质上是通过 ServletContextInitializer实现的,该接口由 spring-web提供 */
protected static class DispatcherServletRegistrationConfiguration {
@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,
WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
/*xxx: 通过 ServletRegistrationBean将 dispatcherServlet 注册为 servlet*/
//xxx: 默认匹配的路径为 / 根路径
DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
webMvcProperties.getServlet().getPath());
/*xxx: 设置名称为 dispatcherServlet*/
registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
/*xxx: 设置加载优先级,默认为-1*/
registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
multipartConfig.ifAvailable(registration::setMultipartConfig);
return registration;
}
}
}
# 应用层面的自动装配(重点)
- 自定义配置器,且其顺序号为0;
/*xxx: webMvc的自动配置项 */
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
//xxx: 官方的优先级低于用户自定义的优先级
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
public class WebMvcAutoConfiguration {
//xxx: 可通过配置,配置 hiddenMetod过滤器,formContent过滤器
@Order(0)
/*xxx: webMvc的自动配置, 适配器,相当于插件化配置 */
//xxx: 主要用于装配相应的属性
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
}
//xxx: webMvc的实际配置项 该类主要配置了 基于 RequestMapping的mvc套件
//xxx: 其父类会配置 springMvc的其它机制的套件
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
}
}
# 视图解析器装配
@Order(0)
/*xxx: webMvc的自动配置, 适配器,相当于插件化配置 */
//xxx: 主要用于装配相应的属性
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
@Bean
@ConditionalOnMissingBean
//xxx: 装配视图解析器
public InternalResourceViewResolver defaultViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix(this.mvcProperties.getView().getPrefix());
resolver.setSuffix(this.mvcProperties.getView().getSuffix());
return resolver;
}
@Bean
//xxx: 如果定义了 view的bean
@ConditionalOnBean(View.class)
@ConditionalOnMissingBean
/*xxx: 通过逻辑视图名称匹配定义好的视图bean对象*/
public BeanNameViewResolver beanNameViewResolver() {
BeanNameViewResolver resolver = new BeanNameViewResolver();
resolver.setOrder(Ordered.LOWEST_PRECEDENCE - 10);
return resolver;
}
@Bean
@ConditionalOnBean(ViewResolver.class)
@ConditionalOnMissingBean(name = "viewResolver", value = ContentNegotiatingViewResolver.class)
/*xxx: 它并不直接处理视图,而是委派给其它其它解析器完成*/
public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {
ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
resolver.setContentNegotiationManager(beanFactory.getBean(ContentNegotiationManager.class));
resolver.setOrder(Ordered.HIGHEST_PRECEDENCE);
return resolver;
}
@Bean
@ConditionalOnMissingBean({ RequestContextListener.class, RequestContextFilter.class })
//xxx: 装载 请求上下文过滤器
@ConditionalOnMissingFilterBean(RequestContextFilter.class)
public static RequestContextFilter requestContextFilter() {
return new OrderedRequestContextFilter();
}
}
# MVC套件装配
- 该配置类,是
WebMvcConfigurationSupport
的子类
//xxx: webMvc的实际配置项 该类主要配置了 基于 RequestMapping的mvc套件
//xxx: 其父类会配置 springMvc的其它机制的套件
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
}
HandlerMapping
套件,它的一些顺序与原生springMVC
配置几乎一致,默认的HandlerMapping
顺序如下RequestMappingHandlerMapping
,其顺序为0viewControllerHandlerMapping
,其顺序为1WelcomePageHandlerMapping
,其顺序为2(优先级更高)BeanNameUrlHandlerMapping
,其顺序为2resourceHandlerMapping
,其顺序为Order.LOWEST_PRECEDENCE - 1
defaultServletHandlerMapping
,其顺序为Order.LOWEST_PRECEDENCE
- 这些配置是一起的,要么共同遵循,要么全部自定义
//xxx: webMvc的实际配置项 该类主要配置了 基于 RequestMapping的mvc套件
//xxx: 其父类会配置 springMvc的其它机制的套件
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
@Bean
@Primary
@Override
// xxx: requestMapping性质的 handlerMapping
// xxx: 其处理顺序为 0
public RequestMappingHandlerMapping requestMappingHandlerMapping(
@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
return super.requestMappingHandlerMapping(contentNegotiationManager, conversionService,
resourceUrlProvider);
}
@Bean
@Nullable
public HandlerMapping viewControllerHandlerMapping(
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
//xxx:省略其它抽象...
ViewControllerRegistry registry = new ViewControllerRegistry(this.applicationContext);
AbstractHandlerMapping handlerMapping = registry.buildHandlerMapping();
return handlerMapping;
}
@Bean
public BeanNameUrlHandlerMapping beanNameHandlerMapping(
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
BeanNameUrlHandlerMapping mapping = new BeanNameUrlHandlerMapping();
mapping.setOrder(2);
return mapping;
}
@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext,
FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
//xxx:略, 顺序为2
WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(
new TemplateAvailabilityProviders(applicationContext), applicationContext, getWelcomePage(),
this.mvcProperties.getStaticPathPattern());
return welcomePageHandlerMapping;
}
@Bean
@Nullable
/*xxx: 处理资源的 HandlerMapping,本质上为 SimpleUrlHandlerMapping */
public HandlerMapping resourceHandlerMapping(
@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
//xxx: 其顺序位于 : Ordered.LOWEST_PRECEDENCE - 1
ResourceHandlerRegistry registry = new ResourceHandlerRegistry(this.applicationContext,
this.servletContext, contentNegotiationManager, pathConfig.getUrlPathHelper());
addResourceHandlers(registry);
AbstractHandlerMapping handlerMapping = registry.getHandlerMapping();
return handlerMapping;
}
@Bean
@Nullable
public HandlerMapping defaultServletHandlerMapping() {
DefaultServletHandlerConfigurer configurer = new DefaultServletHandlerConfigurer(this.servletContext);
configureDefaultServletHandling(configurer);
//xxx: 其顺序位于 Ordered.LOWEST_PRECEDENCE
return configurer.buildHandlerMapping();
}
}
HandlerAdapter
套件装配
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
@Bean
@Override
public RequestMappingHandlerAdapter requestMappingHandlerAdapter(
@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
@Qualifier("mvcValidator") Validator validator) {
RequestMappingHandlerAdapter adapter = super.requestMappingHandlerAdapter(contentNegotiationManager,
conversionService, validator);
adapter.setIgnoreDefaultModelOnRedirect(
this.mvcProperties == null || this.mvcProperties.isIgnoreDefaultModelOnRedirect());
return adapter;
}
@Bean
/*xxx: 处理资源的 适配器套件 */
public HttpRequestHandlerAdapter httpRequestHandlerAdapter() {
return new HttpRequestHandlerAdapter();
}
@Bean
public SimpleControllerHandlerAdapter simpleControllerHandlerAdapter() {
return new SimpleControllerHandlerAdapter();
}
}
- 其它套件装载,略
# 自定义化的启示
- 需要自定义时,可以实现多个WebMvcConfigurer,但不要自定义WebMvcConfigurationSupport
# SpringSecurity自动装配
# 配置生效条件
SpringSecurityCore
模块存在;- 未自定义
WebSecurityConfigurerAdapter
;
# 源码
//xxx: springSecurityCore模块存在时,自动配置
@ConditionalOnClass(DefaultAuthenticationEventPublisher.class)
@EnableConfigurationProperties(SecurityProperties.class)
@Import({ SpringBootWebSecurityConfiguration.class, WebSecurityEnablerConfiguration.class,
SecurityDataConfiguration.class })
public class SecurityAutoConfiguration {
@Bean
@ConditionalOnMissingBean(AuthenticationEventPublisher.class)
public DefaultAuthenticationEventPublisher authenticationEventPublisher(ApplicationEventPublisher publisher) {
return new DefaultAuthenticationEventPublisher(publisher);
}
}
/*xxx: 生效条件,未自定义 springSecurity的过滤器链, 即未自定义 WebSecurityConfigurerAdapter */
@ConditionalOnDefaultWebSecurity
@ConditionalOnWebApplication(type = Type.SERVLET)
class SpringBootWebSecurityConfiguration {
@Bean
@Order(SecurityProperties.BASIC_AUTH_ORDER)
/*xxx: 默认过滤链,顺序点位为 Ordered.LOWEST_PRECEDENCE - 5 */
/*xxx: 默认的过滤链, 所有的 接口都需要认证才能够访问, 认证的方式以 form形式 */
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated().and().formLogin().and().httpBasic();
return http.build();
}
}
@ConditionalOnMissingBean(name = BeanIds.SPRING_SECURITY_FILTER_CHAIN)
@ConditionalOnClass(EnableWebSecurity.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@EnableWebSecurity
/*xxx: 如果没用 springSecurityFilterChain的bean,则使用 @EnableWebSecurity*/
/*xxx: springSecurity本身具有默认的过滤链, 它的默认顺序为 100, 且配置了多个特性,
包括: csrf,异常处理,session管理,securityContext */
/*xxx: 但是它未对权限作拦截 */
class WebSecurityEnablerConfiguration {
}
# 基础设施装配
@ConditionalOnClass({
AbstractSecurityWebApplicationInitializer.class,
SessionCreationPolicy.class })
@AutoConfigureAfter(SecurityAutoConfiguration.class)
//xxx:基础设施配置
/*xxx: 生效条件为 同时存在 spring-security-web模块以及 spring-security-config模块*/
public class SecurityFilterAutoConfiguration {
@Bean
@ConditionalOnBean(name = DEFAULT_FILTER_NAME)
public DelegatingFilterProxyRegistrationBean securityFilterChainRegistration(
SecurityProperties securityProperties) {
/*xxx: 动态代理过滤链, 本质上,也可以直接通过 FilterRegistrationBean 完成filter的注册*/
/*xxx: springboot环境下, springSecurity默认过滤链的顺序为 REQUEST_WRAPPER_FILTER_MAX_ORDER -100, 也就是 -100*/
DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean(
DEFAULT_FILTER_NAME);
registration.setOrder(securityProperties.getFilter().getOrder());
registration.setDispatcherTypes(getDispatcherTypes(securityProperties));
return registration;
}
}
# 注意事项
- 使用
WebSecurityConfiguerAdapter
时,该虚拟内部过滤链的默认顺序为100,存在多个过滤链且未自定义顺序时,会报错 SpringBoot
环境下,默认的过滤链, 所有的 接口都需要认证才能够访问, 认证的方式以 form形式;SpringBoot
环境下,springSecurity
自动生效;springSecurity
在非springBoot
环境下,仅仅使用@EnableWebSecurity
,并不能拦截接口,因为默认的配置项中,未对接口进行拦截,但配置了csrf
等安全功能;springSecurity
默认的基础设施过滤链为**-100**- 虚拟过滤链与实际的过滤链,构成一条完整的链,而不是虚拟过滤链出栈后再执行实际过滤链!!!
- 所有定义的bean,如果未指定order装配顺序,则默认按照最低优先级,即Integer.MAX_VALUE处理.其它的bean,也适用该规则。 由
OrderComparator
的getOrder方法
得到最终的值;
# springSession自动装配
# 配置生效条件
- 具备
spring-session-core
依赖 - 定义了SessionRepositoryFilter时生效
# 源码解析
@ConditionalOnClass(Session.class)
/*xxx: 当前环境下,需要具备 spring-session-core依赖 */
public class SessionAutoConfiguration {
/*xxx: 基础设施配置 */
@Import({ ServletSessionRepositoryValidator.class, SessionRepositoryFilterConfiguration.class })
static class ServletSessionConfiguration {
@Bean
@Conditional(DefaultCookieSerializerCondition.class)
/*xxx: 未自定义 HttpSessionIdResolver,CookieSerializer时生效 */
DefaultCookieSerializer cookieSerializer(ServerProperties serverProperties,
ObjectProvider<DefaultCookieSerializerCustomizer> cookieSerializerCustomizers) {
//xxx: 省略其它抽象...
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RememberMeServices.class)
/*xxx: 存在springSecurity时,配置记住我 令牌的序列化器 */
static class RememberMeServicesConfiguration {
@Bean
DefaultCookieSerializerCustomizer rememberMeServicesCookieSerializerCustomizer() {
return (cookieSerializer) -> cookieSerializer
.setRememberMeRequestAttribute(SpringSessionRememberMeServices.REMEMBER_ME_LOGIN_ATTR);
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(SessionRepository.class)
@Import({ ServletSessionRepositoryImplementationValidator.class,
ServletSessionConfigurationImportSelector.class })
/*xxx: 未自定义 sessionRepository时生效, 有多个条件存在时,自动生效,优先级为 redis>mongodb>hazelcast>jdbc */
/*xxx: 本质上就是用配置值 覆盖相应的 sessionRepository 属性 */
static class ServletSessionRepositoryConfiguration {
}
}
}
/*xxx: spring-session-data-redis模块,spirng-data-redis模块存在时 生效*/
@ConditionalOnMissingBean(SessionRepository.class)
class RedisSessionConfiguration {
@Configuration(proxyBeanMethods = false)
public static class SpringBootRedisHttpSessionConfiguration extends RedisHttpSessionConfiguration {
@Autowired
/*xxx: 本质上,还是使用的spring-session的配置,但是要注意,
xxx: 配置项: spring.session.timeout的优先级大于 server.servlet.session.timeout的优先级
xxx: 如果自定义了 session,则spring.session.timeout 或者 server.servlet.session.timeout能否生效? */
public void customize(SessionProperties sessionProperties, RedisSessionProperties redisSessionProperties,
ServerProperties serverProperties) {
Duration timeout = sessionProperties
.determineTimeout(() -> serverProperties.getServlet().getSession().getTimeout());
if (timeout != null) {
setMaxInactiveIntervalInSeconds((int) timeout.getSeconds());
}
setRedisNamespace(redisSessionProperties.getNamespace());
setFlushMode(redisSessionProperties.getFlushMode());
setSaveMode(redisSessionProperties.getSaveMode());
setCleanupCron(redisSessionProperties.getCleanupCron());
}
}
}
# 注意事项
SpringSession
,本质上,就是利用外观模式
,对HttpServletRequest
进行了一层包装;- 包装的实质,是用spring-session的sessionRepository替换了原生的
SessionMapping
,原生的sessionMapping
是耦合在servlet容器中的;
# springBoot打包依赖优先级
# fatJar优先级
- Spring Boot的可执行JAR使用了一个特殊的类加载器,LaunchedURLClassLoader,它依赖于
classpath.idx
文件来定义类路径的顺序。 - 这个文件列出了所有依赖的JAR,按照优先级排序,确保加载顺序正确。这对于解决依赖冲突很重要,比如当两个库有相同类时,先加载的会被使用。
# war优先级
- WAR文件通常是部署到Servlet容器(比如Tomcat)中运行的,这时候应用的类加载机制由容器管理。
- Servlet容器一般有自己的类加载顺序,通常是先加载WEB-INF/classes下的类,然后是WEB-INF/lib下的JAR,按字母顺序加载。这可能会导致依赖加载顺序的问题,因为字母顺序不一定符合项目需要的优先级。
# 解决war依赖优先级的方案(依赖不同,仅用于替换类的情况)
- 将需要覆盖的类,放到starter目录的源码中;
- 修改依赖包名的首字母,尽量靠前