pull/177/head
Libin YANG 5 months ago
parent 464d7b5c0f
commit 4787c419f3

@ -8,6 +8,7 @@ export default defineConfig({
['meta', { name: 'description', content: '读尽天下源码,心中自然无码——源码猎人' }],
['link', { rel: 'icon', type: 'image/png', href: 'https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/favicon-32x32.png' }]
],
ignoreDeadLinks: true,
themeConfig: {
search: {
provider: 'local'

@ -42,7 +42,7 @@ private static final PropertyPlaceholderHelper nonStrictHelper =
- 解析属性
![SystemPropertyUtils-resolvePlaceholders.png](/images/spring/SystemPropertyUtils-resolvePlaceholders.png)
![SystemPropertyUtils-resolvePlaceholders.png](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/spring/SystemPropertyUtils-resolvePlaceholders.png)
时序图因为有递归所以看着有点长, 其核心方法最后会指向 PlaceholderResolver

@ -3,7 +3,7 @@
- 类全路径: `org.springframework.format.datetime.DateTimeFormatAnnotationFormatterFactory`
- 类图
![EmbeddedValueResolutionSupport](/images/spring/DateTimeFormatAnnotationFormatterFactory.png)
![EmbeddedValueResolutionSupport](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/spring/DateTimeFormatAnnotationFormatterFactory.png)
```java
public class DateTimeFormatAnnotationFormatterFactory extends EmbeddedValueResolutionSupport

@ -65,7 +65,7 @@ public final HandlerExecutionChain getHandler(HttpServletRequest request) throws
存在的实现方法
![image-20200915135933146](images/image-20200915135933146.png)
![image-20200915135933146](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/image-20200915135933146.png)
- 先看`org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#getHandlerInternal`方法是怎么一回事.

@ -141,11 +141,11 @@ private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] par
- `SpringFactoriesLoader.loadFactoryNames(type, classLoader)` 是 spring 提供的方法,主要目的是读取`spring.factories`文件
- 读取需要创建的内容
![image-20200318080601725](../../images/SpringBoot/image-20200318080601725.png)
![image-20200318080601725](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200318080601725.png)
- 创建完成
![image-20200318080901881](../../images/SpringBoot/image-20200318080901881.png)
![image-20200318080901881](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200318080901881.png)
- `AnnotationAwareOrderComparator.sort(instances)`排序
@ -153,21 +153,21 @@ private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] par
`SharedMetadataReaderFactoryContextInitializer`
![image-20200318081112670](../../images/SpringBoot/image-20200318081112670.png)
![image-20200318081112670](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200318081112670.png)
- 同样的再找一个`DelegatingApplicationContextInitializer`
![image-20200318081322781](../../images/SpringBoot/image-20200318081322781.png)
![image-20200318081322781](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200318081322781.png)
- 下图中的所有类都有 Order 数值返回
排序前:
![image-20200318081352639](../../images/SpringBoot/image-20200318081352639.png)
![image-20200318081352639](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200318081352639.png)
排序后:
![image-20200318081458019](../../images/SpringBoot/image-20200318081458019.png)
![image-20200318081458019](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200318081458019.png)
### listeners.starting()
@ -360,7 +360,7 @@ private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] par
### exceptionReporters
![image-20200318085243888](../../images/SpringBoot/image-20200318085243888.png)
![image-20200318085243888](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200318085243888.png)
### prepareContext
@ -439,9 +439,9 @@ private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] par
context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
```
![image-20200318090128983](../../images/SpringBoot/image-20200318090128983.png)
![image-20200318090128983](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200318090128983.png)
![image-20200318090312626](../../images/SpringBoot/image-20200318090312626.png)
![image-20200318090312626](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200318090312626.png)
### applyInitializers
@ -466,7 +466,7 @@ private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] par
- 数据结果
![image-20200318090935285](../../images/SpringBoot/image-20200318090935285.png)
![image-20200318090935285](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200318090935285.png)
- 子类的具体实现不展开了
@ -488,7 +488,7 @@ private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] par
- `primarySources` 就是我们的项目启动类,在`SpringApplication`的构造器中有`this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources))`
![image-20200318091558233](../../images/SpringBoot/image-20200318091558233.png)
![image-20200318091558233](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200318091558233.png)
### load
@ -552,7 +552,7 @@ private int load(Object source) {
- 通过前文我们已经知道 `source`就是一个 class
![image-20200318092027020](../../images/SpringBoot/image-20200318092027020.png)
![image-20200318092027020](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200318092027020.png)
```java
private int load(Class<?> source) {

@ -97,7 +97,7 @@ public enum SearchStrategy {
- 类图
![image-20200824085726621](../../images/SpringBoot/image-20200824085726621.png)
![image-20200824085726621](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200824085726621.png)
在看这部分源码之前需要先了解 `Conditional`和`Condition`的源码
@ -421,7 +421,7 @@ for (String type : spec.getTypes()) {
- 在忽略 bean 找到之后做一个类型移除的操作.
![image-20200825140750035](../../images/SpringBoot/image-20200825140750035.png)
![image-20200825140750035](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200825140750035.png)
### 返回值
@ -469,7 +469,7 @@ public static ConditionOutcome noMatch(ConditionMessage message) {
return ConditionOutcome.match(matchMessage);
```
![image-20200825141506531](../../images/SpringBoot/image-20200825141506531.png)
![image-20200825141506531](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200825141506531.png)
- 到此结果封装完毕.回到方法`org.springframework.boot.autoconfigure.condition.SpringBootCondition#matches(org.springframework.context.annotation.ConditionContext, org.springframework.core.type.AnnotatedTypeMetadata)` 继续进行
- 再往后就继续执行 spring 的 bean 初始化咯
@ -492,7 +492,7 @@ public static ConditionOutcome noMatch(ConditionMessage message) {
- 根据类的注解信息我们可以找到有`ResourceBundleCondition`
![image-20200825092343271](../../images/SpringBoot/image-20200825092343271.png)
![image-20200825092343271](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200825092343271.png)
- 获取类名或者方法名的结果是`MessageSourceAutoConfiguration`全路径
@ -592,8 +592,8 @@ org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition
- 此时我们可以和前文的源码分析连接起来有一个完整的认识了
![image-20200825142332485](../../images/SpringBoot/image-20200825142332485.png)
![image-20200825142332485](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200825142332485.png)
- 最后来看整体类图
![image-20200825142418115](../../images/SpringBoot/image-20200825142418115.png)
![image-20200825142418115](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200825142418115.png)

@ -33,7 +33,7 @@ public @interface ConfigurationPropertiesScan {}
## ConfigurationPropertiesScanRegistrar
![image-20200323094446756](../../images/SpringBoot/image-20200323094446756.png)
![image-20200323094446756](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200323094446756.png)
- debug 没有抓到后续补充
@ -137,11 +137,11 @@ public @interface EnableConfigurationProperties {
- 先看输入参数 **metadata**
![image-20200323134135926](../../images/SpringBoot/image-20200323134135926.png)
![image-20200323134135926](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200323134135926.png)
- getTypes 结果
![image-20200323134325955](../../images/SpringBoot/image-20200323134325955.png)
![image-20200323134325955](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200323134325955.png)
- 源码开始,先找出刚才的对象`org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration`
@ -192,7 +192,7 @@ public @interface EnableConfigurationProperties {
## ConfigurationPropertiesBindingPostProcessor
![image-20200323095626953](../../images/SpringBoot/image-20200323095626953.png)
![image-20200323095626953](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200323095626953.png)
### postProcessBeforeInitialization
@ -301,15 +301,15 @@ public @interface EnableConfigurationProperties {
- `annotation`
![image-20200323104711545](../../images/SpringBoot/image-20200323104711545.png)
![image-20200323104711545](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200323104711545.png)
- `bindType`
![image-20200323104815305](../../images/SpringBoot/image-20200323104815305.png)
![image-20200323104815305](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200323104815305.png)
- 返回对象
![image-20200323105053757](../../images/SpringBoot/image-20200323105053757.png)
![image-20200323105053757](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200323105053757.png)
- 此时数据还没有进去
@ -319,7 +319,7 @@ public @interface EnableConfigurationProperties {
直接看结果
![image-20200323105155998](../../images/SpringBoot/image-20200323105155998.png)
![image-20200323105155998](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200323105155998.png)
- 上述配置和我在配置文件中写的配置一致
@ -361,7 +361,7 @@ BindResult<?> bind(ConfigurationPropertiesBean propertiesBean) {
}
```
![image-20200323105830138](../../images/SpringBoot/image-20200323105830138.png)
![image-20200323105830138](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200323105830138.png)
##### findProperty
@ -427,11 +427,11 @@ BindResult<?> bind(ConfigurationPropertiesBean propertiesBean) {
```
![image-20200323115408877](../../images/SpringBoot/image-20200323115408877.png)
![image-20200323115408877](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200323115408877.png)
![image-20200323115701118](../../images/SpringBoot/image-20200323115701118.png)
![image-20200323115701118](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200323115701118.png)
![image-20200323115711826](../../images/SpringBoot/image-20200323115711826.png)
![image-20200323115711826](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200323115711826.png)
##### getBindHandler
@ -464,7 +464,7 @@ private <T> BindHandler getBindHandler(Bindable<T> target, ConfigurationProperti
- 最终获取得到的处理器
![image-20200323110603959](../../images/SpringBoot/image-20200323110603959.png)
![image-20200323110603959](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200323110603959.png)
- 最后的 bind
@ -498,7 +498,7 @@ private <T> BindHandler getBindHandler(Bindable<T> target, ConfigurationProperti
```
![image-20200323112945449](../../images/SpringBoot/image-20200323112945449.png)
![image-20200323112945449](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200323112945449.png)
配置信息到此绑定成功,关于如何处理集合相关的配置请各位读者自行学习

@ -19,7 +19,7 @@
- `org.springframework.boot.logging.java.JavaLoggingSystem`
![image-20200323144523848](../../images/SpringBoot/image-20200323144523848.png)
![image-20200323144523848](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200323144523848.png)
```java
static {
@ -125,7 +125,7 @@ private static LoggingSystem get(ClassLoader classLoader, String loggingSystemCl
```
![image-20200323151409473](../../images/SpringBoot/image-20200323151409473.png)
![image-20200323151409473](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200323151409473.png)
- 默认日志: `org.springframework.boot.logging.logback.LogbackLoggingSystem`
@ -133,7 +133,7 @@ private static LoggingSystem get(ClassLoader classLoader, String loggingSystemCl
- 初始化之前
![image-20200323154205484](../../images/SpringBoot/image-20200323154205484.png)
![image-20200323154205484](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200323154205484.png)
- 链路
@ -344,9 +344,9 @@ private static LoggingSystem get(ClassLoader classLoader, String loggingSystemCl
- 添加配置文件
![image-20200323161442058](../../images/SpringBoot/image-20200323161442058.png)
![image-20200323161442058](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200323161442058.png)
![image-20200323161522570](../../images/SpringBoot/image-20200323161522570.png)
![image-20200323161522570](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200323161522570.png)
- 此时配置文件地址出现了

@ -9,17 +9,17 @@
2. 全局搜索 yml
![image-20200319083048849](../../images/SpringBoot/image-20200319083048849.png)
![image-20200319083048849](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200319083048849.png)
3. 换成`properties`搜索
![image-20200319083140225](../../images/SpringBoot/image-20200319083140225.png)
![image-20200319083140225](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200319083140225.png)
4. 我们以`yml`为例打上断点开始源码追踪
看到调用堆栈
![image-20200319083345067](../../images/SpringBoot/image-20200319083345067.png)
![image-20200319083345067](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200319083345067.png)
- 一步一步回上去看如何调用具体方法的
@ -29,9 +29,9 @@
### 调用过程
![image-20200319082131146](../../images/SpringBoot/image-20200319082131146.png)
![image-20200319082131146](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200319082131146.png)
![image-20200319082544653](../../images/SpringBoot/image-20200319082544653.png)
![image-20200319082544653](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200319082544653.png)
`org.springframework.boot.context.config.ConfigFileApplicationListener#addPropertySources`
@ -68,13 +68,13 @@ protected void addPropertySources(ConfigurableEnvironment environment, ResourceL
- 搜索目标: `org.springframework.boot.env.PropertySourceLoader`
![image-20200319084141748](../../images/SpringBoot/image-20200319084141748.png)
![image-20200319084141748](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200319084141748.png)
![image-20200319084151997](../../images/SpringBoot/image-20200319084151997.png)
![image-20200319084151997](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200319084151997.png)
观察发现里面有一个`YamlPropertySourceLoader`和我们之前找 yml 字符串的时候找到的类是一样的。说明搜索方式没有什么问题。
![image-20200319084357652](../../images/SpringBoot/image-20200319084357652.png)
![image-20200319084357652](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200319084357652.png)
初始化完成,后续进行解析了
@ -110,7 +110,7 @@ protected void addPropertySources(ConfigurableEnvironment environment, ResourceL
### initializeProfiles
- 初始化`private Deque<Profile> profiles;` 属性
- ![image-20200319084902957](../../images/SpringBoot/image-20200319084902957.png)
- ![image-20200319084902957](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200319084902957.png)
### load
@ -135,7 +135,7 @@ private void load(Profile profile, DocumentFilterFactory filterFactory, Document
- 资源路径可能性
![image-20200319085446640](../../images/SpringBoot/image-20200319085446640.png)
![image-20200319085446640](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200319085446640.png)
该方法采用循环每个路径下面都去尝试一遍
@ -190,7 +190,7 @@ private void load(Profile profile, DocumentFilterFactory filterFactory, Document
```
![image-20200319090446231](../../images/SpringBoot/image-20200319090446231.png)
![image-20200319090446231](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200319090446231.png)
- `PropertiesPropertySourceLoader`解析同理不在次展开描述了

@ -53,7 +53,7 @@ public @interface EnableAutoConfiguration {
- 类图
![image-20200320150642022](../../images/SpringBoot/image-20200320150642022.png)
![image-20200320150642022](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200320150642022.png)
## getAutoConfigurationMetadata()
@ -107,7 +107,7 @@ public @interface EnableAutoConfiguration {
```
![image-20200320160423991](../../images/SpringBoot/image-20200320160423991.png)
![image-20200320160423991](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200320160423991.png)
- `protected static final String PATH = "META-INF/spring-autoconfigure-metadata.properties";`
@ -131,11 +131,11 @@ public @interface EnableAutoConfiguration {
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
```
![image-20200320162835665](../../images/SpringBoot/image-20200320162835665.png)
![image-20200320162835665](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200320162835665.png)
同样找一下 redis
![image-20200320163001728](../../images/SpringBoot/image-20200320163001728.png)
![image-20200320163001728](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200320163001728.png)
- 仔细看`org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration`类
@ -213,13 +213,13 @@ public class RedisProperties {
- `org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#process`
![image-20200320163806852](../../images/SpringBoot/image-20200320163806852.png)
![image-20200320163806852](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200320163806852.png)
再此之前我们看过了`getAutoConfigurationMetadata()`的相关操作
关注 `AnnotationMetadata annotationMetadata` 存储了一些什么
![image-20200320164145286](../../images/SpringBoot/image-20200320164145286.png)
![image-20200320164145286](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200320164145286.png)
这里简单理解
@ -271,7 +271,7 @@ public class RedisProperties {
```
![image-20200320171138431](../../images/SpringBoot/image-20200320171138431.png)
![image-20200320171138431](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200320171138431.png)
### getCandidateConfigurations
@ -289,7 +289,7 @@ public class RedisProperties {
```
![image-20200320171734270](../../images/SpringBoot/image-20200320171734270.png)
![image-20200320171734270](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200320171734270.png)
- 第一个是我自己写的一个测试用
@ -341,7 +341,7 @@ public class RedisProperties {
```
![image-20200323080611527](../../images/SpringBoot/image-20200323080611527.png)
![image-20200323080611527](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200323080611527.png)
- 修改启动类
@ -350,7 +350,7 @@ public class RedisProperties {
```
![image-20200323081009823](../../images/SpringBoot/image-20200323081009823.png)
![image-20200323081009823](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200323081009823.png)
### checkExcludedClasses
@ -418,7 +418,7 @@ public class RedisProperties {
- `getAutoConfigurationImportFilters()` 从`spring.factories` 获取 `AutoConfigurationImportFilter`的接口
![image-20200323081903145](../../images/SpringBoot/image-20200323081903145.png)
![image-20200323081903145](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200323081903145.png)
- 循环内执行`Aware`系列接口
@ -426,7 +426,7 @@ public class RedisProperties {
- `filter.match(candidates, autoConfigurationMetadata)` 比较判断哪些是需要自动注入的类
![image-20200323082553595](../../images/SpringBoot/image-20200323082553595.png)
![image-20200323082553595](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200323082553595.png)
### fireAutoConfigurationImportEvents
@ -448,11 +448,11 @@ public class RedisProperties {
```
![image-20200323083149737](../../images/SpringBoot/image-20200323083149737.png)
![image-20200323083149737](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200323083149737.png)
- `AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);`
![image-20200323083247061](../../images/SpringBoot/image-20200323083247061.png)
![image-20200323083247061](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200323083247061.png)
- `org.springframework.boot.autoconfigure.AutoConfigurationImportListener#onAutoConfigurationImportEvent` 在执行自动配置时触发 , 实现类只有 **`ConditionEvaluationReportAutoConfigurationImportListener`**
@ -470,7 +470,7 @@ public class RedisProperties {
```
![image-20200323083656670](../../images/SpringBoot/image-20200323083656670.png)
![image-20200323083656670](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200323083656670.png)
- 初始化完
@ -478,7 +478,7 @@ public class RedisProperties {
- `org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#process`
![image-20200323084922159](../../images/SpringBoot/image-20200323084922159.png)
![image-20200323084922159](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringBoot/image-20200323084922159.png)
- 后续的一些行为相对简单,直接放个源码了.

@ -102,13 +102,13 @@ public class GatewayClassPathWarningAutoConfiguration {
>
> Route 是由 AsyncPredicate 和 GatewayFilter 组成的。而 AsyncPredicate 由 RoutePredicateFactory 生成GatewayF 创建 ilter 由 GatewayFilterFactory
![RouteLocator](../../images/SpringCloud/spring-cloud-gateway-source-note_imgs/RouteLocator.png)
![RouteLocator](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringCloud/spring-cloud-gateway-source-note_imgs/RouteLocator.png)
> RoutePredicateHandlerMapping 通过 RouteLocator 得到的 `Flux<Route>` ,遍历执行`Route.getPredicate().apply(ServerWebExchange)` 返回`true`说明命中了路由规则,将命中的 Route 存到 ServerWebExchange 中,然后执行 FilteringWebHandler 。
>
> FilteringWebHandler 的逻辑就是执行 GlobalFilter + GatewayFilter
![Route](../../images/SpringCloud/spring-cloud-gateway-source-note_imgs/Route.png)
![Route](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringCloud/spring-cloud-gateway-source-note_imgs/Route.png)
### 源码
@ -633,7 +633,7 @@ public @interface ConditionalOnEnabledPredicate {
因为 @ConditionalOnEnabledGlobalFilter 上标注了 @Conditional,所以在 [ConfigurationClassPostProcessor](https://github.com/haitaoss/spring-framework/blob/source-v5.3.10/note/spring-source-note.md#conditional) 解析配置类时,会执行 `OnEnabledGlobalFilter#matches(ConditionContext,AnnotatedTypeMetadata)` 结果是`true`才会将 bean 注册到 BeanFactory 中
![OnEnabledComponent](../../images/SpringCloud/spring-cloud-gateway-source-note_imgs/OnEnabledComponent.png)
![OnEnabledComponent](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringCloud/spring-cloud-gateway-source-note_imgs/OnEnabledComponent.png)
```java
/**
@ -1121,7 +1121,7 @@ public class PropertiesRouteDefinitionLocator implements RouteDefinitionLocator
看 PredicateDefinition、FilterDefinition 的构造器,就能明白属性文件为啥可以写 `Weight=group1,8`
![image-20230428141218057](../../images/SpringCloud/spring-cloud-gateway-source-note_imgs/image-20230428141218057-1682662351478.png)
![image-20230428141218057](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringCloud/spring-cloud-gateway-source-note_imgs/image-20230428141218057-1682662351478.png)
## InMemoryRouteDefinitionRepository
@ -1129,7 +1129,7 @@ InMemoryRouteDefinitionRepository 是由 [GatewayAutoConfiguration](#GatewayAuto
RouteDefinitionRepository 的职责是通过缓存的方式记录 RouteDefinition而不是通过属性 映射成 RouteDefinition。而 [AbstractGatewayControllerEndpoint](#AbstractGatewayControllerEndpoint) 会依赖 RouteDefinitionWriter 的实例,用来缓存通过接口方式注册的 RouteDefinition。
![RouteDefinitionRepository](../../images/SpringCloud/spring-cloud-gateway-source-note_imgs/RouteDefinitionRepository.png)
![RouteDefinitionRepository](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringCloud/spring-cloud-gateway-source-note_imgs/RouteDefinitionRepository.png)
```java
public class InMemoryRouteDefinitionRepository implements RouteDefinitionRepository {
@ -1186,7 +1186,7 @@ GatewayControllerEndpoint 和 GatewayLegacyControllerEndpoint 是由 [GatewayAut
刷新 RouteDefinition 是会发布 RefreshRoutesEvent 事件,该事件会有 [CachingRouteLocator](#RouteLocator) 处理
![AbstractGatewayControllerEndpoint](../../images/SpringCloud/spring-cloud-gateway-source-note_imgs/AbstractGatewayControllerEndpoint.png)
![AbstractGatewayControllerEndpoint](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringCloud/spring-cloud-gateway-source-note_imgs/AbstractGatewayControllerEndpoint.png)
## RouteRefreshListener

File diff suppressed because it is too large Load Diff

@ -1,319 +0,0 @@
# Spring Security 自定义用户认证
在**Spring Boot 中开启 Spring Security**一节中我们简单地搭建了一个 Spring Boot + Spring Security 的项目,其中登录页、用户名和密码都是由 Spring Security 自动生成的。Spring Security 支持我们自定义认证的过程,如使用自定义的登录页替换默认的登录页,用户信息的获取逻辑、登录成功或失败后的处理逻辑等。这里将在上一节的源码基础上进行改造。
## 配置自定义登录页
为了方便起见,我们直接在`src/main/resources/resources`目录下创建一个`login.html`(不需要 Controller 跳转):
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>登录</title>
<link rel="stylesheet" href="css/login.css" type="text/css">
</head>
<body>
<form class="login-page" action="/login" method="post">
<div class="form">
<h3>账户登录</h3>
<input type="text" placeholder="用户名" name="username" required="required" />
<input type="password" placeholder="密码" name="password" required="required" />
<button type="submit">登录</button>
</div>
</form>
</body>
</html>
```
要怎么做才能让 Spring Security 跳转到我们自己定义的登录页面呢?很简单,只需要在 `BrowserSecurityConfig``configure` 中添加一些配置:
```java
@Configuration
public class BrowserConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin() // 表单登录
.loginPage("/login.html") // 自定义登录页
.loginProcessingUrl("/login") // 登录认证路径
.and()
.authorizeRequests() // 授权配置
.antMatchers("/login.html", "/css/**", "/error").permitAll() // 无需认证
.anyRequest().authenticated() // 其他所有请求都需要认证
.and()
.csrf().disable(); // 禁用 CSRF
}
}
```
上面代码中`.loginPage("/login.html")`指定了跳转到登录页面的请求 URL`.loginProcessingUrl("/login")`对应登录页面 form 表单的`action="/login"``.antMatchers("/login.html", "/css/", "/error").permitAll()`表示跳转到登录页面的请求不被拦截。
这时候启动系统,访问`http://localhost:8080/hello`,会看到页面已经被重定向到了`http://localhost:8080/login.html`
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/d6bd19a2-08d3-4ba6-921c-5b5f57370a16.jpg)
## 配置用户信息的获取逻辑
Spring Security 默认会为我们生成一个用户名为 user密码随机的用户实例当然我们也可以定义自己用户信息的获取逻辑只需要实现 Spring Security 提供的**_UserDetailService_**接口即可,该接口只有一个抽象方法**_loadUserByUsername_**,具体实现如下:
```java
@Service
public class UserDetailService implements UserDetailsService {
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return new User(username, passwordEncoder.encode("123456"), AuthorityUtils.createAuthorityList("admin"));
}
}
```
通过以上配置,我们定义了一个用户名随机,密码统一为 123456 的用户信息的获取逻辑。这样,当我们启动项目,访问`http://localhost:8080/login`,只需要输入任意用户名以及 123456 作为密码即可登录系统。
## 源码解析
### BrowserConfig 配置解析
我们首先来梳理下 `BrowserConfig` 中的配置是如何被 Spring Security 所加载的。
首先找到调用 `BrowserConfig``configure()` 的地方,在其父类 `WebSecurityConfigurerAdapter``getHttp()` 中:
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/12629a18-56ef-4286-9ab9-c124dc3d6791.png)
往上一步找到调用 `getHttp()` 的地方,在同个类的 `init()` 中:
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/2b54af34-7d68-4f40-8726-d02d18e03dea.png)
往上一步找到调用`init()`的地方,在 `AbstractConfiguredSecurityBuilder` 的`init()`中:
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/6e009bf1-aba3-4b89-8e86-d3d110e0f4a7.png)
在`init()`被调用时,它首先会遍历`getConfigurers()`返回的集合中的元素,调用其`init()`,点击`getConfigurers()`查看,发现其读取的是`configurers`属性的值,那么`configurers`是什么时候被赋值的呢?我们在同个类的`add()`中找到`configurers`被赋值的代码:
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/ed65fd2a-8a9f-4808-bc16-36128b4af47a.png)
往上一步找到调用`add()`的地方,在同个类的`apply()`中:
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/1e929fec-d1ab-44b5-89bc-6e5ebcda1daf.png)
往上一步找到调用`apply()`的地方,在`WebSecurityConfiguration`的`setFilterChainProxySecurityConfigurer()`中:
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/1840f96a-6a31-4fce-8a98-02fa7fc60fbf.png)
我们可以看到,在`setFilterChainProxySecurityConfigurer()`中,首先会实例化一个`WebSecurity``AbstractConfiguredSecurityBuilder`的实现类)的实例,遍历参数`webSecurityConfigurers`,将存储在其中的元素作为参数传递给`WebSecurity`的`apply()`,那么`webSecurityConfigurers`是什么时候被赋值的呢?我们根据`@Value`中的信息找到`webSecurityConfigurers`被赋值的地方,在`AutowiredWebSecurityConfigurersIgnoreParents`的`getWebSecurityConfigurers()`中:
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/eb7a5916-4049-4682-9a11-10f1f1f94c74.png)
我们重点看第二句代码,可以看到这里会提取存储在 bean 工厂中类型为`WebSecurityConfigurer.class`的 bean而`BrowserConfig`正是`WebSecurityConfigurerAdapter`的实现类。
解决完`configurers`的赋值问题,我们回到`AbstractConfiguredSecurityBuilder`的`init()`处,找到调用该方法的地方,在同个类的`doBuild()`中:
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/522574e3-bacc-4794-a17e-492bc2b4457d.png)
往上一步找到调用`doBuild()`的地方,在`AbstractSecurityBuilder`的`build()`中:
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/fb22f2ca-3d9a-420f-b77a-f9c0f737d9ad.png)
往上一步找到调用`doBuild()`的地方,在`WebSecurityConfiguration`的`springSecurityFilterChain()`中:
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/a5c61feb-ca72-4768-94bd-1b0a8cf8af70.png)
至此,我们分析完了`BrowserConfig`被 Spring Security 加载的过程。现在我们再来看看当我们自定义的配置被加载完后,`http`各属性的变化,在`BrowserConfig`的`configure()`末尾打上断点,当程序走到断点处时,查看`http`属性:
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/6c72c09b-742c-4415-851a-8ca5292a4969.png)
我们配置的`.loginPage("/login.html")`和`.loginProcessingUrl("/login")`在`FormLoginConfigurer`中:
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/51ba02f0-bae6-4c08-9adf-7ee0f12b05d3.png)
配置的`.antMatchers("/login.html", "/css/", "/error").permitAll()`在`ExpressionUrlAuthorizationConfigurer`中:
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/52390725-d87d-42b1-9071-ea21e445e1e6.png)
这样,当我们访问除`"/login.html", "/css/", "/error"`以外的路径时,在`AbstractSecurityInterceptor``FilterSecurityInterceptor`的父类)的`attemptAuthorization()`中会抛出`AccessDeniedException`异常(最终由`AuthenticationTrustResolverImpl`的<strong></i>isAnonymous()`进行判断)
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/558b8b1c-be32-44c4-8d8f-5f0d231741f8.png)
当我们访问`"/login.html", "/css/", "/error"`这几个路径时,在`AbstractSecurityInterceptor``FilterSecurityInterceptor`的父类)的`attemptAuthorization()`中正常执行(最终由`SecurityExpressionRoot`的`permitAll()`进行判断)
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/4612c27e-dd9f-4e60-92dc-fc9858496ec5.png)
### login.html 路径解析
当我们请求的资源需要经过认证时Spring Security 会将请求重定向到我们自定义的登录页,那么 Spring 又是如何找到我们自定义的登录页的呢?下面就让我们来解析一下:
我们首先来到`DispatcherServlet`中,`DispatcherServlet`是 Spring Web 处理请求的入口。当 Spring Web 项目启动后,第一次接收到请求时,会调用其`initStrategies()`进行初始化:
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/964ec4a4-6039-4205-8a87-ea2febcc00b6.png)
我们重点关注`initHandlerMappings(context);`这句,`initHandlerMappings()`用于初始化处理器映射器(处理器映射器可以根据请求找到对应的资源),我们来到`initHandlerMappings()`中:
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/0a97b011-34ed-4d57-945e-95c8e6bafc8e.png)
可以看到,当程序走到`initHandlerMappings()`中时,会从 bean 工厂中找出`HandlerMapping.class`类型的 bean将其存储到`handlerMappings`属性中。这里看到一共找到 5 个,分别是:`requestMappingHandlerMapping`(将请求与标注了`@RequestMapping`的方法进行关联)、`weclomePageHandlerMapping`(将请求与主页进行关联)、`beanNameHandlerMapping`(将请求与同名的 bean 进行关联)、`routerFunctionMapping`(将请求与`RouterFunction`进行关联)、`resourceHandlerMapping`(将请求与静态资源进行关联),这 5 个 bean 是在`WebMvcAutoConfiguration$EnableWebMvcConfiguration`中配置的:
`requestMappingHandlerMapping:`
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/cd7aee86-66f8-4197-99d1-1c9275e33bee.png)
`weclomePageHandlerMapping:`
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/ef28372b-de89-46ff-8679-8b8feca04a7a.png)
`beanNameHandlerMapping`、`routerFunctionMapping`、`resourceHandlerMapping`在`EnableWebMvcConfiguration`的父类(`WebMvcConfigurationSupport`)中配置:
`beanNameHandlerMapping:`
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/8d05ac54-f034-47d4-b750-67b2e3b3cd14.png)
`routerFunctionMapping:`
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/481b88aa-028d-4392-8c0a-365f1d0e2ae9.png)
`resourceHandlerMapping:`
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/fcdab503-2735-46bd-a5b6-226dc348e78c.png)
我们将目光锁定在`resourceHandlerMapping`上,当`resourceHandlerMapping`被初始化时,会调用`addResourceHandlers()`为`registry`添加资源处理器,我们找到实际被调用的`addResourceHandlers()`,在`DelegatingWebMvcConfiguration`中:
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/6eca7b58-80f9-4e98-8483-62b4ef751854.png)
可以看到这里实际调用的是`configurers`属性的`addResourceHandlers()`,而`configurers`是一个 final 类型的成员变量,其值是`WebMvcConfigurerComposite`的实例,我们来到`WebMvcConfigurerComposite`中:
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/c365e5ab-a7a3-4ebf-8a25-09cd2e049f22.png)
可以看到这里实际调用的是`delegates`属性的`addResourceHandlers()``delegates`是一个 final 类型的集合,集合的元素由`addWebMvcConfigurers()`负责添加:
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/895afead-ea6b-4ac6-8138-fbe0a223daf9.png)
我们找到调用`addWebMvcConfigurers()`的地方,在`DelegatingWebMvcConfiguration`的`setConfigurers()`中:
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/a20e06a4-5a43-4edf-bea1-53fc9bc929e9.png)
可以看到当`setConfigurers()`被初始化时Spring 会往参数`configurers`中传入两个值,我们关注第一个值,是一个`WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter`的实例,注意它的属性`resourceProperties`,是一个`WebProperties$Resources`的实例,默认情况下,在实例化`WebMvcAutoConfigurationAdapter`时,由传入参数`webProperties`进行赋值:`webProperties.getResources()`
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/97b6f22c-414d-4a16-aa8e-c3deece2f7cd.png)
我们进入参数`webProperties`的类中,可以看到`getResources()`是直接实例化了一个`Resources`,其属性`staticLocations`是一个含有 4 个值的 final 类型的字符串数组,这 4 个值正是 Spring 寻找静态文件的地方:
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/4d84fe43-2646-4a6f-a580-f39f6416d02d.png)
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/25899949-dac3-4873-a2af-7abfe0e97615.png)
我们回到`WebMvcAutoConfiguration`的`addResourceHandlers()`中:![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/003ff2fa-022e-47cb-8aa9-343ed7c40c4a.png)
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/ccd16b38-d724-4c03-949e-3b6ba03268a9.png)
在`addResourceHandlers()`中,会为`registry`添加两个资源处理器,当请求路径是“/webjars/”时会在”classpath:/META-INF/resources/webjars/“路径下寻找对应的资源,当请求路径是“/\*\*”时会在”classpath:/META-INF/resources/“、”classpath:/resources/“、”classpath:/static/“、”classpath:/public/“路径下寻找对应的资源。
现在我们通过访问`http://localhost:8080/login.html`来验证这个过程。
请求首先来到`DispatcherServlet`的`doDispatch()`中,由于是对静态资源的请求,当程序走到`mappedHandler = getHandler(processedRequest);`时,通过`getHandler()`返回`SimpleUrlHandlerMapping`(即`resourceHandlerMapping`的类型)的`HandlerExecutionChain`
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/4c9c302d-2ce0-4b5b-beb6-c76d2e94038f.png)
然后由实际的处理器进行处理:
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/1590bae4-2e3a-4f97-b02d-d918c49cac22.png)
程序一路调试,来到`ResourceHttpRequestHandler`的`handleRequest()`中,通过调用`Resource resource = getResource(request);`找到请求对应的资源:
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/0879e131-da5f-491e-a153-42770ce8b975.png)
而在`getResource()`中,实际是将请求路径(即`login.html`)与前面配置的路径进行拼接(组合成`/resources/login.html`这样的路径),再通过类加载器来寻找资源。
后面源码深入过深,就不一一展开了,只截取其中比较重要的几段代码:
`PathResourceResolver`中:
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/6c58e27d-dd29-48fc-b597-8067e1c97786.png)
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/c7ef78df-c2ab-4f89-b5ad-45561a91ffcc.png)
`ClassPathResource`中:
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/42c5125e-0dc2-4c0c-9434-af4a9efd2d5d.png)
`ClassLoader`中:
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/7842f83d-5417-4cb2-bb30-d70f98c3053f.png)
`URLClassLoader`中:
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/870891e9-f8ea-4097-98c9-829b1cdcf145.png)
最终,类加载器会在如上两个路径下找到登录页并返回。
### UserDetailService 配置解析
我们定义的用户信息的获取逻辑是如何被 Spring Security 应用的呢?让我们通过阅读源码来了解一下。
还记得前面我们讲**_BrowserConfig 配置_**被加载的过程吗?**_UserDetailService_**也是在这个过程中被一起加载完成的,回到**BrowserConfig 配置解析**的第一幅图中,如下:
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/68490740-e03c-4353-b5fc-ac99c0cf0435.png)
在断点处位置,`authenticationManager()`会返回一个**_AuthenticationManager_**实例,我们进入`authenticationManager()`中:
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/e7a1a684-64db-41d0-a5c0-d9a841d86cc1.png)
在`authenticationManager()`中,**_AuthenticationManager_**转由**_AuthenticationConfiguration_**中获取,我们进入`getAuthenticationManager()`中:
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/d9ae84ae-d60c-4d9c-a7fd-cddeb1142f95.png)
程序来到**_AuthenticationConfiguration_**的`getAuthenticationManager()`中,**_AuthenticationManager_**转由**_AuthenticationManagerBuilder_**中获取,我们进入`build()`中:
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/19f71152-f456-4db7-a1d5-f79aaa37253b.png)
程序来到**_AbstractConfiguredSecurityBuilder_**的`doBuild()`中,这里在构建**_AuthenticationManager_**实例时,需要初始化 3 个配置类,我们重点关注第 3 个配置类:**_org.springframework.security.config.annotation.authentication.configuration.InitializeUserDetailsBeanManagerConfigurer_**,这个配置类是在**_AuthenticationConfiguration_**中引入的:
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/9f06c823-645d-413b-8bb6-1d81b8f329ea.png)
我们来到**_InitializeUserDetailsBeanManagerConfigurer_**的`init()`中:
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/cd7d6cb9-c6e7-4570-aab8-309adcb15e16.png)
这里会新建一个**_InitializeUserDetailsManagerConfigurer_**实例添加到**_AuthenticationManagerBuilder_**中。我们回到`doBuild()`中:
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/3ea76980-417d-4c0c-9330-e0bb241c6a47.png)
可以看到配置类变成了 5 个,其中就有刚刚新建的**_InitializeUserDetailsManagerConfigurer_**,程序接下来会调用各个配置类的`configure()`进行配置,我们来到**_InitializeUserDetailsManagerConfigurer_**的`configure()`中:
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/ec39a9f6-c97d-4b7d-8843-d20358c1d194.png)
可以看到在`configure()`中,就会去 bean 工厂中寻找**_UserDetailsService_**类型的 bean若是我们没有自定义**_UserDetailsService_**的实现类的话Spring Security 默认会生成一个**_InMemoryUserDetailsManager_**的实例:
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/c6a7370c-4afb-4c5d-aa35-ba9c3406b1ed.png)
**_InMemoryUserDetailsManager_**是在**_UserDetailsServiceAutoConfiguration_**类中配置的:
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/476f8954-abe3-4e26-bfe1-8c5b4abbf0e0.png)
解决完**_UserDetailsService_**的加载问题,现在我们来看看 Spring Security 是如何通过**_UserDetailsService_**获取用户信息的。
通过**Spring Boot 中开启 Spring Security**一节的学习我们知道,登录判断的逻辑是在**_UsernamePasswordAuthenticationFilter_**中进行的,因此我们在**_UsernamePasswordAuthenticationFilter_**的`attemptAuthenticatio()`中打上断点,然后启动项目,访问登录页,输入用户名和密码点击登录后,程序来到**_UsernamePasswordAuthenticationFilter_**中:
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/1282014b-fc29-4c2b-9316-9fdd638653c9.png)
这里将验证的逻辑交由**_AuthenticationManager_**进行,我们进入`authenticate()`中:
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/5d511c93-3614-40e0-b3c9-9673c573d60f.png)
程序来到**_ProviderManager_**的`authenticate()`中,这里将验证的逻辑委托给其父类进行,再次点击进入`authenticate()`中:
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/8dddd63e-c567-4b41-a9d9-8ef8aa6f2a92.png)
这里将验证的逻辑交由**_AuthenticationProvider_**进行,我们进入`authenticate()`中:
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/ec796a9b-7c65-49a2-9f9f-7685af7bd57b.png)
程序来到**_AbstractUserDetailsAuthenticationProvider_**的`authenticate()`中,这里会根据用户名去寻找对应的用户实例,我们进入`retrieveUser()`中:
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/3980e264-c073-456a-b808-715edd85633a.png)
程序来到**_DaoAuthenticationProvider_**的`retrieveUser()`中,可以看到正是在这里,会从**_UserDetailsService_**的`loadUserByUsername()`中寻找对应的用户信息。
## 参考
1. [Spring Security 自定义用户认证](https://mrbird.cc/Spring-Security-Authentication.html)

@ -34,7 +34,7 @@ public class HelloController {
这时候我们直接启动项目,访问<strong><i>http://localhost:8080/hello</i></strong>,可以看到页面跳转到一个登陆页面:
![image-20210811091508157](../../images/SpringSecurity/image-20210811091508157.png)
![image-20210811091508157](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/image-20210811091508157.png)
默认的用户名为 user密码由 Sping Security 自动生成,回到 IDEA 的控制台,可以找到密码信息:
@ -50,11 +50,11 @@ Spring Security 默认为我们开启了一个简单的安全配置,下面让
当 Spring Boot 项目配置了 Spring Security 后Spring Security 的整个加载过程如下图所示:
![image-20210811091633434](../../images/SpringSecurity/image-20210811091633434.png)
![image-20210811091633434](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/image-20210811091633434.png)
而当我们访问<strong><i>http://localhost:8080/hello</i></strong>时,代码的整个执行过程如下图所示:
![image-20210811091659121](../../images/SpringSecurity/image-20210811091659121.png)
![image-20210811091659121](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/image-20210811091659121.png)
如上图所示Spring Security 包含了众多的过滤器,这些过滤器形成了一条链,所有请求都必须通过这些过滤器后才能成功访问到资源。
@ -62,69 +62,69 @@ Spring Security 默认为我们开启了一个简单的安全配置,下面让
首先,通过前面可以知道,当有请求来到时,最先由**_DelegatingFilterProxy_**负责接收,因此在**_DelegatingFilterProxy_**的<strong><i>doFilter()</i></strong>的首行打上断点:
![image-20210811091719470](../../images/SpringSecurity/image-20210811091719470.png)
![image-20210811091719470](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/image-20210811091719470.png)
接着**_DelegatingFilterProxy_**会将请求委派给**_FilterChainProxy_**进行处理,在**_FilterChainProxy_**的首行打上断点:
![img](../../images/SpringSecurity/56ac5128-eab7-4b92-912f-ff50bac68a4f.png)
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/56ac5128-eab7-4b92-912f-ff50bac68a4f.png)
**_FilterChainProxy_**会在<strong><i>doFilterInternal()</i></strong>中生成一个内部类**_VirtualFilterChain_**的实例,以此来调用 Spring Security 的整条过滤器链,在**_VirtualFilterChain_**的<strong><i>doFilter()</i></strong>首行打上断点:
![image-20210811091755498](../../images/SpringSecurity/image-20210811091755498.png)
![image-20210811091755498](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/image-20210811091755498.png)
接下来**_VirtualFilterChain_**会通过**_currentPosition_**依次调用存在**_additionalFilters_**中的过滤器,其中比较重要的几个过滤器有:**_UsernamePasswordAuthenticationFilter_**、**_DefaultLoginPageGeneratingFilter_**、**_AnonymousAuthenticationFilter_**、**_ExceptionTranslationFilter_**、**_FilterSecurityInterceptor_**,我们依次在这些过滤器的<strong><i>doFilter()</i></strong>的首行打上断点:
![image-20210811091815473](../../images/SpringSecurity/image-20210811091815473.png)
![image-20210811091815473](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/image-20210811091815473.png)
准备完毕后,我们启动项目,然后访问<strong><i>http://localhost:8080/hello</i></strong>,程序首先跳转到**_DelegatingFilterProxy_**的断点上:
![image-20210811091833065](../../images/SpringSecurity/image-20210811091833065.png)
![image-20210811091833065](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/image-20210811091833065.png)
此时**_delegate_**还是 null 的,接下来依次执行代码,可以看到**_delegate_**最终被赋值一个**_FilterChainProxy_**的实例:
![img](../../images/SpringSecurity/f045b025-bd97-4222-8a02-51634be6745b.png)
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/f045b025-bd97-4222-8a02-51634be6745b.png)
接下来程序依次跳转到**_FilterChainProxy_**的<strong><i>doFilter()</i></strong>和**_VirtualFilterChain_**的<strong><i>doFilter()</i></strong>中:
![img](../../images/SpringSecurity/90d3e369-510f-45cb-982d-241d2eedb55c.png)
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/90d3e369-510f-45cb-982d-241d2eedb55c.png)
![image-20210811092048784](../../images/SpringSecurity/image-20210811092048784.png)
![image-20210811092048784](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/image-20210811092048784.png)
接着程序跳转到**_AbstractAuthenticationProcessingFilter_****_UsernamePasswordAuthenticationFilter_**的父类)的<strong><i>doFilter()</i></strong>中,通过<strong><i>requiresAuthentication()</i></strong>判定为 false是否是 POST 请求):
![img](../../images/SpringSecurity/2e5440bc-9488-4213-a030-0d25153bb2ea.png)
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/2e5440bc-9488-4213-a030-0d25153bb2ea.png)
接着程序跳转到**_DefaultLoginPageGeneratingFilter_**的<strong><i>doFilter()</i></strong>中,通过<strong><i>isLoginUrlRequest()</i></strong>判定为 false请求路径是否是<strong><i>/login</i></strong>
![img](../../images/SpringSecurity/47a7bca4-d858-4cb1-b126-347805b74053.png)
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/47a7bca4-d858-4cb1-b126-347805b74053.png)
接着程序跳转到**_AnonymousAuthenticationFilter_**的<strong><i>doFilter()</i></strong>中,由于是首次请求,此时<strong><i>SecurityContextHolder.getContext().getAuthentication()</i></strong>为 null因此会生成一个**_AnonymousAuthenticationToken_**的实例:
![img](../../images/SpringSecurity/6b1aded6-5229-47ba-b192-78a7c2622b8c.png)
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/6b1aded6-5229-47ba-b192-78a7c2622b8c.png)
接着程序跳转到**_ExceptionTranslationFilter_**的<strong><i>doFilter()</i></strong>中,**_ExceptionTranslationFilter_**负责处理**_FilterSecurityInterceptor_**抛出的异常,我们在 catch 代码块的首行打上断点:
**![img](../../images/SpringSecurity/8efa0b1c-2b32-4d5b-9655-985374326e10.png)**
**![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/8efa0b1c-2b32-4d5b-9655-985374326e10.png)**
接着程序跳转到**_FilterSecurityInterceptor_**的<strong><i>doFilter()</i></strong>中,依次执行代码后程序停留在其父类(**_AbstractSecurityInterceptor_**)的<strong><i>attemptAuthorization()</i></strong>中:
![img](../../images/SpringSecurity/d6e99143-6207-43a5-8d04-f0c81baa11b4.png)
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/d6e99143-6207-43a5-8d04-f0c81baa11b4.png)
**_accessDecisionManager_**是**_AccessDecisionManager_**(访问决策器)的实例,**_AccessDecisionManager_**主要有 3 个实现类:**_AffirmativeBased_**(一票通过)**ConsensusBased**(少数服从多数)、UnanimousBased(一票否决),此时**_AccessDecisionManager_**的的实现类是**_AffirmativeBased_**,我们可以看到程序进入**_AffirmativeBased_**的<strong><i>decide()</i></strong>中:
![img](../../images/SpringSecurity/6724647c-34ee-4a57-8cfa-b46f57400d14.png)
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/6724647c-34ee-4a57-8cfa-b46f57400d14.png)
从上图可以看出,决策的关键在<strong><i>voter.vote(authentication, object, configAttributes)</i></strong>这句代码上,通过跟踪调试,程序最终进入**_AuthenticationTrustResolverImpl_**的<strong><i>isAnonymous()</i></strong>中:
![img](../../images/SpringSecurity/4beaa02f-a93d-4d95-9ad1-0d7213cb0e46.png)
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/4beaa02f-a93d-4d95-9ad1-0d7213cb0e46.png)
<strong><i>isAssignableFrom()</i></strong>判断前者是否是后者的父类,而**_anonymousClass_**被固定为**_AnonymousAuthenticationToken.class_**,参数**_authentication_**由前面**_AnonymousAuthenticationFilter_**可以知道是**_AnonymousAuthenticationToken_**的实例,因此<strong><i>isAnonymous()</i></strong>返回 true**_FilterSecurityInterceptor_**抛出**_AccessDeniedException_**异常,程序返回**_ExceptionTranslationFilter_**的 catch 块中:
![img](../../images/SpringSecurity/8e1ac9db-5987-484d-abf4-4c6535c60cc6.png)
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/8e1ac9db-5987-484d-abf4-4c6535c60cc6.png)
接着程序会依次进入**_DelegatingAuthenticationEntryPoint_**、**_LoginUrlAuthenticationEntryPoint_**中,最后由**_LoginUrlAuthenticationEntryPoint_**的<strong><i>commence()</i></strong>决定重定向到<strong><i>/login</i></strong>
![img](../../images/SpringSecurity/1b03bdd4-6773-4b39-a664-fdf65d104403.png)
![img](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/SpringSecurity/1b03bdd4-6773-4b39-a664-fdf65d104403.png)
后续对<strong><i>/login</i></strong>的请求同样会经过之前的执行流程,在**_DefaultLoginPageGeneratingFilter_**的<strong><i>doFilter()</i></strong>中,通过<strong><i>isLoginUrlRequest()</i></strong>判定为 true请求路径是否是<strong><i>/login</i></strong>,直接返回**_login.html_**,也就是我们开头看到的登录页面。

@ -215,7 +215,7 @@ public interface ServletResponse {
其主要部分的类图 如下。
![avatar](../../images/Tomcat/Servlet主要类图.png)
![avatar](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/Tomcat/Servlet主要类图.png)
下面看一下 javax.servlet.http 包下的内容,它提供了很多 我经常用到的类和接口比如HttpServlet、HttpServletRequest、HttpServletResponse。其源码如下。

@ -128,7 +128,7 @@ public static void registerGlobalNacosProperties(AnnotationAttributes attributes
```
![image-20200821111938485](../../images/nacos/image-20200821111938485.png)
![image-20200821111938485](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/nacos/image-20200821111938485.png)
## registerNacosCommonBeans
@ -168,7 +168,7 @@ public static void registerInfrastructureBean(BeanDefinitionRegistry registry,
属性读取,从 application 配置文件中读取数据转换成 java 对象。
![image-20200821132413628](../../images/nacos/image-20200821132413628.png)
![image-20200821132413628](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/nacos/image-20200821132413628.png)
## NacosDiscoveryAutoRegister
@ -228,11 +228,11 @@ public void onApplicationEvent(WebServerInitializedEvent event) {
- 注册的参数
![image-20200821133350982](../../images/nacos/image-20200821133350982.png)
![image-20200821133350982](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/nacos/image-20200821133350982.png)
## 服务注册
![image-20200821133445090](../../images/nacos/image-20200821133445090.png)
![image-20200821133445090](https://fastly.jsdelivr.net/gh/doocs/source-code-hunter@main/images/nacos/image-20200821133445090.png)
- 注册一个实例
1. 将 instance 对象转换成 BeatInfo 对象

Loading…
Cancel
Save