@ -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 |