Merge pull request #70 from huifer/master

spring source code
pull/74/head
AmyliaY 4 years ago committed by GitHub
commit e6c5ce2132
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -38,6 +38,8 @@
* [IoC容器 在 Web环境 中的启动](/docs/Spring/SpringMVC/IoC容器在Web环境中的启动.md) * [IoC容器 在 Web环境 中的启动](/docs/Spring/SpringMVC/IoC容器在Web环境中的启动.md)
* [SpringMVC 的设计与实现](/docs/Spring/SpringMVC/SpringMVC的设计与实现.md) * [SpringMVC 的设计与实现](/docs/Spring/SpringMVC/SpringMVC的设计与实现.md)
* [SpringMVC 跨域解析](/docs/Spring/SpringMVC/SpringMVC-CROS.md) * [SpringMVC 跨域解析](/docs/Spring/SpringMVC/SpringMVC-CROS.md)
* [Spring-MVC-HandlerMapping](/docs/Spring/mvc/Spring-MVC-HandlerMapping.md)
* [Spring-mvc-MappingRegistry](/docs/Spring/mvc/Spring-mvc-MappingRegistry.md)
### SpringJDBC ### SpringJDBC
@ -73,6 +75,14 @@
* [Spring 元数据](/docs/Spring/clazz/Spring-Metadata.md) * [Spring 元数据](/docs/Spring/clazz/Spring-Metadata.md)
* [Spring 条件接口](/docs/Spring/clazz/Spring-Conditional.md) * [Spring 条件接口](/docs/Spring/clazz/Spring-Conditional.md)
* [Spring MultiValueMap](/docs/Spring/clazz/Spring-MultiValueMap.md)
* [Spring MethodOverride](/docs/Spring/clazz/Spring-MethodOverride.md)
* [Spring BeanDefinitionReaderUtils](/docs/Spring/clazz/Spring-BeanDefinitionReaderUtils.md)
* [Spring PropertyPlaceholderHelper](/docs/Spring/clazz/Spring-PropertyPlaceholderHelper.md)
* [Spring PropertySource](/docs/Spring/clazz/PropertySource)
* [Spring PlaceholderResolver](/docs/Spring/clazz/PlaceholderResolver)
### Spring5 新特性 ### Spring5 新特性

@ -0,0 +1,26 @@
# Spring PlaceholderResolver
- 类全路径: `org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver`
- 类作用将占位符中的内容替换成属性值.
- 假设现有属性表: user.dir = c:\home
传入参数 user.dir 会获得 c:\home
```java
@FunctionalInterface
public interface PlaceholderResolver {
/**
* Resolve the supplied placeholder name to the replacement value.
* @param placeholderName the name of the placeholder to resolve
* @return the replacement value, or {@code null} if no replacement is to be made
*/
@Nullable
String resolvePlaceholder(String placeholderName);
}
```
- 类图如下
![PropertyPlaceholderConfigurerResolver](/images/spring/PropertyPlaceholderConfigurerResolver.png)

@ -0,0 +1,75 @@
# Spring PropertyPlaceholderConfigurerResolver
- 类全路径: `org.springframework.beans.factory.config.PropertyPlaceholderConfigurer.PropertyPlaceholderConfigurerResolver`
- 这个类是从 Properties 中获取属性
```java
private final class PropertyPlaceholderConfigurerResolver implements PlaceholderResolver {
private final Properties props;
private PropertyPlaceholderConfigurerResolver(Properties props) {
this.props = props;
}
@Override
@Nullable
public String resolvePlaceholder(String placeholderName) {
return PropertyPlaceholderConfigurer.this.resolvePlaceholder(placeholderName,
this.props, systemPropertiesMode);
}
}
```
- 详细方法如下
```java
@Nullable
protected String resolvePlaceholder(String placeholder, Properties props, int systemPropertiesMode) {
String propVal = null;
if (systemPropertiesMode == SYSTEM_PROPERTIES_MODE_OVERRIDE) {
propVal = resolveSystemProperty(placeholder);
}
if (propVal == null) {
propVal = resolvePlaceholder(placeholder, props);
}
if (propVal == null && systemPropertiesMode == SYSTEM_PROPERTIES_MODE_FALLBACK) {
propVal = resolveSystemProperty(placeholder);
}
return propVal;
}
```
```java
@Nullable
protected String resolvePlaceholder(String placeholder, Properties props) {
return props.getProperty(placeholder);
}
```
```java
@Nullable
protected String resolveSystemProperty(String key) {
try {
String value = System.getProperty(key);
if (value == null && this.searchSystemEnvironment) {
value = System.getenv(key);
}
return value;
}
catch (Throwable ex) {
if (logger.isDebugEnabled()) {
logger.debug("Could not access system property '" + key + "': " + ex);
}
return null;
}
}
```

@ -0,0 +1,43 @@
# Spring ServletContextPlaceholderResolver
- 类全路径: `org.springframework.web.util.ServletContextPropertyUtils.ServletContextPlaceholderResolver`
```java
private static class ServletContextPlaceholderResolver
implements PropertyPlaceholderHelper.PlaceholderResolver {
private final String text;
private final ServletContext servletContext;
public ServletContextPlaceholderResolver(String text, ServletContext servletContext) {
this.text = text;
this.servletContext = servletContext;
}
@Override
@Nullable
public String resolvePlaceholder(String placeholderName) {
try {
// servlet 上下文获取
String propVal = this.servletContext.getInitParameter(placeholderName);
if (propVal == null) {
// Fall back to system properties.
propVal = System.getProperty(placeholderName);
if (propVal == null) {
// Fall back to searching the system environment.
propVal = System.getenv(placeholderName);
}
}
return propVal;
}
catch (Throwable ex) {
System.err.println("Could not resolve placeholder '" + placeholderName + "' in [" +
this.text + "] as ServletContext init-parameter or system property: " + ex);
return null;
}
}
}
```

@ -0,0 +1,35 @@
# Spring SystemPropertyPlaceholderResolver
- 类全路径: `org.springframework.util.SystemPropertyUtils.SystemPropertyPlaceholderResolver`
```java
private static class SystemPropertyPlaceholderResolver implements PropertyPlaceholderHelper.PlaceholderResolver {
private final String text;
public SystemPropertyPlaceholderResolver(String text) {
this.text = text;
}
@Override
@Nullable
public String resolvePlaceholder(String placeholderName) {
try {
String propVal = System.getProperty(placeholderName);
if (propVal == null) {
// Fall back to searching the system environment.
// 获取系统属性
propVal = System.getenv(placeholderName);
}
return propVal;
}
catch (Throwable ex) {
System.err.println("Could not resolve placeholder '" + placeholderName + "' in [" +
this.text + "] as system property: " + ex);
return null;
}
}
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

@ -0,0 +1,127 @@
# Spring CommandLinePropertySource
- Author: [HuiFer](https://github.com/huifer)
- 源码阅读仓库: [SourceHot-spring](https://github.com/SourceHot/spring-framework-read)
- 类全路径: `org.springframework.core.env.CommandLinePropertySource`
- 作用: 用来存储命令行参数
```java
public abstract class CommandLinePropertySource<T> extends EnumerablePropertySource<T> {
public static final String COMMAND_LINE_PROPERTY_SOURCE_NAME = "commandLineArgs";
public static final String DEFAULT_NON_OPTION_ARGS_PROPERTY_NAME = "nonOptionArgs";
private String nonOptionArgsPropertyName = DEFAULT_NON_OPTION_ARGS_PROPERTY_NAME;
public CommandLinePropertySource(T source) {
// 命令行参数, 属性值
super(COMMAND_LINE_PROPERTY_SOURCE_NAME, source);
}
public CommandLinePropertySource(String name, T source) {
// 参数名称, 参数值
super(name, source);
}
public void setNonOptionArgsPropertyName(String nonOptionArgsPropertyName) {
this.nonOptionArgsPropertyName = nonOptionArgsPropertyName;
}
@Override
public final boolean containsProperty(String name) {
// 输入值是否等于nonOptionArgs
if (this.nonOptionArgsPropertyName.equals(name)) {
// 等于后判断参数列表是否为空
return !this.getNonOptionArgs().isEmpty();
}
// 是否存在 name 属性
return this.containsOption(name);
}
@Override
@Nullable
public final String getProperty(String name) {
if (this.nonOptionArgsPropertyName.equals(name)) {
// 获取 非可选项参数列表
Collection<String> nonOptionArguments = this.getNonOptionArgs();
if (nonOptionArguments.isEmpty()) {
return null;
}
else {
// 可选参数命令行参数
return StringUtils.collectionToCommaDelimitedString(nonOptionArguments);
}
}
Collection<String> optionValues = this.getOptionValues(name);
if (optionValues == null) {
return null;
}
else {
// 命令行参数
return StringUtils.collectionToCommaDelimitedString(optionValues);
}
}
/**
* 是否存在 name 的命令行参数
*/
protected abstract boolean containsOption(String name);
/**
* 获取参数列表集合
*/
@Nullable
protected abstract List<String> getOptionValues(String name);
/**
* 获取 non-option 参数列表
*/
protected abstract List<String> getNonOptionArgs();
}
```
## getOptionValues
```java
/**
* Return the collection of values associated with the command line option having the
* given name.
* <ul>
* <li>if the option is present and has no argument (e.g.: "--foo"), return an empty
* collection ({@code []})</li>
* <li>if the option is present and has a single value (e.g. "--foo=bar"), return a
* collection having one element ({@code ["bar"]})</li>
* <li>if the option is present and the underlying command line parsing library
* supports multiple arguments (e.g. "--foo=bar --foo=baz"), return a collection
* having elements for each value ({@code ["bar", "baz"]})</li>
* <li>if the option is not present, return {@code null}</li>
* </ul>
*
* 获取参数列表集合
*/
@Nullable
protected abstract List<String> getOptionValues(String name);
```
阅读注释可以知道该方法可以获取命令行参数的列表.
- 如 `--foo`作为开头当输入命令行为 `--foo=bar --foo=baz` 在输入参数名称 `foo` 会得到数据`bar,baz`

@ -0,0 +1,44 @@
# Spring ComparisonPropertySource
- Author: [HuiFer](https://github.com/huifer)
- 源码阅读仓库: [SourceHot-spring](https://github.com/SourceHot/spring-framework-read)
- 整体代码如下.
- 下面几个调用方法会直接抛出异常
1. getSource
1. containsProperty
1. getProperty
```java
static class ComparisonPropertySource extends StubPropertySource {
// 异常信息
private static final String USAGE_ERROR =
"ComparisonPropertySource instances are for use with collection comparison only";
public ComparisonPropertySource(String name) {
super(name);
}
@Override
public Object getSource() {
// 抛异常
throw new UnsupportedOperationException(USAGE_ERROR);
}
@Override
public boolean containsProperty(String name) {
// 抛异常
throw new UnsupportedOperationException(USAGE_ERROR);
}
@Override
@Nullable
public String getProperty(String name) {
// 抛异常
throw new UnsupportedOperationException(USAGE_ERROR);
}
}
```

@ -0,0 +1,105 @@
# Spring CompositePropertySource
- Author: [HuiFer](https://github.com/huifer)
- 源码阅读仓库: [SourceHot-spring](https://github.com/SourceHot/spring-framework-read)
- 全路径: `org.springframework.core.env.CompositePropertySource`
- 整体代码如下
```java
public class CompositePropertySource extends EnumerablePropertySource<Object> {
/**
* set 集合
*/
private final Set<PropertySource<?>> propertySources = new LinkedHashSet<>();
/**
* Create a new {@code CompositePropertySource}.
* @param name the name of the property source
*/
public CompositePropertySource(String name) {
super(name);
}
@Override
@Nullable
public Object getProperty(String name) {
// 循环
for (PropertySource<?> propertySource : this.propertySources) {
// 获取存储内容
Object candidate = propertySource.getProperty(name);
if (candidate != null) {
return candidate;
}
}
return null;
}
@Override
public boolean containsProperty(String name) {
for (PropertySource<?> propertySource : this.propertySources) {
// 是否存在name
if (propertySource.containsProperty(name)) {
return true;
}
}
return false;
}
@Override
public String[] getPropertyNames() {
Set<String> names = new LinkedHashSet<>();
for (PropertySource<?> propertySource : this.propertySources) {
// 类型不同抛出异常
if (!(propertySource instanceof EnumerablePropertySource)) {
throw new IllegalStateException(
"Failed to enumerate property names due to non-enumerable property source: " + propertySource);
}
// 批量添加
names.addAll(Arrays.asList(((EnumerablePropertySource<?>) propertySource).getPropertyNames()));
}
// 转换成 array
return StringUtils.toStringArray(names);
}
/**
* Add the given {@link PropertySource} to the end of the chain.
* @param propertySource the PropertySource to add
*/
public void addPropertySource(PropertySource<?> propertySource) {
this.propertySources.add(propertySource);
}
/**
* Add the given {@link PropertySource} to the start of the chain.
* @param propertySource the PropertySource to add
* @since 4.1
*/
public void addFirstPropertySource(PropertySource<?> propertySource) {
// 头插
List<PropertySource<?>> existing = new ArrayList<>(this.propertySources);
this.propertySources.clear();
this.propertySources.add(propertySource);
this.propertySources.addAll(existing);
}
/**
* Return all property sources that this composite source holds.
* @since 4.1.1
*/
public Collection<PropertySource<?>> getPropertySources() {
return this.propertySources;
}
@Override
public String toString() {
return getClass().getSimpleName() + " {name='" + this.name + "', propertySources=" + this.propertySources + "}";
}
}
```

@ -0,0 +1,46 @@
# Spring EnumerablePropertySource
- Author: [HuiFer](https://github.com/huifer)
- 源码阅读仓库: [SourceHot-spring](https://github.com/SourceHot/spring-framework-read)
- 全路径: `org.springframework.core.env.EnumerablePropertySource`
- 在这个类中定义了一个抽象方法`getPropertyNames` 用来获取所有的 property 的名称
```java
public abstract String[] getPropertyNames();
```
- 整体代码如下
```java
public abstract class EnumerablePropertySource<T> extends PropertySource<T> {
public EnumerablePropertySource(String name, T source) {
super(name, source);
}
protected EnumerablePropertySource(String name) {
super(name);
}
/**
* Return whether this {@code PropertySource} contains a property with the given name.
* <p>This implementation checks for the presence of the given name within the
* {@link #getPropertyNames()} array.
*
* 在属性列表中是否存在 properties
* @param name the name of the property to find
*/
@Override
public boolean containsProperty(String name) {
return ObjectUtils.containsElement(getPropertyNames(), name);
}
/**
* Return the names of all properties contained by the
* 获取所有的 properties 名称
* {@linkplain #getSource() source} object (never {@code null}).
*/
public abstract String[] getPropertyNames();
}
```

@ -0,0 +1,43 @@
# Spring MapPropertySource
- Author: [HuiFer](https://github.com/huifer)
- 源码阅读仓库: [SourceHot-spring](https://github.com/SourceHot/spring-framework-read)
- 类全路径: `org.springframework.core.env.MapPropertySource`
- 内部数据结构是一个`Map<String,Object>`
这是一个对map的操作.
- 整体代码如下.
```java
public class MapPropertySource extends EnumerablePropertySource<Map<String, Object>> {
public MapPropertySource(String name, Map<String, Object> source) {
super(name, source);
}
@Override
@Nullable
public Object getProperty(String name) {
// 从map中获取 name 对应的value
return this.source.get(name);
}
@Override
public boolean containsProperty(String name) {
// 判断是否存在 name 属性
return this.source.containsKey(name);
}
@Override
public String[] getPropertyNames() {
// 互殴去 map 的所有key
return StringUtils.toStringArray(this.source.keySet());
}
}
```

@ -0,0 +1,112 @@
# Spring MockPropertySource
- Author: [HuiFer](https://github.com/huifer)
- 源码阅读仓库: [SourceHot-spring](https://github.com/SourceHot/spring-framework-read)
- 内部 source 是 Properties 类型
## withProperty
- 设置属性名称和属性值
```java
public MockPropertySource withProperty(String name, Object value) {
this.setProperty(name, value);
return this;
}
```
## setProperty
```java
public void setProperty(String name, Object value) {
this.source.put(name, value);
}
```
## 完整代码
```java
public class MockPropertySource extends PropertiesPropertySource {
/**
* {@value} is the default name for {@link MockPropertySource} instances not
* otherwise given an explicit name.
* @see #MockPropertySource()
* @see #MockPropertySource(String)
*/
public static final String MOCK_PROPERTIES_PROPERTY_SOURCE_NAME = "mockProperties";
/**
* Create a new {@code MockPropertySource} named {@value #MOCK_PROPERTIES_PROPERTY_SOURCE_NAME}
* that will maintain its own internal {@link Properties} instance.
*/
public MockPropertySource() {
this(new Properties());
}
/**
* Create a new {@code MockPropertySource} with the given name that will
* maintain its own internal {@link Properties} instance.
* @param name the {@linkplain #getName() name} of the property source
*/
public MockPropertySource(String name) {
this(name, new Properties());
}
/**
* Create a new {@code MockPropertySource} named {@value #MOCK_PROPERTIES_PROPERTY_SOURCE_NAME}
* and backed by the given {@link Properties} object.
* @param properties the properties to use
*/
public MockPropertySource(Properties properties) {
this(MOCK_PROPERTIES_PROPERTY_SOURCE_NAME, properties);
}
/**
* Create a new {@code MockPropertySource} with the given name and backed by the given
* {@link Properties} object.
* @param name the {@linkplain #getName() name} of the property source
* @param properties the properties to use
*/
public MockPropertySource(String name, Properties properties) {
super(name, properties);
}
/**
* Set the given property on the underlying {@link Properties} object.
*/
public void setProperty(String name, Object value) {
// map 操作
this.source.put(name, value);
}
/**
* Convenient synonym for {@link #setProperty} that returns the current instance.
* Useful for method chaining and fluent-style use.
* 设置属性名称和属性值
* @return this {@link MockPropertySource} instance
*/
public MockPropertySource withProperty(String name, Object value) {
this.setProperty(name, value);
return this;
}
}
```

@ -0,0 +1,35 @@
# Spring PropertiesPropertySource
- Author: [HuiFer](https://github.com/huifer)
- 源码阅读仓库: [SourceHot-spring](https://github.com/SourceHot/spring-framework-read)
- 全路径: `org.springframework.core.env.PropertiesPropertySource`
- Properties 是map结构。可以做类型转换.
- getPropertyNames 就转换成了父类MapPropertySource的方法了
- map.keySet()
```java
public class PropertiesPropertySource extends MapPropertySource {
@SuppressWarnings({"rawtypes", "unchecked"})
public PropertiesPropertySource(String name, Properties source) {
super(name, (Map) source);
}
protected PropertiesPropertySource(String name, Map<String, Object> source) {
super(name, source);
}
@Override
public String[] getPropertyNames() {
synchronized (this.source) {
return super.getPropertyNames();
}
}
}
```

@ -0,0 +1,241 @@
# Spring ResourcePropertySource
- Author: [HuiFer](https://github.com/huifer)
- 源码阅读仓库: [SourceHot-spring](https://github.com/SourceHot/spring-framework-read)
- 全路径: `org.springframework.core.io.support.ResourcePropertySource`
- source 依然是map结构
## getNameForResource
```java
private static String getNameForResource(Resource resource) {
// 获取 resource 的介绍
String name = resource.getDescription();
if (!StringUtils.hasText(name)) {
// 短类名+@+hashcode
name = resource.getClass().getSimpleName() + "@" + System.identityHashCode(resource);
}
return name;
}
```
## withName
- 创建 ResourcePropertySource 对象, 根据 name 属性
```java
public ResourcePropertySource withName(String name) {
if (this.name.equals(name)) {
return this;
}
// Store the original resource name if necessary...
if (this.resourceName != null) {
if (this.resourceName.equals(name)) {
return new ResourcePropertySource(this.resourceName, null, this.source);
}
else {
return new ResourcePropertySource(name, this.resourceName, this.source);
}
}
else {
// Current name is resource name -> preserve it in the extra field...
return new ResourcePropertySource(name, this.name, this.source);
}
}
```
## 构造函数
- 通过 location 字符串读取 resource
```java
public ResourcePropertySource(String name, String location, ClassLoader classLoader) throws IOException {
// 默认资源读取器读取 location 转换成 resource
this(name, new DefaultResourceLoader(classLoader).getResource(location));
}
```
- 读取 resource 信息进行存储
```java
public ResourcePropertySource(String name, EncodedResource resource) throws IOException {
// 设置 name + map 对象
// map 对象是 资源信息
super(name, PropertiesLoaderUtils.loadProperties(resource));
// 获取 resource name
this.resourceName = getNameForResource(resource.getResource());
}
```
## 完整代码
```java
public class ResourcePropertySource extends PropertiesPropertySource {
/** The original resource name, if different from the given name. */
@Nullable
private final String resourceName;
/**
* Create a PropertySource having the given name based on Properties
* loaded from the given encoded resource.
*/
public ResourcePropertySource(String name, EncodedResource resource) throws IOException {
// 设置 name + map 对象
// map 对象是 资源信息
super(name, PropertiesLoaderUtils.loadProperties(resource));
// 获取 resource name
this.resourceName = getNameForResource(resource.getResource());
}
/**
* Create a PropertySource based on Properties loaded from the given resource.
* The name of the PropertySource will be generated based on the
* {@link Resource#getDescription() description} of the given resource.
*/
public ResourcePropertySource(EncodedResource resource) throws IOException {
// 设置 key: name, resource 的 name
// 设置 value: resource 资源信息
super(getNameForResource(resource.getResource()), PropertiesLoaderUtils.loadProperties(resource));
this.resourceName = null;
}
/**
* Create a PropertySource having the given name based on Properties
* loaded from the given encoded resource.
*/
public ResourcePropertySource(String name, Resource resource) throws IOException {
super(name, PropertiesLoaderUtils.loadProperties(new EncodedResource(resource)));
this.resourceName = getNameForResource(resource);
}
/**
* Create a PropertySource based on Properties loaded from the given resource.
* The name of the PropertySource will be generated based on the
* {@link Resource#getDescription() description} of the given resource.
*/
public ResourcePropertySource(Resource resource) throws IOException {
super(getNameForResource(resource), PropertiesLoaderUtils.loadProperties(new EncodedResource(resource)));
this.resourceName = null;
}
/**
* Create a PropertySource having the given name based on Properties loaded from
* the given resource location and using the given class loader to load the
* resource (assuming it is prefixed with {@code classpath:}).
*/
public ResourcePropertySource(String name, String location, ClassLoader classLoader) throws IOException {
// 默认资源读取器读取 location 转换成 resource
this(name, new DefaultResourceLoader(classLoader).getResource(location));
}
/**
* Create a PropertySource based on Properties loaded from the given resource
* location and use the given class loader to load the resource, assuming it is
* prefixed with {@code classpath:}. The name of the PropertySource will be
* generated based on the {@link Resource#getDescription() description} of the
* resource.
*/
public ResourcePropertySource(String location, ClassLoader classLoader) throws IOException {
this(new DefaultResourceLoader(classLoader).getResource(location));
}
/**
* Create a PropertySource having the given name based on Properties loaded from
* the given resource location. The default thread context class loader will be
* used to load the resource (assuming the location string is prefixed with
* {@code classpath:}.
*/
public ResourcePropertySource(String name, String location) throws IOException {
this(name, new DefaultResourceLoader().getResource(location));
}
/**
* Create a PropertySource based on Properties loaded from the given resource
* location. The name of the PropertySource will be generated based on the
* {@link Resource#getDescription() description} of the resource.
*/
public ResourcePropertySource(String location) throws IOException {
this(new DefaultResourceLoader().getResource(location));
}
private ResourcePropertySource(String name, @Nullable String resourceName, Map<String, Object> source) {
super(name, source);
this.resourceName = resourceName;
}
/**
* Return the description for the given Resource; if the description is
* empty, return the class name of the resource plus its identity hash code.
* @see org.springframework.core.io.Resource#getDescription()
*/
private static String getNameForResource(Resource resource) {
// 获取 resource 的介绍
String name = resource.getDescription();
if (!StringUtils.hasText(name)) {
// 短类名+@+hashcode
name = resource.getClass().getSimpleName() + "@" + System.identityHashCode(resource);
}
return name;
}
/**
* Return a potentially adapted variant of this {@link ResourcePropertySource},
* overriding the previously given (or derived) name with the specified name.
* @since 4.0.4
*/
public ResourcePropertySource withName(String name) {
if (this.name.equals(name)) {
return this;
}
// Store the original resource name if necessary...
if (this.resourceName != null) {
if (this.resourceName.equals(name)) {
return new ResourcePropertySource(this.resourceName, null, this.source);
}
else {
return new ResourcePropertySource(name, this.resourceName, this.source);
}
}
else {
// Current name is resource name -> preserve it in the extra field...
return new ResourcePropertySource(name, this.name, this.source);
}
}
/**
* Return a potentially adapted variant of this {@link ResourcePropertySource},
* overriding the previously given name (if any) with the original resource name
* (equivalent to the name generated by the name-less constructor variants).
* @since 4.1
*/
public ResourcePropertySource withResourceName() {
if (this.resourceName == null) {
return this;
}
return new ResourcePropertySource(this.resourceName, null, this.source);
}
}
```

@ -0,0 +1,36 @@
# Spring ServletConfigPropertySource
- Author: [HuiFer](https://github.com/huifer)
- 源码阅读仓库: [SourceHot-spring](https://github.com/SourceHot/spring-framework-read)
- 类全路径: `org.springframework.web.context.support.ServletConfigPropertySource`
- 内部数据结构是 `ServletConfig`
- 整体代码如下
```java
public class ServletConfigPropertySource extends EnumerablePropertySource<ServletConfig> {
public ServletConfigPropertySource(String name, ServletConfig servletConfig) {
super(name, servletConfig);
}
@Override
public String[] getPropertyNames() {
// javax.servlet.ServletConfig.getInitParameterNames
return StringUtils.toStringArray(this.source.getInitParameterNames());
}
@Override
@Nullable
public String getProperty(String name) {
// javax.servlet.ServletConfig.getInitParameter
return this.source.getInitParameter(name);
}
}
```

@ -0,0 +1,37 @@
# Spring ServletContextPropertySource
- Author: [HuiFer](https://github.com/huifer)
- 源码阅读仓库: [SourceHot-spring](https://github.com/SourceHot/spring-framework-read)
- 类全路径: `org.springframework.web.context.support.ServletContextPropertySource`
- 内部数据结构是 ServletContext 接口
- 整体代码如下.
```java
public class ServletContextPropertySource extends EnumerablePropertySource<ServletContext> {
public ServletContextPropertySource(String name, ServletContext servletContext) {
super(name, servletContext);
}
@Override
public String[] getPropertyNames() {
// javax.servlet.ServletContext.getInitParameterNames 方法调用
return StringUtils.toStringArray(this.source.getInitParameterNames());
}
@Override
@Nullable
public String getProperty(String name) {
// javax.servlet.ServletContext.getInitParameter
return this.source.getInitParameter(name);
}
}
```

@ -0,0 +1,55 @@
# Spring SimpleCommandLineArgsParser
- Author: [HuiFer](https://github.com/huifer)
- 源码阅读仓库: [SourceHot-spring](https://github.com/SourceHot/spring-framework-read)
- 类全路径: `org.springframework.core.env.SimpleCommandLineArgsParser
- 类作用: 将命令行参数解析成 `org.springframework.core.env.CommandLineArgs`
- 完整代码如下.
```java
class SimpleCommandLineArgsParser {
/**
* Parse the given {@code String} array based on the rules described {@linkplain
* SimpleCommandLineArgsParser above}, returning a fully-populated
* {@link CommandLineArgs} object.
* @param args command line arguments, typically from a {@code main()} method
*/
public CommandLineArgs parse(String... args) {
CommandLineArgs commandLineArgs = new CommandLineArgs();
for (String arg : args) {
if (arg.startsWith("--")) {
String optionText = arg.substring(2, arg.length());
String optionName;
String optionValue = null;
if (optionText.contains("=")) {
optionName = optionText.substring(0, optionText.indexOf('='));
optionValue = optionText.substring(optionText.indexOf('=') + 1, optionText.length());
}
else {
optionName = optionText;
}
if (optionName.isEmpty() || (optionValue != null && optionValue.isEmpty())) {
throw new IllegalArgumentException("Invalid argument syntax: " + arg);
}
commandLineArgs.addOptionArg(optionName, optionValue);
}
else {
commandLineArgs.addNonOptionArg(arg);
}
}
return commandLineArgs;
}
}
```
- 处理流程
1. 循环命令行参数列表
2. 去掉 `--``=` 获取参数名称和参数值放入结果集合

@ -0,0 +1,175 @@
# Spring SimpleCommandLinePropertySource
- 全路径: `org.springframework.core.env.SimpleCommandLinePropertySource`
```java
public class SimpleCommandLinePropertySource extends CommandLinePropertySource<CommandLineArgs> {}
```
- SimpleCommandLinePropertySource 的source 类型是 CommandLineArgs 具体解释请看下面分析
## CommandLineArgs
两个内部属性
```java
class CommandLineArgs {
/**
* 选项参数列表
*/
private final Map<String, List<String>> optionArgs = new HashMap<>();
/**
* 非选项参数列表
*/
private final List<String> nonOptionArgs = new ArrayList<>();
}
```
### addOptionArg
添加 选项参数
```java
public void addOptionArg(String optionName, @Nullable String optionValue) {
if (!this.optionArgs.containsKey(optionName)) {
this.optionArgs.put(optionName, new ArrayList<>());
}
if (optionValue != null) {
this.optionArgs.get(optionName).add(optionValue);
}
}
```
### getOptionNames
- 获取选项参数列表
```java
public Set<String> getOptionNames() {
return Collections.unmodifiableSet(this.optionArgs.keySet());
}
```
- 其他方法不具体描述了,各位可以查看下面的代码
```java
class CommandLineArgs {
/**
* 选项参数列表
*/
private final Map<String, List<String>> optionArgs = new HashMap<>();
/**
* 非选项参数列表
*/
private final List<String> nonOptionArgs = new ArrayList<>();
/**
* Add an option argument for the given option name and add the given value to the
* list of values associated with this option (of which there may be zero or more).
* The given value may be {@code null}, indicating that the option was specified
* without an associated value (e.g. "--foo" vs. "--foo=bar").
*
* 添加 选项参数
*/
public void addOptionArg(String optionName, @Nullable String optionValue) {
if (!this.optionArgs.containsKey(optionName)) {
this.optionArgs.put(optionName, new ArrayList<>());
}
if (optionValue != null) {
this.optionArgs.get(optionName).add(optionValue);
}
}
/**
* Return the set of all option arguments present on the command line.
* 获取选项参数列表
*/
public Set<String> getOptionNames() {
return Collections.unmodifiableSet(this.optionArgs.keySet());
}
/**
* Return whether the option with the given name was present on the command line.
*/
public boolean containsOption(String optionName) {
return this.optionArgs.containsKey(optionName);
}
/**
* Return the list of values associated with the given option. {@code null} signifies
* that the option was not present; empty list signifies that no values were associated
* with this option.
*/
@Nullable
public List<String> getOptionValues(String optionName) {
return this.optionArgs.get(optionName);
}
/**
* Add the given value to the list of non-option arguments.
*/
public void addNonOptionArg(String value) {
this.nonOptionArgs.add(value);
}
/**
* Return the list of non-option arguments specified on the command line.
*/
public List<String> getNonOptionArgs() {
return Collections.unmodifiableList(this.nonOptionArgs);
}
}
```
在了解 CommandLineArgs 类后再来看 SimpleCommandLinePropertySource 会相对容易. 内部的几个方法就是调用 CommandLineArgs 所提供的方法
```java
@Override
public String[] getPropertyNames() {
return StringUtils.toStringArray(this.source.getOptionNames());
}
@Override
protected boolean containsOption(String name) {
return this.source.containsOption(name);
}
@Override
@Nullable
protected List<String> getOptionValues(String name) {
return this.source.getOptionValues(name);
}
@Override
protected List<String> getNonOptionArgs() {
return this.source.getNonOptionArgs();
}
```

@ -0,0 +1,28 @@
# Spring StubPropertySource
- Author: [HuiFer](https://github.com/huifer)
- 源码阅读仓库: [SourceHot-spring](https://github.com/SourceHot/spring-framework-read)
- 整体代码如下.
- 通过 StubPropertySource 的 getProperty 方法永远返回null
```java
public static class StubPropertySource extends PropertySource<Object> {
public StubPropertySource(String name) {
super(name, new Object());
}
/**
* Always returns {@code null}.
*/
@Override
@Nullable
public String getProperty(String name) {
return null;
}
}
```

File diff suppressed because it is too large Load Diff

@ -0,0 +1,145 @@
# Spring BeanDefinitionReaderUtils
- Author: [HuiFer](https://github.com/huifer)
- 源码阅读仓库: [SourceHot-spring](https://github.com/SourceHot/spring-framework-read)
## createBeanDefinition
- `org.springframework.beans.factory.support.BeanDefinitionReaderUtils.createBeanDefinition`
```java
public static AbstractBeanDefinition createBeanDefinition(
@Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException {
GenericBeanDefinition bd = new GenericBeanDefinition();
// 设置 父bean
bd.setParentName(parentName);
if (className != null) {
if (classLoader != null) {
// 设置 class
// 内部是通过反射创建 class
bd.setBeanClass(ClassUtils.forName(className, classLoader));
}
else {
// 设置 class name
bd.setBeanClassName(className);
}
}
return bd;
}
```
## generateBeanName
- `org.springframework.beans.factory.support.BeanDefinitionReaderUtils.generateBeanName(org.springframework.beans.factory.config.BeanDefinition, org.springframework.beans.factory.support.BeanDefinitionRegistry, boolean)`
```java
public static String generateBeanName(
BeanDefinition definition, BeanDefinitionRegistry registry, boolean isInnerBean)
throws BeanDefinitionStoreException {
// 获取 bean class 的名称
// Class.getName()
String generatedBeanName = definition.getBeanClassName();
if (generatedBeanName == null) {
// 父类名称是否存在
if (definition.getParentName() != null) {
generatedBeanName = definition.getParentName() + "$child";
}
// 工厂 beanName 是否为空
else if (definition.getFactoryBeanName() != null) {
generatedBeanName = definition.getFactoryBeanName() + "$created";
}
}
if (!StringUtils.hasText(generatedBeanName)) {
throw new BeanDefinitionStoreException("Unnamed bean definition specifies neither " +
"'class' nor 'parent' nor 'factory-bean' - can't generate bean name");
}
String id = generatedBeanName;
if (isInnerBean) {
// Inner bean: generate identity hashcode suffix.
// 组装名称
// 生成名称 + # + 16 进制的一个字符串
id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + ObjectUtils.getIdentityHexString(definition);
}
else {
// Top-level bean: use plain class name with unique suffix if necessary.
// 唯一beanName设置
// // beanName + # + 序号
return uniqueBeanName(generatedBeanName, registry);
}
return id;
}
```
## uniqueBeanName
```java
public static String uniqueBeanName(String beanName, BeanDefinitionRegistry registry) {
String id = beanName;
int counter = -1;
// Increase counter until the id is unique.
while (counter == -1 || registry.containsBeanDefinition(id)) {
counter++;
// beanName + # + 序号
id = beanName + GENERATED_BEAN_NAME_SEPARATOR + counter;
}
return id;
}
```
## registerBeanDefinition
```java
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// Register bean definition under primary name.
// 获取 beanName
String beanName = definitionHolder.getBeanName();
// 注册bean definition
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// Register aliases for bean name, if any.
// 别名列表
String[] aliases = definitionHolder.getAliases();
// 注册别名列表
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
```
## registerWithGeneratedName
```java
public static String registerWithGeneratedName(
AbstractBeanDefinition definition, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// 生成一个 beanName
String generatedName = generateBeanName(definition, registry, false);
// 注册 bean Definition
registry.registerBeanDefinition(generatedName, definition);
return generatedName;
}
```

@ -0,0 +1,140 @@
# Spring BeanNameGenerator
- Author: [HuiFer](https://github.com/huifer)
- 源码阅读仓库: [SourceHot-spring](https://github.com/SourceHot/spring-framework-read)
- `org.springframework.beans.factory.support.BeanNameGenerator`
- 方法用来生成 beanName
```java
public interface BeanNameGenerator {
/**
* Generate a bean name for the given bean definition.
* 生成 beanName
* @param definition the bean definition to generate a name for
* @param registry the bean definition registry that the given definition
* is supposed to be registered with
* @return the generated bean name
*/
String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry);
}
```
![](/images/spring/BeanNameGenerator.png)
## DefaultBeanNameGenerator
- `org.springframework.beans.factory.support.DefaultBeanNameGenerator`
- 调用工具类方法进行生成
```JAVA
@Override
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
return BeanDefinitionReaderUtils.generateBeanName(definition, registry);
}
```
1. ClassName + # + 十六进制字符
2. parentName + $child + # + 十六进制字符
3. factoryBeanName +$created+# + 十六进制字符
4. beanName + # + 序号
```java
public static String generateBeanName(
BeanDefinition definition, BeanDefinitionRegistry registry, boolean isInnerBean)
throws BeanDefinitionStoreException {
// 获取 bean class 的名称
// Class.getName()
String generatedBeanName = definition.getBeanClassName();
if (generatedBeanName == null) {
// 父类名称是否存在
if (definition.getParentName() != null) {
generatedBeanName = definition.getParentName() + "$child";
}
// 工厂 beanName 是否为空
else if (definition.getFactoryBeanName() != null) {
generatedBeanName = definition.getFactoryBeanName() + "$created";
}
}
if (!StringUtils.hasText(generatedBeanName)) {
throw new BeanDefinitionStoreException("Unnamed bean definition specifies neither " +
"'class' nor 'parent' nor 'factory-bean' - can't generate bean name");
}
String id = generatedBeanName;
if (isInnerBean) {
// Inner bean: generate identity hashcode suffix.
// 组装名称
// 生成名称 + # + 16 进制的一个字符串
id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + ObjectUtils.getIdentityHexString(definition);
}
else {
// Top-level bean: use plain class name with unique suffix if necessary.
// 唯一beanName设置
// // beanName + # + 序号
return uniqueBeanName(generatedBeanName, registry);
}
return id;
}
```
## AnnotationBeanNameGenerator
1. 获取注解的value作为beanName
2. 类名首字母小写
```java
@Override
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
if (definition instanceof AnnotatedBeanDefinition) {
// 从注解中获取 beanName
// 获取注解的value属性值
String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
if (StringUtils.hasText(beanName)) {
// Explicit bean name found.
// 如果存在直接返回
return beanName;
}
}
// Fallback: generate a unique default bean name.
// 默认beanName
// 类名,首字母小写
return buildDefaultBeanName(definition, registry);
}
```
## FullyQualifiedAnnotationBeanNameGenerator
- 全类名
```java
@Override
protected String buildDefaultBeanName(BeanDefinition definition) {
String beanClassName = definition.getBeanClassName();
Assert.state(beanClassName != null, "No bean class name set");
return beanClassName;
}
```

@ -0,0 +1,293 @@
# Spring MethodOverride
- Author: [HuiFer](https://github.com/huifer)
- 源码阅读仓库: [SourceHot-spring](https://github.com/SourceHot/spring-framework-read)
- `org.springframework.beans.factory.support.MethodOverride`
- `org.springframework.beans.factory.support.LookupOverride`
- `org.springframework.beans.factory.support.ReplaceOverride`
- `org.springframework.beans.factory.support.MethodOverrides`
## MethodOverride
- MethodOverride 方法重载类
在`MethodOverride`定义了下面三个属性
1. 方法名称
2. 是否重载
3. 源
```java
public abstract class MethodOverride implements BeanMetadataElement {
/**
* 方法名称
*/
private final String methodName;
/**
* 是否重载
*/
private boolean overloaded = true;
/**
* 源
*/
@Nullable
private Object source;
}
```
- 定义了一个抽象方法, 交由子类实现
```java
public abstract boolean matches(Method method);
```
类图
![MethodOverride](/images/spring/MethodOverride.png)
- 在Spring中有两种可以重写的机制(XML)
1. `lookup-method` 标签
```xml
<lookup-method name="" bean=""/>
```
2. `replaced-method` 标签
```xml
<replaced-method name="" replacer=""/>
```
相对应的两个类如类图所示
## LookupOverride
- `org.springframework.beans.factory.support.LookupOverride`
- lookup-method 标签对应的实体对象
属性列表
1. beanName
2. method
```java
@Nullable
private final String beanName;
@Nullable
private Method method;
```
### matches
比较方法
1. method是否直接相等
1. method 名称是否相同
2. 是否需要重载
3. 是不是 ABSTRACT 方法
4. 参数列表长度是否等于0
```java
@Override
public boolean matches(Method method) {
if (this.method != null) {
// 通过 equals 判断
return method.equals(this.method);
}
else {
// 1. method 名称是否相同
// 2. 是否需要重载
// 3. 是不是 ABSTRACT 方法
// 4. 参数列表长度是否等于0
return (method.getName().equals(getMethodName()) && (!isOverloaded() ||
Modifier.isAbstract(method.getModifiers()) || method.getParameterCount() == 0));
}
}
```
## ReplaceOverride
- `org.springframework.beans.factory.support.ReplaceOverride`
```java
/**
* 实现 MethodReplacer 接口的bean name
* @see MethodReplacer
*/
private final String methodReplacerBeanName;
/**
* 标签 arg-type 数据
*/
private final List<String> typeIdentifiers = new LinkedList<>();
```
- 一个例子
```XML
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="apple" class="org.source.hot.spring.overview.ioc.bean.lookup.Apple">
<replaced-method replacer="methodReplacerApple" name="hello" >
<arg-type>String</arg-type>
</replaced-method>
</bean>
<bean id="methodReplacerApple" class="org.source.hot.spring.overview.ioc.bean.lookup.MethodReplacerApple">
</bean>
</beans>
```
methodReplacerBeanName 对应`org.springframework.beans.factory.support.MethodReplacer` 的实现类
typeIdentifiers 对应标签 arg-type 的属性值
构造方法
```java
public ReplaceOverride(String methodName, String methodReplacerBeanName) {
super(methodName);
Assert.notNull(methodName, "Method replacer bean name must not be null");
this.methodReplacerBeanName = methodReplacerBeanName;
}
```
methodName 通过父类进行设置
### matches
```java
@Override
public boolean matches(Method method) {
// 方法名称是否相同
if (!method.getName().equals(getMethodName())) {
return false;
}
// 是否重载
if (!isOverloaded()) {
// Not overloaded: don't worry about arg type matching...
return true;
}
// If we get here, we need to insist on precise argument matching...
// 类型标识数量是否和参数列表是否不相同
if (this.typeIdentifiers.size() != method.getParameterCount()) {
return false;
}
// 获取参数类型列表
Class<?>[] parameterTypes = method.getParameterTypes();
for (int i = 0; i < this.typeIdentifiers.size(); i++) {
String identifier = this.typeIdentifiers.get(i);
// 判断 方法参数的类型是否在类型标识列表中
if (!parameterTypes[i].getName().contains(identifier)) {
return false;
}
}
return true;
}
```
## MethodOverrides
- `org.springframework.beans.factory.support.MethodOverrides`
- 重载方法对象
- 存储所有重载的方法列表(set结构)
```java
private final Set<MethodOverride> overrides = new CopyOnWriteArraySet<>();
```
几个方法
1. 添加 MethodOverride
```java
public void addOverride(MethodOverride override) {
this.overrides.add(override);
}
public void addOverrides(@Nullable MethodOverrides other) {
if (other != null) {
this.overrides.addAll(other.overrides);
}
}
```
1. 获取 MethodOverride
```java
@Nullable
public MethodOverride getOverride(Method method) {
MethodOverride match = null;
for (MethodOverride candidate : this.overrides) {
if (candidate.matches(method)) {
match = candidate;
}
}
return match;
}
```

@ -0,0 +1,137 @@
# Spring MultiValueMap
- Author: [HuiFer](https://github.com/huifer)
- 源码阅读仓库: [SourceHot-spring](https://github.com/SourceHot/spring-framework-read)
- 类路径: `org.springframework.util.MultiValueMap`
```java
public interface MultiValueMap<K, V> extends Map<K, List<V>> {
/**
* 获取value的第一
*/
@Nullable
V getFirst(K key);
/**
* 添加元素
*/
void add(K key, @Nullable V value);
/**
* 添加所有元素
*/
void addAll(K key, List<? extends V> values);
/**
* 添加要给 {@link MultiValueMap} 对象
*/
void addAll(MultiValueMap<K, V> values);
default void addIfAbsent(K key, @Nullable V value) {
if (!containsKey(key)) {
add(key, value);
}
}
/**
* 设置数据
*/
void set(K key, @Nullable V value);
/**
* 设置一个map数据
*/
void setAll(Map<K, V> values);
/**
* 转换成 map 结构
*/
Map<K, V> toSingleValueMap();
}
```
- 但从接口定义上可以明确 value 是一个list结构
类图
![](/images/spring/MultiValueMap.png)
## LinkedMultiValueMap
```java
public class LinkedMultiValueMap<K, V> implements MultiValueMap<K, V>, Serializable, Cloneable {
@Override
@Nullable
public V getFirst(K key) {
// 获取list
List<V> values = this.targetMap.get(key);
// 获取 list 的第一个
return (values != null && !values.isEmpty() ? values.get(0) : null);
}
@Override
public void add(K key, @Nullable V value) {
// 从当前内存中获取key对应的list.
List<V> values = this.targetMap.computeIfAbsent(key, k -> new LinkedList<>());
// 将value 插入到values中
values.add(value);
}
@Override
public void addAll(K key, List<? extends V> values) {
// 从当前内存中获取key对应的list.
List<V> currentValues = this.targetMap.computeIfAbsent(key, k -> new LinkedList<>());
// 将value 插入到values中
currentValues.addAll(values);
}
@Override
public void addAll(MultiValueMap<K, V> values) {
for (Entry<K, List<V>> entry : values.entrySet()) {
addAll(entry.getKey(), entry.getValue());
}
}
@Override
public void set(K key, @Nullable V value) {
// 构造list
List<V> values = new LinkedList<>();
// 添加
values.add(value);
// 添加
this.targetMap.put(key, values);
}
@Override
public void setAll(Map<K, V> values) {
// 循环执行 set 方法
values.forEach(this::set);
}
@Override
public Map<K, V> toSingleValueMap() {
// 返回结果定义
LinkedHashMap<K, V> singleValueMap = new LinkedHashMap<>(this.targetMap.size());
// 循环
this.targetMap.forEach((key, values) -> {
if (values != null && !values.isEmpty()) {
// value 获取原来list中的第一个元素
singleValueMap.put(key, values.get(0));
}
});
return singleValueMap;
}
}
```
- 其他实现类也基本和这个类相同, 不做具体展开

@ -0,0 +1,393 @@
# Spring Property
- Author: [HuiFer](https://github.com/huifer)
- 源码阅读仓库: [SourceHot-spring](https://github.com/SourceHot/spring-framework-read)
- 相关类
- `org.springframework.beans.PropertyValues`
- `org.springframework.beans.PropertyValue`
- `org.springframework.beans.MutablePropertyValues`
- 类图如下
![images](/images/spring/PropertyValues.png)
- 在 Spring IoC 中,**非Web工程**,使用 xml 或者注解进行配置主要使用到的是 `PropertyValues` `PropertyValue` `MutablePropertyValues` 三个
其中 `PropertyValues` 是继承迭代器,具体实现在`MutablePropertyValues` 他们处理的对象是`PropertyValues`
关系就是这样.
- 开始类的解析了
## PropertyValue
- `org.springframework.beans.PropertyValue`
- 类图
![](/images/spring/PropertyValue.png)
- 这个类暂时只关注两个属性
1. name: 属性名称
2. value: 属性值
对应标签`<property name="age" value="30"/>`
属性值一一对应填入.
## MutablePropertyValues
- `org.springframework.beans.MutablePropertyValues`
- 属性
1. `propertyValueList`:属性列表, key:参数名称,value:具体数据
2. `processedProperties`: 已经处理的属性名称
3. `converted`: 是否转换
```java
public class MutablePropertyValues implements PropertyValues, Serializable {
/**
* 属性列表, key:参数名称,value:具体数据
*/
private final List<PropertyValue> propertyValueList;
/**
* 已经处理的属性名称
*/
@Nullable
private Set<String> processedProperties;
/**
* 是否转换
*/
private volatile boolean converted = false;
}
```
### 构造器
- `MutablePropertyValues` 的一个构造器. 其他构造器的方式原理实现差不多. 核心是将构造参数转换成`PropertyValue`对象在放入`propertyValueList`中
```java
public MutablePropertyValues(@Nullable PropertyValues original) {
// We can optimize this because it's all new:
// There is no replacement of existing property values.
if (original != null) {
// 从列表中获取所有可能指
PropertyValue[] pvs = original.getPropertyValues();
this.propertyValueList = new ArrayList<>(pvs.length);
for (PropertyValue pv : pvs) {
// 循环插入 property values
this.propertyValueList.add(new PropertyValue(pv));
}
}
else {
this.propertyValueList = new ArrayList<>(0);
}
}
```
### PropertyValue 的构造方法
```JAVA
public PropertyValue(PropertyValue original) {
Assert.notNull(original, "Original must not be null");
this.name = original.getName();
this.value = original.getValue();
this.optional = original.isOptional();
this.converted = original.converted;
this.convertedValue = original.convertedValue;
this.conversionNecessary = original.conversionNecessary;
this.resolvedTokens = original.resolvedTokens;
setSource(original.getSource());
copyAttributesFrom(original);
}
```
- 除了最后一行是一个复杂调用. 前面几行代码都是属性赋值操作.
- 最后一行代码会调用`AttributeAccessor`接口上的方法.
## AttributeAccessor
- `org.springframework.core.AttributeAccessor`
- 完整的方法列表及作用注释
```java
public interface AttributeAccessor {
/**
* 设置属性值
* @param name 属性值名称
* @param value 属性值
*/
void setAttribute(String name, @Nullable Object value);
/**
* 通过属性名称获取属性值
*
* @param name 属性值名称
* @return 属性值
*/
@Nullable
Object getAttribute(String name);
/**
* 移除指定属性名称的值,返回移除的属性值
*
* @param name 属性值名称
* @return 移除的属性值
*/
@Nullable
Object removeAttribute(String name);
/**
* 是否包含属性名称
* @param 属性名称
*/
boolean hasAttribute(String name);
/**
* 属性名称列表
*/
String[] attributeNames();
}
```
- 回到`org.springframework.core.AttributeAccessorSupport#copyAttributesFrom`方法
```java
protected void copyAttributesFrom(AttributeAccessor source) {
Assert.notNull(source, "Source must not be null");
// 获取属性名称列表
String[] attributeNames = source.attributeNames();
// 循环属性名称列表
for (String attributeName : attributeNames) {
// 设置属性
// name: 属性名称,value: 从入参中获取属性名称对应的属性值
setAttribute(attributeName, source.getAttribute(attributeName));
}
}
```
### setAttribute
- 一个map操作
```java
@Override
public void setAttribute(String name, @Nullable Object value) {
Assert.notNull(name, "Name must not be null");
if (value != null) {
this.attributes.put(name, value);
}
else {
removeAttribute(name);
}
}
```
## addPropertyValue
- `org.springframework.beans.MutablePropertyValues#addPropertyValue(org.springframework.beans.PropertyValue)`
```java
public MutablePropertyValues addPropertyValue(PropertyValue pv) {
// 循环获取 属性对象
for (int i = 0; i < this.propertyValueList.size(); i++) {
// 正在处理的 属性对象
PropertyValue currentPv = this.propertyValueList.get(i);
// 正在处理的属性对象名称和添加的属性对象名称比较
// 如果相同会做一个合并操作
if (currentPv.getName().equals(pv.getName())) {
// 合并属性
pv = mergeIfRequired(pv, currentPv);
// 重新设置
setPropertyValueAt(pv, i);
return this;
}
}
// 放入 list 集合
this.propertyValueList.add(pv);
return this;
}
```
## mergeIfRequired
- `org.springframework.beans.MutablePropertyValues#mergeIfRequired`
- 这段代码会取舍新老数据.
1. 如果是`Mergeable`类型会做合并操作
2. 直接返回新数据
```java
private PropertyValue mergeIfRequired(PropertyValue newPv, PropertyValue currentPv) {
Object value = newPv.getValue();
if (value instanceof Mergeable) {
Mergeable mergeable = (Mergeable) value;
if (mergeable.isMergeEnabled()) {
// 获取合并的结果,放入对象
Object merged = mergeable.merge(currentPv.getValue());
// 创建新的 属性对象
return new PropertyValue(newPv.getName(), merged);
}
}
return newPv;
}
```
- 配合测试代码,跟容易看懂.
```java
@Test
public void testAddOrOverride() {
MutablePropertyValues pvs = new MutablePropertyValues();
pvs.addPropertyValue(new PropertyValue("forname", "Tony"));
pvs.addPropertyValue(new PropertyValue("surname", "Blair"));
pvs.addPropertyValue(new PropertyValue("age", "50"));
doTestTony(pvs);
PropertyValue addedPv = new PropertyValue("rod", "Rod");
pvs.addPropertyValue(addedPv);
assertThat(pvs.getPropertyValue("rod").equals(addedPv)).isTrue();
PropertyValue changedPv = new PropertyValue("forname", "Greg");
pvs.addPropertyValue(changedPv);
assertThat(pvs.getPropertyValue("forname").equals(changedPv)).isTrue();
}
```
## Mergeable
新的接口`Mergeable`
- `org.springframework.beans.Mergeable`
```java
public interface Mergeable {
/**
* 是否需要合并
*/
boolean isMergeEnabled();
/**
* 合并方法
*/
Object merge(@Nullable Object parent);
}
```
![](/images/spring/Mergeable.png)
- 看一下 List 怎么实现`merge`
```java
@Override
@SuppressWarnings("unchecked")
public List<E> merge(@Nullable Object parent) {
if (!this.mergeEnabled) {
throw new IllegalStateException("Not allowed to merge when the 'mergeEnabled' property is set to 'false'");
}
if (parent == null) {
return this;
}
if (!(parent instanceof List)) {
throw new IllegalArgumentException("Cannot merge with object of type [" + parent.getClass() + "]");
}
List<E> merged = new ManagedList<>();
merged.addAll((List<E>) parent);
merged.addAll(this);
return merged;
}
```
- 在 list 视线中就是讲两个结果合并. 事实上其他的几个都是这个操作. 这里就不贴所有的代码了

@ -0,0 +1,154 @@
# Spring PropertyPlaceholderHelper
- 类全路径: `org.springframework.util.PropertyPlaceholderHelper`
## parseStringValue
- `org.springframework.util.PropertyPlaceholderHelper#parseStringValue` 这个方法是主要方法
```java
protected String parseStringValue(
String value, PlaceholderResolver placeholderResolver, @Nullable Set<String> visitedPlaceholders) {
// 占位符所在位置
int startIndex = value.indexOf(this.placeholderPrefix);
if (startIndex == -1) {
return value;
}
// 返回值
StringBuilder result = new StringBuilder(value);
while (startIndex != -1) {
// 寻找结尾占位符
int endIndex = findPlaceholderEndIndex(result, startIndex);
if (endIndex != -1) {
// 返回值切分留下中间内容
String placeholder = result.substring(startIndex + this.placeholderPrefix.length(), endIndex);
String originalPlaceholder = placeholder;
if (visitedPlaceholders == null) {
visitedPlaceholders = new HashSet<>(4);
}
if (!visitedPlaceholders.add(originalPlaceholder)) {
throw new IllegalArgumentException(
"Circular placeholder reference '" + originalPlaceholder + "' in property definitions");
}
// Recursive invocation, parsing placeholders contained in the placeholder key.
// 递归获取占位符内容
placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);
// Now obtain the value for the fully resolved key...
// 解析占位符内容获得真正的属性值
String propVal = placeholderResolver.resolvePlaceholder(placeholder);
if (propVal == null && this.valueSeparator != null) {
int separatorIndex = placeholder.indexOf(this.valueSeparator);
if (separatorIndex != -1) {
String actualPlaceholder = placeholder.substring(0, separatorIndex);
String defaultValue = placeholder.substring(separatorIndex + this.valueSeparator.length());
propVal = placeholderResolver.resolvePlaceholder(actualPlaceholder);
if (propVal == null) {
propVal = defaultValue;
}
}
}
if (propVal != null) {
// Recursive invocation, parsing placeholders contained in the
// previously resolved placeholder value.
propVal = parseStringValue(propVal, placeholderResolver, visitedPlaceholders);
result.replace(startIndex, endIndex + this.placeholderSuffix.length(), propVal);
if (logger.isTraceEnabled()) {
logger.trace("Resolved placeholder '" + placeholder + "'");
}
startIndex = result.indexOf(this.placeholderPrefix, startIndex + propVal.length());
}
else if (this.ignoreUnresolvablePlaceholders) {
// Proceed with unprocessed value.
startIndex = result.indexOf(this.placeholderPrefix, endIndex + this.placeholderSuffix.length());
}
else {
throw new IllegalArgumentException("Could not resolve placeholder '" +
placeholder + "'" + " in value \"" + value + "\"");
}
visitedPlaceholders.remove(originalPlaceholder);
}
else {
startIndex = -1;
}
}
return result.toString();
}
```
在这里还需要关注一个接口
- 占位符解析.
```java
@FunctionalInterface
public interface PlaceholderResolver {
/**
* Resolve the supplied placeholder name to the replacement value.
* @param placeholderName the name of the placeholder to resolve
* @return the replacement value, or {@code null} if no replacement is to be made
*/
@Nullable
String resolvePlaceholder(String placeholderName);
}
```
占位符解析请查看: [PlaceholderResolver](PlaceholderResolver)
## findPlaceholderEndIndex
- 寻找结尾占位符索引
```java
/**
* 寻找结尾占位符索引
*/
private int findPlaceholderEndIndex(CharSequence buf, int startIndex) {
int index = startIndex + this.placeholderPrefix.length();
int withinNestedPlaceholder = 0;
while (index < buf.length()) {
if (StringUtils.substringMatch(buf, index, this.placeholderSuffix)) {
if (withinNestedPlaceholder > 0) {
withinNestedPlaceholder--;
index = index + this.placeholderSuffix.length();
}
else {
return index;
}
}
else if (StringUtils.substringMatch(buf, index, this.simplePrefix)) {
withinNestedPlaceholder++;
index = index + this.simplePrefix.length();
}
else {
index++;
}
}
return -1;
}
```

@ -0,0 +1,509 @@
# Spring PropertySources
- Author: [HuiFer](https://github.com/huifer)
- 源码阅读仓库: [SourceHot-spring](https://github.com/SourceHot/spring-framework-read)
## MutablePropertySources
- 全路径: `org.springframework.core.env.MutablePropertySources`
- `MutablePropertySources`类内部存储了`List<PropertySource<?>>`对象,主要是针对`List<PropertySource<?>>` 进行的操作.换句话说就是对 list 操作的实现
- 类注解如下
```java
public class MutablePropertySources implements PropertySources {
private final List<PropertySource<?>> propertySourceList = new CopyOnWriteArrayList<>();
/**
* Create a new {@link MutablePropertySources} object.
*
* 构造方法
*/
public MutablePropertySources() {
}
/**
* Create a new {@code MutablePropertySources} from the given propertySources
* object, preserving the original order of contained {@code PropertySource} objects.
* 构造方法, 传递一个集合, 将集合中的数据放入 {@code propertySourceList}.
*/
public MutablePropertySources(PropertySources propertySources) {
this();
// PropertySources 是一个迭代器接口的实现,通过循环取出信息放入到 propertySourceList 中
for (PropertySource<?> propertySource : propertySources) {
// 放入方法
addLast(propertySource);
}
}
/**
* 获取迭代器对象
*/
@Override
public Iterator<PropertySource<?>> iterator() {
return this.propertySourceList.iterator();
}
/**
* 获取 Spliterator 对象
*/
@Override
public Spliterator<PropertySource<?>> spliterator() {
return Spliterators.spliterator(this.propertySourceList, 0);
}
/**
* 获取流
*/
@Override
public Stream<PropertySource<?>> stream() {
return this.propertySourceList.stream();
}
/**
* 判断是否存在 name
* @param name the {@linkplain PropertySource#getName() name of the property source} to find
*/
@Override
public boolean contains(String name) {
return this.propertySourceList.contains(PropertySource.named(name));
}
/**
* 获取 PropertySource 信息
* @param name the {@linkplain PropertySource#getName() name of the property source} to find
* @return
*/
@Override
@Nullable
public PropertySource<?> get(String name) {
// 获取 name 所在的索引位置
int index = this.propertySourceList.indexOf(PropertySource.named(name));
// get方法获取结果
return (index != -1 ? this.propertySourceList.get(index) : null);
}
/**
* Add the given property source object with highest precedence.
*
* 头插数据
*/
public void addFirst(PropertySource<?> propertySource) {
removeIfPresent(propertySource);
this.propertySourceList.add(0, propertySource);
}
/**
* Add the given property source object with lowest precedence.
*
* 尾插数据
*/
public void addLast(PropertySource<?> propertySource) {
removeIfPresent(propertySource);
this.propertySourceList.add(propertySource);
}
/**
* Add the given property source object with precedence immediately higher
* than the named relative property source.
*
* 在relativePropertySourceName的索引位置前添加数据
*/
public void addBefore(String relativePropertySourceName, PropertySource<?> propertySource) {
assertLegalRelativeAddition(relativePropertySourceName, propertySource);
removeIfPresent(propertySource);
int index = assertPresentAndGetIndex(relativePropertySourceName);
addAtIndex(index, propertySource);
}
/**
* Add the given property source object with precedence immediately lower
* than the named relative property source.
* 在relativePropertySourceName的索引位置后添加数据
*/
public void addAfter(String relativePropertySourceName, PropertySource<?> propertySource) {
assertLegalRelativeAddition(relativePropertySourceName, propertySource);
// 删除存在的数据
removeIfPresent(propertySource);
// 获取所有
int index = assertPresentAndGetIndex(relativePropertySourceName);
// 在索引+1出添加数据
addAtIndex(index + 1, propertySource);
}
/**
* Return the precedence of the given property source, {@code -1} if not found.
* 获取索引位置
*/
public int precedenceOf(PropertySource<?> propertySource) {
return this.propertySourceList.indexOf(propertySource);
}
/**
* Remove and return the property source with the given name, {@code null} if not found.
* 删除索引位置
* @param name the name of the property source to find and remove
*/
@Nullable
public PropertySource<?> remove(String name) {
// 获取索引
int index = this.propertySourceList.indexOf(PropertySource.named(name));
// 删除索引上的数据
return (index != -1 ? this.propertySourceList.remove(index) : null);
}
/**
* Replace the property source with the given name with the given property source object.
* 替换 name 的信息
* @param name the name of the property source to find and replace
* @param propertySource the replacement property source
* @throws IllegalArgumentException if no property source with the given name is present
* @see #contains
*/
public void replace(String name, PropertySource<?> propertySource) {
// 获取索引位置
int index = assertPresentAndGetIndex(name);
// 设置具体所应位置的值
this.propertySourceList.set(index, propertySource);
}
/**
* Return the number of {@link PropertySource} objects contained.
* 数量
*/
public int size() {
return this.propertySourceList.size();
}
@Override
public String toString() {
return this.propertySourceList.toString();
}
/**
* Ensure that the given property source is not being added relative to itself.
* 确保两个 PropertySource 的 name不相同
*/
protected void assertLegalRelativeAddition(String relativePropertySourceName, PropertySource<?> propertySource) {
// 获取 PropertySource 的名字
String newPropertySourceName = propertySource.getName();
// 历史名字和新的名字是否相同
if (relativePropertySourceName.equals(newPropertySourceName)) {
throw new IllegalArgumentException(
"PropertySource named '" + newPropertySourceName + "' cannot be added relative to itself");
}
}
/**
* Remove the given property source if it is present.
* 删除已存在的数据
*/
protected void removeIfPresent(PropertySource<?> propertySource) {
this.propertySourceList.remove(propertySource);
}
/**
* Add the given property source at a particular index in the list.
* 指定索引位置插入数据
*/
private void addAtIndex(int index, PropertySource<?> propertySource) {
removeIfPresent(propertySource);
this.propertySourceList.add(index, propertySource);
}
/**
* Assert that the named property source is present and return its index.
* 获取 name 所在的索引位置
* @param name {@linkplain PropertySource#getName() name of the property source} to find
* @throws IllegalArgumentException if the named property source is not present
*/
private int assertPresentAndGetIndex(String name) {
int index = this.propertySourceList.indexOf(PropertySource.named(name));
if (index == -1) {
throw new IllegalArgumentException("PropertySource named '" + name + "' does not exist");
}
return index;
}
}
```
## PropertySources
- 类路径: `org.springframework.core.env.PropertySources`
- 详细说明如下
```java
public interface PropertySources extends Iterable<PropertySource<?>> {
/**
* Return a sequential {@link Stream} containing the property sources.
* 获取流
* @since 5.1
*/
default Stream<PropertySource<?>> stream() {
return StreamSupport.stream(spliterator(), false);
}
/**
* Return whether a property source with the given name is contained.
* 判断是否存在 name
* @param name the {@linkplain PropertySource#getName() name of the property source} to find
*/
boolean contains(String name);
/**
* Return the property source with the given name, {@code null} if not found.
* 获取 PropertySource
* @param name the {@linkplain PropertySource#getName() name of the property source} to find
*/
@Nullable
PropertySource<?> get(String name);
}
```
## PropertySource
- 类路径: `org.springframework.core.env.PropertySource`
- 存有两个子类
1. StubPropertySource
2. ComparisonPropertySource
3. 调用`getSource`、`containsProperty`、`getProperty` 都会直接异常
```java
public abstract class PropertySource<T> {
protected final Log logger = LogFactory.getLog(getClass());
/**
* 属性名称
*/
protected final String name;
/**
* 值
*/
protected final T source;
/**
* Create a new {@code PropertySource} with the given name and source object.
*/
public PropertySource(String name, T source) {
Assert.hasText(name, "Property source name must contain at least one character");
Assert.notNull(source, "Property source must not be null");
this.name = name;
this.source = source;
}
/**
* Create a new {@code PropertySource} with the given name and with a new
* {@code Object} instance as the underlying source.
* <p>Often useful in testing scenarios when creating anonymous implementations
* that never query an actual source but rather return hard-coded values.
*/
@SuppressWarnings("unchecked")
public PropertySource(String name) {
this(name, (T) new Object());
}
/**
* Return a {@code PropertySource} implementation intended for collection comparison purposes only.
* <p>Primarily for internal use, but given a collection of {@code PropertySource} objects, may be
* used as follows:
* <pre class="code">
* {@code List<PropertySource<?>> sources = new ArrayList<PropertySource<?>>();
* sources.add(new MapPropertySource("sourceA", mapA));
* sources.add(new MapPropertySource("sourceB", mapB));
* assert sources.contains(PropertySource.named("sourceA"));
* assert sources.contains(PropertySource.named("sourceB"));
* assert !sources.contains(PropertySource.named("sourceC"));
* }</pre>
* The returned {@code PropertySource} will throw {@code UnsupportedOperationException}
* if any methods other than {@code equals(Object)}, {@code hashCode()}, and {@code toString()}
* are called.
* @param name the name of the comparison {@code PropertySource} to be created and returned.
*/
public static PropertySource<?> named(String name) {
return new ComparisonPropertySource(name);
}
/**
* Return the name of this {@code PropertySource}.
*/
public String getName() {
return this.name;
}
/**
* Return the underlying source object for this {@code PropertySource}.
*/
public T getSource() {
return this.source;
}
/**
* Return whether this {@code PropertySource} contains the given name.
* <p>This implementation simply checks for a {@code null} return value
* from {@link #getProperty(String)}. Subclasses may wish to implement
* a more efficient algorithm if possible.
* @param name the property name to find
*/
public boolean containsProperty(String name) {
// getProperty 抽象方法子类实现
return (getProperty(name) != null);
}
/**
* Return the value associated with the given name,
* or {@code null} if not found.
* // getProperty 抽象方法子类实现
* @param name the property to find
* @see PropertyResolver#getRequiredProperty(String)
*/
@Nullable
public abstract Object getProperty(String name);
/**
* This {@code PropertySource} object is equal to the given object if:
* <ul>
* <li>they are the same instance
* <li>the {@code name} properties for both objects are equal
* </ul>
* <p>No properties other than {@code name} are evaluated.
*/
@Override
public boolean equals(@Nullable Object other) {
return (this == other || (other instanceof PropertySource &&
ObjectUtils.nullSafeEquals(this.name, ((PropertySource<?>) other).name)));
}
/**
* Return a hash code derived from the {@code name} property
* of this {@code PropertySource} object.
*/
@Override
public int hashCode() {
return ObjectUtils.nullSafeHashCode(this.name);
}
/**
* Produce concise output (type and name) if the current log level does not include
* debug. If debug is enabled, produce verbose output including the hash code of the
* PropertySource instance and every name/value property pair.
* <p>This variable verbosity is useful as a property source such as system properties
* or environment variables may contain an arbitrary number of property pairs,
* potentially leading to difficult to read exception and log messages.
* @see Log#isDebugEnabled()
*/
@Override
public String toString() {
if (logger.isDebugEnabled()) {
return getClass().getSimpleName() + "@" + System.identityHashCode(this) +
" {name='" + this.name + "', properties=" + this.source + "}";
}
else {
return getClass().getSimpleName() + " {name='" + this.name + "'}";
}
}
/**
* {@code PropertySource} to be used as a placeholder in cases where an actual
* property source cannot be eagerly initialized at application context
* creation time. For example, a {@code ServletContext}-based property source
* must wait until the {@code ServletContext} object is available to its enclosing
* {@code ApplicationContext}. In such cases, a stub should be used to hold the
* intended default position/order of the property source, then be replaced
* during context refresh.
* @see org.springframework.context.support.AbstractApplicationContext#initPropertySources()
* @see org.springframework.web.context.support.StandardServletEnvironment
* @see org.springframework.web.context.support.ServletContextPropertySource
*/
public static class StubPropertySource extends PropertySource<Object> {
public StubPropertySource(String name) {
super(name, new Object());
}
/**
* Always returns {@code null}.
*/
@Override
@Nullable
public String getProperty(String name) {
return null;
}
}
/**
* A {@code PropertySource} implementation intended for collection comparison
* purposes.
*
* @see PropertySource#named(String)
*/
static class ComparisonPropertySource extends StubPropertySource {
// 异常信息
private static final String USAGE_ERROR =
"ComparisonPropertySource instances are for use with collection comparison only";
public ComparisonPropertySource(String name) {
super(name);
}
@Override
public Object getSource() {
// 抛异常
throw new UnsupportedOperationException(USAGE_ERROR);
}
@Override
public boolean containsProperty(String name) {
// 抛异常
throw new UnsupportedOperationException(USAGE_ERROR);
}
@Override
@Nullable
public String getProperty(String name) {
// 抛异常
throw new UnsupportedOperationException(USAGE_ERROR);
}
}
}
```
类图
![PropertySource.png](/images/spring/PropertySource.png)

@ -0,0 +1,90 @@
# Spring SystemPropertyUtils
- spring 中获取系统属性的工具类
- 内部属性
```java
/**
*
* Prefix for system property placeholders: "${".
* 前缀占位符
* */
public static final String PLACEHOLDER_PREFIX = "${";
/**
* Suffix for system property placeholders: "}".
* 后缀占位符
* */
public static final String PLACEHOLDER_SUFFIX = "}";
/**
* Value separator for system property placeholders: ":".
* 值分割符号
* */
public static final String VALUE_SEPARATOR = ":";
/**
* 占位符解析类
*/
private static final PropertyPlaceholderHelper strictHelper =
new PropertyPlaceholderHelper(PLACEHOLDER_PREFIX, PLACEHOLDER_SUFFIX, VALUE_SEPARATOR, false);
/**
* 占位符解析类
*/
private static final PropertyPlaceholderHelper nonStrictHelper =
new PropertyPlaceholderHelper(PLACEHOLDER_PREFIX, PLACEHOLDER_SUFFIX, VALUE_SEPARATOR, true);
```
## resolvePlaceholders
- 解析属性
![SystemPropertyUtils-resolvePlaceholders.png](/images/spring/SystemPropertyUtils-resolvePlaceholders.png)
时序图因为有递归所以看着有点长, 其核心方法最后会指向 PlaceholderResolver
通过 PlaceholderResolver 获取属性值
`SystemPropertyUtils` 内部有 `PlaceholderResolver ` 实现
- 最终通过下面的类来获取具体的属性值
```java
private static class SystemPropertyPlaceholderResolver implements PropertyPlaceholderHelper.PlaceholderResolver {
private final String text;
public SystemPropertyPlaceholderResolver(String text) {
this.text = text;
}
@Override
@Nullable
public String resolvePlaceholder(String placeholderName) {
try {
String propVal = System.getProperty(placeholderName);
if (propVal == null) {
// Fall back to searching the system environment.
// 获取系统属性
propVal = System.getenv(placeholderName);
}
return propVal;
}
catch (Throwable ex) {
System.err.println("Could not resolve placeholder '" + placeholderName + "' in [" +
this.text + "] as system property: " + ex);
return null;
}
}
}
```

@ -0,0 +1,792 @@
# Spring HandlerMapping
- Author: [HuiFer](https://github.com/huifer)
- 源码阅读仓库: [SourceHot-spring](https://github.com/SourceHot/spring-framework-read)
- 源码路径: `org.springframework.jms.annotation.EnableJms`
- `org.springframework.web.servlet.HandlerMapping`
- HandlerMapping 处理映射关系, 通过请求转换成对象`HandlerExecutionChain`
```java
public interface HandlerMapping {
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
// 其他静态变量省略
}
```
![image](/images/springMVC/HandlerMapping.png)
```java
@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// 转换成handler
Object handler = getHandlerInternal(request);
if (handler == null) {
// 获取默认的 handler
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// Bean name or resolved handler?
if (handler instanceof String) {
// handler 是beanName 直接从容器中获取
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (logger.isTraceEnabled()) {
logger.trace("Mapped to " + handler);
}
else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
logger.debug("Mapped to " + executionChain.getHandler());
}
if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
CorsConfiguration config = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(request) : null);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
config = (config != null ? config.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
```
- `getHandlerInternal`方法是一个抽象方法
```JAVA
@Nullable
protected abstract Object getHandlerInternal(HttpServletRequest request) throws Exception;
```
存在的实现方法
![image-20200915135933146](images/image-20200915135933146.png)
- 先看`org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#getHandlerInternal`方法是怎么一回事.
```java
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
// 获取当前请求路径
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
// 设置属性
request.setAttribute(LOOKUP_PATH, lookupPath);
// 上锁
this.mappingRegistry.acquireReadLock();
try {
// 寻找 handler method
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
// 释放锁
this.mappingRegistry.releaseReadLock();
}
}
```
## UrlPathHelper
- 全路径:`org.springframework.web.util.UrlPathHelper`
- 几个属性
```java
/**
* 是否全路径标记
*/
private boolean alwaysUseFullPath = false;
/**
* 是否需要 decode
*/
private boolean urlDecode = true;
private boolean removeSemicolonContent = true;
/**
* 默认的encoding编码格式
*/
private String defaultEncoding = WebUtils.DEFAULT_CHARACTER_ENCODING;
```
### getPathWithinApplication
```java
public String getPathWithinApplication(HttpServletRequest request) {
// 获取 context path
String contextPath = getContextPath(request);
// 获取 uri
String requestUri = getRequestUri(request);
String path = getRemainingPath(requestUri, contextPath, true);
if (path != null) {
// Normal case: URI contains context path.
return (StringUtils.hasText(path) ? path : "/");
}
else {
return requestUri;
}
}
```
1. 从 request 中获取 context-path
1. 从属性中直接获取
2. 从request中调用 getContextPath 获取
3. 判断是否是**`/`**
4. decode request string
2. 从 request 中虎丘 request-uri
1. 从属性中获取
2. 从 request 中调用 getRequestURI 获取
3. decode
3. 获取剩余路径
### getContextPath
- 获取 context-path 地址
```java
public String getContextPath(HttpServletRequest request) {
// 从 request 获取 context path
String contextPath = (String) request.getAttribute(WebUtils.INCLUDE_CONTEXT_PATH_ATTRIBUTE);
if (contextPath == null) {
contextPath = request.getContextPath();
}
if ("/".equals(contextPath)) {
// Invalid case, but happens for includes on Jetty: silently adapt it.
contextPath = "";
}
// decode context path
return decodeRequestString(request, contextPath);
}
```
### decodeRequestString
- 判断是否需要编码, 需要编码就做编码操作,不需要就直接返回
```java
public String decodeRequestString(HttpServletRequest request, String source) {
// 判断是否需要编码
if (this.urlDecode) {
// 进行编码
return decodeInternal(request, source);
}
return source;
}
```
### decodeInternal
- 编码方法
```java
@SuppressWarnings("deprecation")
private String decodeInternal(HttpServletRequest request, String source) {
// 确定编码方式
String enc = determineEncoding(request);
try {
// 将 source 编译成 enc 的编码方式
return UriUtils.decode(source, enc);
}
catch (UnsupportedCharsetException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Could not decode request string [" + source + "] with encoding '" + enc +
"': falling back to platform default encoding; exception message: " + ex.getMessage());
}
// 直接编码,JDK底层编码
return URLDecoder.decode(source);
}
}
```
### determineEncoding
- 确认编码
```java
protected String determineEncoding(HttpServletRequest request) {
// 从 request 中获取编码方式
String enc = request.getCharacterEncoding();
if (enc == null) {
// 默认编码
enc = getDefaultEncoding();
}
return enc;
}
```
### getRequestUri
- 获取 uri 地址
```java
public String getRequestUri(HttpServletRequest request) {
// 从属性中获取
String uri = (String) request.getAttribute(WebUtils.INCLUDE_REQUEST_URI_ATTRIBUTE);
if (uri == null) {
// 调用方法获取
uri = request.getRequestURI();
}
//编码和清理数据
return decodeAndCleanUriString(request, uri);
}
```
### decodeAndCleanUriString
- 编码和清理数据
```java
private String decodeAndCleanUriString(HttpServletRequest request, String uri) {
// 去掉分号
uri = removeSemicolonContent(uri);
// decoding
uri = decodeRequestString(request, uri);
// 去掉 // 双斜杠
uri = getSanitizedPath(uri);
return uri;
}
```
### shouldRemoveTrailingServletPathSlash
- 是否删除 servlet path 后的斜杠
- 默认是 false .
- 代码流程
1. 通过 classLoader 加载 `"com.ibm.ws.webcontainer.WebContainer"`
2. 调用方法 `"getWebContainerProperties"`
3. 从方法结果中取`"getWebContainerProperties"`
```java
private boolean shouldRemoveTrailingServletPathSlash(HttpServletRequest request) {
if (request.getAttribute(WEBSPHERE_URI_ATTRIBUTE) == null) {
// Regular servlet container: behaves as expected in any case,
// so the trailing slash is the result of a "/" url-pattern mapping.
// Don't remove that slash.
return false;
}
Boolean flagToUse = websphereComplianceFlag;
if (flagToUse == null) {
ClassLoader classLoader = UrlPathHelper.class.getClassLoader();
String className = "com.ibm.ws.webcontainer.WebContainer";
String methodName = "getWebContainerProperties";
String propName = "com.ibm.ws.webcontainer.removetrailingservletpathslash";
boolean flag = false;
try {
Class<?> cl = classLoader.loadClass(className);
Properties prop = (Properties) cl.getMethod(methodName).invoke(null);
flag = Boolean.parseBoolean(prop.getProperty(propName));
}
catch (Throwable ex) {
if (logger.isDebugEnabled()) {
logger.debug("Could not introspect WebSphere web container properties: " + ex);
}
}
flagToUse = flag;
websphereComplianceFlag = flag;
}
// Don't bother if WebSphere is configured to be fully Servlet compliant.
// However, if it is not compliant, do remove the improper trailing slash!
return !flagToUse;
}
```
### decodeMatrixVariables
- 编码修改方法
```java
public MultiValueMap<String, String> decodeMatrixVariables(
HttpServletRequest request, MultiValueMap<String, String> vars) {
// 判断是否需要重写编码
if (this.urlDecode) {
return vars;
}
else {
// 需要重写编码的情况
MultiValueMap<String, String> decodedVars = new LinkedMultiValueMap<>(vars.size());
// 循环, 将 value 调用decodeInternal写到结果map返回
vars.forEach((key, values) -> {
for (String value : values) {
decodedVars.add(key, decodeInternal(request, value));
}
});
return decodedVars;
}
}
```
- 与这个方法对应的还有`decodePathVariables`
### decodePathVariables
```java
public Map<String, String> decodePathVariables(HttpServletRequest request, Map<String, String> vars) {
// 判断是否需要重写编码
if (this.urlDecode) {
return vars;
}
else {
Map<String, String> decodedVars = new LinkedHashMap<>(vars.size());
// 虚幻 decoding
vars.forEach((key, value) -> decodedVars.put(key, decodeInternal(request, value)));
return decodedVars;
}
}
```
- 回到`org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#getHandlerInternal`
```JAVA
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
```
- 设置属性上锁开锁就不具体展开了.
## lookupHandlerMethod
- `org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#lookupHandlerMethod` 方法
- 第一部分
```java
@Nullable
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<>();
// 从 MultiValueMap 获取
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
// 如果不为空
if (directPathMatches != null) {
// 添加匹配映射
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
// No choice but to go through all mappings...
// 添加匹配映射
addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
}
//...
}
```
- 创建一个匹配list,将匹配结果放入
```
List<Match> matches = new ArrayList<>();
```
- 从 map 中获取数据
```
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
```
```JAVA
@Nullable
public List<T> getMappingsByUrl(String urlPath) {
return this.urlLookup.get(urlPath);
}
```
urlLookup 是`MultiValueMap`接口.
key:url value:mapping
- addMatchingMappings 方法
```java
if (directPathMatches != null) {
// 添加匹配映射
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
// No choice but to go through all mappings...
// 添加匹配映射
addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
}
```
```java
private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
for (T mapping : mappings) {
// 抽象方法
// 通过抽象方法获取 match 结果
T match = getMatchingMapping(mapping, request);
// 是否为空
if (match != null) {
// 从 mappingLookup 获取结果并且插入到matches中
matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping)));
}
}
}
```
- `getMatchingMapping` 方法是一个抽象方法
```java
protected abstract T getMatchingMapping(T mapping, HttpServletRequest request);
```
- `org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping#getMatchingMapping`
```java
@Override
protected RequestMappingInfo getMatchingMapping(RequestMappingInfo info, HttpServletRequest request) {
return info.getMatchingCondition(request);
}
```
- 第二部分
```java
if (!matches.isEmpty()) {
// 比较对象
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
// 排序
matches.sort(comparator);
// 获取第一个 match 对象
Match bestMatch = matches.get(0);
if (matches.size() > 1) {
if (logger.isTraceEnabled()) {
logger.trace(matches.size() + " matching mappings: " + matches);
}
if (CorsUtils.isPreFlightRequest(request)) {
return PREFLIGHT_AMBIGUOUS_MATCH;
}
Match secondBestMatch = matches.get(1);
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
// 拿出 handlerMethod 进行比较
Method m1 = bestMatch.handlerMethod.getMethod();
Method m2 = secondBestMatch.handlerMethod.getMethod();
String uri = request.getRequestURI();
throw new IllegalStateException(
"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
}
}
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
handleMatch(bestMatch.mapping, lookupPath, request);
return bestMatch.handlerMethod;
}
else {
return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
}
```
- 一行行开始分析
```java
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
```
- 抽象方法`getMappingComparator`
```java
protected abstract Comparator<T> getMappingComparator(HttpServletRequest request);
```
- 实现方法
```java
@Override
protected Comparator<RequestMappingInfo> getMappingComparator(final HttpServletRequest request) {
return (info1, info2) -> info1.compareTo(info2, request);
}
```
内部定义了 compareTo 方法
- 执行完成比较方法后创建对象`MatchComparator`
- 对象创建后进行排序,排序后取出第一个元素作为后续操作的基准对象
```java
// 排序
matches.sort(comparator);
// 获取第一个 match 对象
Match bestMatch = matches.get(0);
```
```java
if (matches.size() > 1) {
if (logger.isTraceEnabled()) {
logger.trace(matches.size() + " matching mappings: " + matches);
}
// 是否跨域请求
if (CorsUtils.isPreFlightRequest(request)) {
return PREFLIGHT_AMBIGUOUS_MATCH;
}
// 取出第二个元素.
Match secondBestMatch = matches.get(1);
// 如果比较结果相同
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
// 第二个元素和第一个元素的比较过程
// 拿出 handlerMethod 进行比较
Method m1 = bestMatch.handlerMethod.getMethod();
Method m2 = secondBestMatch.handlerMethod.getMethod();
String uri = request.getRequestURI();
throw new IllegalStateException(
"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
}
}
```
- 取出第一个元素和第二个元素进行比较. 如果两个 match 相同, 出现异常
最后两个方法
```java
// 设置属性
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
// 处理匹配的结果
handleMatch(bestMatch.mapping, lookupPath, request);
return bestMatch.handlerMethod;
}
else {
// 处理没有匹配的结果
return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
}
```
- `handleMatch`
```java
protected void handleMatch(T mapping, String lookupPath, HttpServletRequest request) {
request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, lookupPath);
}
```
设置一次属性
这个方法子类会继续实现
- `org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping#handleMatch`
```java
@Override
protected void handleMatch(RequestMappingInfo info, String lookupPath, HttpServletRequest request) {
super.handleMatch(info, lookupPath, request);
String bestPattern;
Map<String, String> uriVariables;
// 匹配器
Set<String> patterns = info.getPatternsCondition().getPatterns();
// 如果空设置基本数据
if (patterns.isEmpty()) {
bestPattern = lookupPath;
uriVariables = Collections.emptyMap();
}
else {
// 取出一个匹配器
bestPattern = patterns.iterator().next();
// 地址匹配器比较 路由地址和匹配器比较
uriVariables = getPathMatcher().extractUriTemplateVariables(bestPattern, lookupPath);
}
request.setAttribute(BEST_MATCHING_PATTERN_ATTRIBUTE, bestPattern);
if (isMatrixVariableContentAvailable()) {
// 处理多层参数, 带有;分号的处理
Map<String, MultiValueMap<String, String>> matrixVars = extractMatrixVariables(request, uriVariables);
request.setAttribute(HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE, matrixVars);
}
// 编码url参数
Map<String, String> decodedUriVariables = getUrlPathHelper().decodePathVariables(request, uriVariables);
request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, decodedUriVariables);
if (!info.getProducesCondition().getProducibleMediaTypes().isEmpty()) {
// 获取 media type
Set<MediaType> mediaTypes = info.getProducesCondition().getProducibleMediaTypes();
request.setAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE, mediaTypes);
}
}
```
- `handleNoMatch` 也是同类型操作
- `org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#handleNoMatch`
- `org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping#handleNoMatch`
```java
@Override
protected HandlerMethod handleNoMatch(
Set<RequestMappingInfo> infos, String lookupPath, HttpServletRequest request) throws ServletException {
// 创建对象 PartialMatchHelper
PartialMatchHelper helper = new PartialMatchHelper(infos, request);
if (helper.isEmpty()) {
return null;
}
// 函数是否匹配
if (helper.hasMethodsMismatch()) {
Set<String> methods = helper.getAllowedMethods();
// 请求方式比较
if (HttpMethod.OPTIONS.matches(request.getMethod())) {
// handler 转换
HttpOptionsHandler handler = new HttpOptionsHandler(methods);
// 构建 handler method
return new HandlerMethod(handler, HTTP_OPTIONS_HANDLE_METHOD);
}
throw new HttpRequestMethodNotSupportedException(request.getMethod(), methods);
}
if (helper.hasConsumesMismatch()) {
Set<MediaType> mediaTypes = helper.getConsumableMediaTypes();
MediaType contentType = null;
if (StringUtils.hasLength(request.getContentType())) {
try {
// 字符串转换成对象
contentType = MediaType.parseMediaType(request.getContentType());
}
catch (InvalidMediaTypeException ex) {
throw new HttpMediaTypeNotSupportedException(ex.getMessage());
}
}
throw new HttpMediaTypeNotSupportedException(contentType, new ArrayList<>(mediaTypes));
}
if (helper.hasProducesMismatch()) {
Set<MediaType> mediaTypes = helper.getProducibleMediaTypes();
throw new HttpMediaTypeNotAcceptableException(new ArrayList<>(mediaTypes));
}
if (helper.hasParamsMismatch()) {
List<String[]> conditions = helper.getParamConditions();
throw new UnsatisfiedServletRequestParameterException(conditions, request.getParameterMap());
}
return null;
}
```

@ -0,0 +1,347 @@
# MappingRegistry
- Author: [HuiFer](https://github.com/huifer)
- 源码阅读仓库: [SourceHot-spring](https://github.com/SourceHot/spring-framework-read)
- 源码路径: `org.springframework.jms.annotation.EnableJms`
- 类全路径
- `org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry`
- 基本属性
```java
class MappingRegistry {
/**
* key:mapping
* value: mapping registration
*/
private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
/**
* key: mapping
* value: handlerMethod
*/
private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();
/**
* key: url
* value: list mapping
*/
private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();
/**
* key: name
* value: handler method
*/
private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();
/**
* key:handler method
* value: 跨域配置
*/
private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();
/**
* 读写锁
*/
private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
}
```
- 写一个简单的controller 来进行解析
```java
@RestController
@RequestMapping("/demo")
public class DemoController {
@GetMapping("/do")
public Object go() {
return "fff";
}
}
```
- 前置链路追踪
- `org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#registerHandlerMethod`
```java
protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) {
super.registerHandlerMethod(handler, method, mapping);
this.updateConsumesCondition(mapping, method);
}
```
- `org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#registerHandlerMethod`
```JAVA
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
this.mappingRegistry.register(mapping, handler, method);
}
```
- `org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#register`
本文重点的方法
先将对象截图出来方便后续理解
![image-20200918130340555](/images/springMVC/clazz/image-20200918130340555.png)
## createHandlerMethod
- `org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#createHandlerMethod`
```java
protected HandlerMethod createHandlerMethod(Object handler, Method method) {
// 是否是字符串
if (handler instanceof String) {
// 创建对象
return new HandlerMethod((String) handler,
obtainApplicationContext().getAutowireCapableBeanFactory(), method);
}
return new HandlerMethod(handler, method);
}
```
- HandlerMethod 构造函数
```java
public HandlerMethod(String beanName, BeanFactory beanFactory, Method method){}
public HandlerMethod(Object bean, Method method) {}
```
## HandlerMethod
- 成员变量
```java
public class HandlerMethod {
/** Logger that is available to subclasses. */
protected final Log logger = LogFactory.getLog(getClass());
/**
* beanName 或者 bean 实例
*/
private final Object bean;
/**
* 上下文
*/
@Nullable
private final BeanFactory beanFactory;
/**
* bean 类型
*/
private final Class<?> beanType;
/**
* 处理方法
*/
private final Method method;
private final Method bridgedMethod;
/**
* 方法参数
*/
private final MethodParameter[] parameters;
}
```
## validateMethodMapping
- `org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#validateMethodMapping`
HandlerMethod 进行验证
```java
private void validateMethodMapping(HandlerMethod handlerMethod, T mapping) {
// Assert that the supplied mapping is unique.
// 从缓存中获取
HandlerMethod existingHandlerMethod = this.mappingLookup.get(mapping);
// 是否为空 , 是否相同
if (existingHandlerMethod != null && !existingHandlerMethod.equals(handlerMethod)) {
throw new IllegalStateException(
"Ambiguous mapping. Cannot map '" + handlerMethod.getBean() + "' method \n" +
handlerMethod + "\nto " + mapping + ": There is already '" +
existingHandlerMethod.getBean() + "' bean method\n" + existingHandlerMethod + " mapped.");
}
}
```
## getDirectUrls
- 找到 mapping 匹配的 url
- `org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#getDirectUrls`
```java
private List<String> getDirectUrls(T mapping) {
List<String> urls = new ArrayList<>(1);
// mapping.getPatternsCondition().getPatterns()
for (String path : getMappingPathPatterns(mapping)) {
// 是否匹配
if (!getPathMatcher().isPattern(path)) {
urls.add(path);
}
}
return urls;
}
```
## handlerMethod 和 name 绑定
```java
String name = null;
if (getNamingStrategy() != null) {
// 获取名字
// 类名#方法名
name = getNamingStrategy().getName(handlerMethod, mapping);
// 设置 handlerMethod + name 的关系
addMappingName(name, handlerMethod);
}
```
- `org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMethodMappingNamingStrategy#getName`
```java
@Override
public String getName(HandlerMethod handlerMethod, RequestMappingInfo mapping) {
if (mapping.getName() != null) {
return mapping.getName();
}
StringBuilder sb = new StringBuilder();
// 短类名
String simpleTypeName = handlerMethod.getBeanType().getSimpleName();
for (int i = 0; i < simpleTypeName.length(); i++) {
if (Character.isUpperCase(simpleTypeName.charAt(i))) {
sb.append(simpleTypeName.charAt(i));
}
}
// 组装名称
// 类名+#+方法名称
sb.append(SEPARATOR).append(handlerMethod.getMethod().getName());
return sb.toString();
}
```
## initCorsConfiguration
- `org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#initCorsConfiguration`
```java
@Override
protected CorsConfiguration initCorsConfiguration(Object handler, Method method, RequestMappingInfo mappingInfo) {
// 创建 handlerMethod
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
// 获取 beanType
Class<?> beanType = handlerMethod.getBeanType();
// 获取跨域注解 CrossOrigin
CrossOrigin typeAnnotation = AnnotatedElementUtils.findMergedAnnotation(beanType, CrossOrigin.class);
CrossOrigin methodAnnotation = AnnotatedElementUtils.findMergedAnnotation(method, CrossOrigin.class);
if (typeAnnotation == null && methodAnnotation == null) {
return null;
}
// 跨域信息配置
CorsConfiguration config = new CorsConfiguration();
// 更新跨域配置
updateCorsConfig(config, typeAnnotation);
updateCorsConfig(config, methodAnnotation);
if (CollectionUtils.isEmpty(config.getAllowedMethods())) {
// 跨域配置赋给方法
for (RequestMethod allowedMethod : mappingInfo.getMethodsCondition().getMethods()) {
config.addAllowedMethod(allowedMethod.name());
}
}
// 应用跨域
return config.applyPermitDefaultValues();
}
```
## unregister
- `org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#unregister`
移除 mapping 信息
- 执行 map list 相关的移除方法.
```java
public void unregister(T mapping) {
this.readWriteLock.writeLock().lock();
try {
MappingRegistration<T> definition = this.registry.remove(mapping);
if (definition == null) {
return;
}
this.mappingLookup.remove(definition.getMapping());
for (String url : definition.getDirectUrls()) {
List<T> list = this.urlLookup.get(url);
if (list != null) {
list.remove(definition.getMapping());
if (list.isEmpty()) {
this.urlLookup.remove(url);
}
}
}
removeMappingName(definition);
this.corsLookup.remove(definition.getHandlerMethod());
}
finally {
this.readWriteLock.writeLock().unlock();
}
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Loading…
Cancel
Save