@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
@ -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,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;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 66 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 38 KiB |
After Width: | Height: | Size: 82 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 104 KiB |
After Width: | Height: | Size: 42 KiB |
After Width: | Height: | Size: 83 KiB |
After Width: | Height: | Size: 54 KiB |