# spring源码分析
# 接口分析
# ResourceLoader 资源加载器
/** Pseudo URL prefix for loading from the class path: "classpath:". */
String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;
// public static final String CLASSPATH_URL_PREFIX = "classpath:";
// 返回指定资源位置的资源句柄。句柄应该始终是一个可重用的资源描述符,允许多个 Resource.getInputStream() 调用。
// 必须支持完全限定的 URL,例如“文件:C:test.dat”。必须支持类路径伪 URL,例如“类路径:test.dat”。
// 应该支持相对文件路径,例如“WEB-INFtest.dat”。 (这将是特定于实现的,通常由 ApplicationContext 实现提供。)请注意,资源句柄并不意味着现有资源;
// 您需要调用 Resource.exists 来检查是否存在。
// 参数:location——资源位置返回:一个对应的资源句柄(永远不会为空)另见:CLASSPATH_URL_PREFIX, Resource.exists(), Resource.getInputStream()
Resource getResource(String location);
// 公开此 ResourceLoader 使用的 ClassLoader。
// 需要直接访问 ClassLoader 的客户端可以通过 ResourceLoader 以统一的方式访问,而不是依赖于线程上下文 ClassLoader。返回:类加载器(如果系统类加载器不可访问,则为空)另见:org.springframework.util.ClassUtils.getDefaultClassLoader(), org.springframework.util.ClassUtils.forName(String, ClassLoader)
@Nullable
ClassLoader getClassLoader();
# DefaultResourceLoader 默认资源加载器
private ClassLoader classLoader;
// 协议解析器集合
private final Set<ProtocolResolver> protocolResolvers = new LinkedHashSet<>(4);
// 资源缓存
private final Map<Class<?>, Map<Resource, ?>> resourceCaches = new ConcurrentHashMap<>(4);
// 默认构造器,新建默认 classLoader
public DefaultResourceLoader() {
this.classLoader = ClassUtils.getDefaultClassLoader();
}
// 带参构造器,接收传入的 classLoader
public DefaultResourceLoader(@Nullable ClassLoader classLoader) {
this.classLoader = classLoader;
}
// classLoader 的set方法赋值
public void setClassLoader(@Nullable ClassLoader classLoader) {
this.classLoader = classLoader;
}
// 获取 classLoader
public ClassLoader getClassLoader() {
return (this.classLoader != null ? this.classLoader : ClassUtils.getDefaultClassLoader());
}
// 添加协议解析器
public void addProtocolResolver(ProtocolResolver resolver) {
Assert.notNull(resolver, "ProtocolResolver must not be null");
this.protocolResolvers.add(resolver);
}
// 获取协议解析器集合
public Collection<ProtocolResolver> getProtocolResolvers() {
return this.protocolResolvers;
}
// 从缓存中获取资源
// 如果 valueType 在 resourceCaches 则直接取出返回,如果不在,则向 resourceCaches 中放一个 key 为 valueType ,值为 new ConcurrentHashMap<>(),并返回 new ConcurrentHashMap<>()
public <T> Map<Resource, T> getResourceCache(Class<T> valueType) {
return (Map<Resource, T>) this.resourceCaches.computeIfAbsent(valueType, key -> new ConcurrentHashMap<>());
}
// 清空资源缓存
public void clearResourceCaches() {
this.resourceCaches.clear();
}
public Resource getResource(String location) {
Assert.notNull(location, "Location must not be null");
// 遍历协议解析器
for (ProtocolResolver protocolResolver : getProtocolResolvers()) {
// 执行协议解析
Resource resource = protocolResolver.resolve(location, this);
if (resource != null) {
return resource;
}
}
// 解析路径返回资源
if (location.startsWith("/")) {
return getResourceByPath(location);
}
// 以 CLASSPATH_URL_PREFIX 开头 (ResourceLoader 中的常量:classpath:)
else if (location.startsWith(CLASSPATH_URL_PREFIX)) {
return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
}
else {
try {
// Try to parse the location as a URL...
URL url = new URL(location);
return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url));
}
catch (MalformedURLException ex) {
// No URL -> resolve as resource path.
return getResourceByPath(location);
}
}
}
// 按照路径获取资源
protected Resource getResourceByPath(String path) {
return new ClassPathContextResource(path, getClassLoader());
}
// 相对路径上下文资源
protected static class ClassPathContextResource extends ClassPathResource implements ContextResource {
public ClassPathContextResource(String path, @Nullable ClassLoader classLoader) {
super(path, classLoader);
}
@Override
public String getPathWithinContext() {
return getPath();
}
@Override
public Resource createRelative(String relativePath) {
String pathToUse = StringUtils.applyRelativePath(getPath(), relativePath);
//
return new ClassPathContextResource(pathToUse, getClassLoader());
}
}
# ClassRelativeResourceLoader 类相对资源加载器
public class ClassRelativeResourceLoader extends DefaultResourceLoader {
private final Class<?> clazz;
public ClassRelativeResourceLoader(Class<?> clazz) {
Assert.notNull(clazz, "Class must not be null");
this.clazz = clazz;
setClassLoader(clazz.getClassLoader());
}
@Override
protected Resource getResourceByPath(String path) {
return new ClassRelativeContextResource(path, this.clazz);
}
private static class ClassRelativeContextResource extends ClassPathResource implements ContextResource {
private final Class<?> clazz;
public ClassRelativeContextResource(String path, Class<?> clazz) {
super(path, clazz);
this.clazz = clazz;
}
@Override
public String getPathWithinContext() {
return getPath();
}
@Override
public Resource createRelative(String relativePath) {
String pathToUse = StringUtils.applyRelativePath(getPath(), relativePath);
// 类相对路径
return new ClassRelativeContextResource(pathToUse, this.clazz);
}
}
}
# FileSystemResourceLoader 文件系统路径资源加载器
public class FileSystemResourceLoader extends DefaultResourceLoader {
@Override
protected Resource getResourceByPath(String path) {
if (path.startsWith("/")) {
path = path.substring(1);
}
return new FileSystemContextResource(path);
}
private static class FileSystemContextResource extends FileSystemResource implements ContextResource {
public FileSystemContextResource(String path) {
super(path);
}
@Override
public String getPathWithinContext() {
return getPath();
}
}
}
# ResourcePatternResolver 资源模式解析器
String CLASSPATH_ALL_URL_PREFIX = "classpath*:";
// 将给定的位置模式解析为 Resource 对象。
// 应尽可能避免指向同一物理资源的重叠资源条目。结果应该具有设置语义。参数:locationPattern——要解析的位置模式
// 返回:对应的资源对象;在IO错误的情况下,抛出:IOException
Resource[] getResources(String locationPattern) throws IOException;
# InputStreamSource 输入流资源
// 为底层资源的内容返回一个 InputStream。预计每次调用都会创建一个新流。
// 当您考虑 JavaMail 等 API 时,此要求尤其重要,该 API 在创建邮件附件时需要能够多次读取流。
// 对于这样的用例,要求每个 getInputStream() 调用都返回一个新的流。
// 返回: 底层资源的输入流(不能为空);如果底层资源不存在,抛出: java.io.FileNotFoundException;如果无法打开内容流,抛出 IOException
InputStream getInputStream() throws IOException;
# Resource 资源
// 确定该资源是否以物理形式实际存在。此方法执行明确的存在检查,而资源句柄的存在仅保证有效的描述符句柄
boolean exists();
// 指示是否可以通过 getInputStream() 读取此资源的非空内容。对于存在的典型资源描述符将是正确的,因为它严格地暗示了自 5.1 起的 exists() 语义。请注意,尝试实际阅读内容时可能仍会失败。但是,false 值是无法读取资源内容的明确指示。另请参阅:getInputStream()、exists()
default boolean isReadable() {
return exists();
}
// 指示此资源是否表示具有打开流的句柄。如果为 true,则 InputStream 不能被多次读取,必须读取并关闭以避免资源泄漏。对于典型的资源描述符将是假的。
default boolean isOpen() {
return false;
}
// 确定此资源是否代表文件系统中的文件
default boolean isFile() {
return false;
}
// 返回此资源的 URL 句柄。如果资源不能解析为 URL,即如果资源不能作为描述符,抛出: IOException
URL getURL() throws IOException;
// 返回此资源的 URI 句柄。如果资源不能被解析为 URI,即如果资源不能作为描述符使用,抛出:IOException
URI getURI() throws IOException;
// 返回此资源的文件句柄。如果资源无法解析为绝对文件路径,即如果资源在文件系统中不可用,抛出: java.io.FileNotFoundException;在一般解析读取失败的情况下, IOException
File getFile() throws IOException;
// 返回一个 ReadableByteChannel。预计每次调用都会创建一个新通道。默认实现返回 Channels.newChannel(InputStream) 和 getInputStream() 的结果。返回:底层资源的字节通道(不能为空);如果底层资源不存在 抛出:java.io.FileNotFoundException;如果内容通道无法打开,抛出 IOException
default ReadableByteChannel readableChannel() throws IOException {
return Channels.newChannel(getInputStream());
}
// 确定此资源的内容长度。如果资源无法解析(在文件系统中或作为其他已知的物理资源类型)抛出: IOException
long contentLength() throws IOException;
// 确定此资源的最后修改时间戳。如果资源无法解析(在文件系统中或作为其他已知的物理资源类型)抛出: IOException
long lastModified() throws IOException;
// 创建与此资源相关的资源。参数:relativePath - 相对路径(相对于该资源)返回:相对资源的资源句柄;如果无法确定相对资源,抛出:IOException
Resource createRelative(String relativePath) throws IOException;
// 确定此资源的文件名,即通常是路径的最后一部分:例如,“myfile.txt”。如果此类资源没有文件名,则返回 null。
String getFilename();
// 返回此资源的描述,用于在使用资源时输出错误。还鼓励实现从它们的 toString 方法返回这个值。另请参见:Object.toString()
String getDescription();
# ContextResource 上下文资源
// 返回封闭的“上下文”中的路径。这通常是相对于特定于上下文的根目录的路径,例如ServletContext 根或 PortletContext 根。
String getPathWithinContext();
# WritableResource 可写入的资源
// 指示是否可以通过 getOutputStream() 写入此资源的内容。对于典型的资源描述符将是真的;请注意,实际内容写入在尝试时可能仍会失败。但是,false 值是无法修改资源内容的明确指示。另请参见:getOutputStream()、isReadable()
default boolean isWritable() {
return true;
}
// 为底层资源返回一个 OutputStream,允许(覆盖)写入其内容。如果无法打开流 另见:getInputStream(),抛出:IOException
OutputStream getOutputStream() throws IOException;
// 返回 WritableByteChannel。预计每次调用都会创建一个新通道。默认实现返回 Channels.newChannel(OutputStream) 和 getOutputStream() 的结果。返回: 底层资源的字节通道(不能为空);如果底层资源不存在,抛出: java.io.FileNotFoundException;如果内容通道无法打开,抛出 IOException
default WritableByteChannel writableChannel() throws IOException {
return Channels.newChannel(getOutputStream());
}
# ProtocolResolver 协议解析器
// 用户扩展点:实现该接口,支持用户自定义自己的解析协议
// 解析
Resource resolve(String location, ResourceLoader resourceLoader);
# PropertyResolver 属性解析器
// 是否包含该key
boolean containsProperty(String key);
// 基于key获取字符串类型的值
String getProperty(String key);
// 基于key获取字符串类型的值,如果该值为空,返回传入的默认值
String getProperty(String key, String defaultValue);
// 基于key获取传入类的实例
<T> T getProperty(String key, Class<T> targetType);
// 基于key获取传入类的实例,如果实例为空,返回传入的默认值
<T> T getProperty(String key, Class<T> targetType, T defaultValue);
// 基于key获取必要的属性,如果值为空,则抛出异常
String getRequiredProperty(String key) throws IllegalStateException;
// 基于key获取必要的传入类的实例,如果值为空,则抛出异常
<T> T getRequiredProperty(String key, Class<T> targetType) throws IllegalStateException;
// 解析给定文本中的 {...} 占位符,将它们替换为由 getProperty 解析的相应属性值。没有默认值的无法解析的占位符将被忽略并保持不变
String resolvePlaceholders(String text);
// 解析给定文本中的 {...} 占位符,将它们替换为由 getProperty 解析的相应属性值。没有默认值的无法解析时,抛出异常
String resolveRequiredPlaceholders(String text) throws IllegalArgumentException;
# Environment 环境
// 获取此环境明确激活的配置文件集;设置方式有以下几种
// 1.可以通过将“spring.profiles.active”设置为系统属性
// 2.通过调用 ConfigurableEnvironment.setActiveProfiles(String...) 来激活配置文件
String[] getActiveProfiles();
// 当没有明确设置活动配置文件时,默认情况下返回要激活的配置文件集
String[] getDefaultProfiles();
// 返回一个或多个给定的配置文件是否处于活动状态;
// 例如, env.acceptsProfiles("p1", "!p2") 如果配置文件 'p1' 处于活动状态或 'p2' 未处于活动状态,则将返回 true。自 5.1 起弃用
boolean acceptsProfiles(String... profiles);
// 返回一个给定的配置文件是否处于活动状态;
boolean acceptsProfiles(Profiles profiles);
# ConfigurablePropertyResolver 可配置的属性解析器
// 返回对属性执行类型转换时使用的 ConfigurableConversionService
ConfigurableConversionService getConversionService();
// 设置对属性执行类型转换时使用的 ConfigurableConversionService
void setConversionService(ConfigurableConversionService conversionService);
// 设置此解析器替换的占位符必须以前缀开头
void setPlaceholderPrefix(String placeholderPrefix);
// 设置此解析器替换的占位符必须以结尾的后缀
void setPlaceholderSuffix(String placeholderSuffix);
// 指定由该解析器替换的占位符与其关联的默认值之间的分隔符,如果不应将此类特殊字符作为值分隔符处理,则指定为 null。
void setValueSeparator(@Nullable String valueSeparator);
// 设置在遇到嵌套在给定属性值中的无法解析的占位符时是否抛出异常。
// false 值表示严格解析,即会抛出异常。
// true 值表示不可解析的嵌套占位符应以其未解析的 {...} 形式传递。
// 当属性值包含无法解析的占位符时,getProperty(String) 及其变体的实现必须检查此处设置的值以确定正确的行为
void setIgnoreUnresolvableNestedPlaceholders(boolean ignoreUnresolvableNestedPlaceholders);
// 指定必须存在的属性,以通过 validateRequiredProperties() 进行验证
void setRequiredProperties(String... requiredProperties);
// 验证 setRequiredProperties 指定的每个属性是否存在并解析为非空值。
// 如果任何必需的属性不可解析,抛出: MissingRequiredPropertiesException
void validateRequiredProperties() throws MissingRequiredPropertiesException;
# Converter 转换器
// 将类型 S 的源对象转换为目标类型 T。 参数:源 – 要转换的源对象,必须是 S 的实例(永远不会为空)返回:转换后的对象,必须是 T 的实例(可能为空)
// 如果源无法转换为所需的目标类型 抛出 IllegalArgumentException
T convert(S source);
# ConverterRegistry 转换器注册表
// 向该注册表添加一个普通转换器
void addConverter(Converter<?, ?> converter);
// 向该注册表添加一个普通转换器。明确指定了可转换的 sourcetarget 类型
<S, T> void addConverter(Class<S> sourceType, Class<T> targetType, Converter<? super S, ? extends T> converter);
// 向该注册表添加一个通用转换器
void addConverter(GenericConverter converter);
// 向该注册表添加一个转换器工厂
void addConverterFactory(ConverterFactory<?, ?> factory);
// 删除所有从 sourceType 到 targetType 的转换器。
void removeConvertible(Class<?> sourceType, Class<?> targetType);
# ConversionService 转换服务
// 如果 sourceType 的对象可以转换为 targetType,则返回 true。
// 如果此方法返回 true,则表示 convert(Object, Class) 能够将 sourceType 的实例转换为 targetType。
// 关于集合、数组和映射类型的特别说明:对于集合、数组和映射类型之间的转换,即使如果底层元素不可转换,转换调用仍可能生成 ConversionException,此方法也将返回 true。
// 在处理集合和映射时,调用者应该处理这种特殊情况。参数:sourceType – 要转换的源类型(如果源为 null,则可能为 null);targetType – 要转换为的目标类型(必需)返回:如果可以执行转换,则为 true,否则为 false;如果 targetType 为空值,抛出:IllegalArgumentException
boolean canConvert(@Nullable Class<?> sourceType, Class<?> targetType);
// 如果 sourceType 的对象可以转换为 targetType,则返回 true。
// TypeDescriptors 提供有关发生转换的源和目标位置的附加上下文,通常是对象字段或属性位置。如果此方法返回 true,则表示 convert(Object, TypeDescriptor, TypeDescriptor) 能够将 sourceType 的实例转换为 targetType。
// 关于集合、数组和映射类型的特别说明:对于集合、数组和映射类型之间的转换,即使如果底层元素不可转换,转换调用仍可能生成 ConversionException,此方法也将返回 true。
// 在处理集合和映射时,调用者应该处理这种特殊情况。参数: sourceType - 关于要转换的源类型的上下文(如果源为 null,则可能为 null);targetType - 关于要转换为的目标类型的上下文(必需)返回:如果可以在源类型和目标类型之间执行转换,则为 true,如果不是,则为 false;如果 targetType 为 null 抛出:IllegalArgumentException
boolean canConvert(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType);
// 将给定的源转换为指定的目标类型。
// 参数: source - 要转换的源对象(可能为 null) targetType - 要转换为的目标类型(必需)返回:转换后的对象,targetType 的一个实例
// 如果发生转换异常,抛出:ConversionException; 如果 targetType 为 null,抛出 IllegalArgumentException
<T> T convert(@Nullable Object source, Class<T> targetType);
// 将给定的源转换为指定的目标类型。
// TypeDescriptors 提供有关将发生转换的源和目标位置的附加上下文,通常是对象字段或属性位置。
// 参数: source - 要转换的源对象(可能为 null);sourceType - 关于要转换的源类型的上下文(如果源为 null,则可能为 null);targetType - 关于要转换为的目标类型的上下文(必需)返回:转换后的object,targetType 的一个实例
// 如果发生转换异常,抛出: ConversionException;如果 targetType 为 null,或者 sourceType 为 null 但 source 不为 null,抛出 IllegalArgumentException
Object convert(@Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType);
# GenericConverter 调用转换器
// 返回此转换器可以在其间转换的源类型和目标类型。
// 每个条目都是一个可转换的源到目标类型对。
// 对于条件转换器,此方法可能返回 null 以指示应考虑所有源到目标对
Set<ConvertiblePair> getConvertibleTypes();
// 将源对象转换为 TypeDescriptor 描述的 targetType。
// 参数:source——要转换的源对象(可能为空)
// sourceType——我们要转换的字段的类型描述符
// targetType——我们要转换的字段的类型描述符返回:转换后的对象
Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
// 转换类型对
final class ConvertiblePair {
private final Class<?> sourceType;
private final Class<?> targetType;
public ConvertiblePair(Class<?> sourceType, Class<?> targetType) {
Assert.notNull(sourceType, "Source type must not be null");
Assert.notNull(targetType, "Target type must not be null");
this.sourceType = sourceType;
this.targetType = targetType;
}
public Class<?> getSourceType() {
return this.sourceType;
}
public Class<?> getTargetType() {
return this.targetType;
}
@Override
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
if (other == null || other.getClass() != ConvertiblePair.class) {
return false;
}
ConvertiblePair otherPair = (ConvertiblePair) other;
return (this.sourceType == otherPair.sourceType && this.targetType == otherPair.targetType);
}
@Override
public int hashCode() {
return (this.sourceType.hashCode() * 31 + this.targetType.hashCode());
}
@Override
public String toString() {
return (this.sourceType.getName() + " -> " + this.targetType.getName());
}
}
# ConverterFactory 转换器工厂
// 获取从 S 转换为目标类型 T 的转换器,其中 T 也是 R 的一个实例
<T extends R> Converter<S, T> getConverter(Class<T> targetType);
# 工具类
# PropertyPlaceholderHelper 属性占位符处理类
# BeanUtils bean的工具类
# 部分主要流程
# ClassPathXmlApplicationContext 基于xml配置的上下文