相关源码注释
ApplicationContext
Spring 5 DefaultResourceLoader 源码注释
Spring 5 AbstractApplicationContext 源码注释
BeanFactory
Spring 5 SimpleAliasRegistry 源码注释
Spring 5 DefaultSingletonBeanRegistry 源码注释
Spring 5 FactoryBeanRegistrySupport 源码注释
Spring 5 AbstractBeanFactory 源码注释
Spring 5 AbstractAutowireCapableBeanFactory 源码注释
Spring 5 DefaultLisbaleBeanFactory 源码注释
AbstractBeanFactory#resolveBeanClass
为mdb解析出对应的bean class:
- 如果mbd指定了bean class,就直接返回该bean class
- 调用doResolveBeanClass(mbd, typesToMatch)来解析获取对应的bean Class对象然后返回出去。如果成功获取到 系统的安全管理器,使用特权的方式调用。
- 捕捉PrivilegedActionException,ClassNotFoundException异常和LinkageError错误,保证其异常或错误信息, 抛出CannotLoadBeanClassException
/**
* Resolve the bean class for the specified bean definition,
* resolving a bean class name into a Class reference (if necessary)
* and storing the resolved Class in the bean definition for further use.
* <p>为指定的bean定义解析bean类,将bean类名解析为Class引用(如果需要),并
* 将解析后的Class存储在bean定义中以备将来使用。</p>
* @param mbd the merged bean definition to determine the class for
* -- 合并的bean定义来确定其类
* @param beanName the name of the bean (for error handling purposes)
* -- bean名称(用于错误处理),用于发生异常时,描述异常信息
* @param typesToMatch
* -- 要匹配的类型,用于当该工厂有临时类加载器且该类加载器属于DecoratingClassLoader实例时,
* 对这些要匹配的类型进行在临时类加载器中的排除,以交由父ClassLoader以常规方式处理
* 默认情况下父classLoader是线程上下文类加载器】。<br/>
* the types to match in case of internal type matching purposes
* (also signals that the returned {@code Class} will never be exposed to application code)
* -- 内部类型匹配时要匹配的类型(也表示返回的Class永远不会暴露给应用程序代码)
* @return the resolved bean class (or {@code null} if none)
* -- 解析的Bean类(如果没有,则为null)
* @throws CannotLoadBeanClassException if we failed to load the class
* -- 如果我们无法加载类
* @see #doResolveBeanClass(RootBeanDefinition, Class[])
*/
@Nullable
protected Class<?> resolveBeanClass(final RootBeanDefinition mbd, String beanName, final Class<?>... typesToMatch)
throws CannotLoadBeanClassException {
try {
//如果mbd指定了bean类
if (mbd.hasBeanClass()) {
//获取mbd的指定bean类
return mbd.getBeanClass();
}
//如果成功获取到系统的安全管理器
if (System.getSecurityManager() != null) {
//AccessController.doPrivileged:允许在一个类实例中的代码通知这个AccessController:
// 它的代码主体是享受"privileged(特权的)",它单独负责对它的可得的资源的访问请求,
// 而不管这个请求是由什么代码所引发的。
// 一个调用者在调用doPrivileged方法时,可被标识为 "特权"。在做访问控制决策时,
// 如果checkPermission方法遇到一个通过doPrivileged调用而被表示为 "特权"的调用者,
// 并且没有上下文自变量,checkPermission方法则将终止检查。如果那个调用者的域具有特定的许可,
// 则不做进一步检查,checkPermission安静地返回,表示那个访问请求是被允许的;
// 如果那个域没有特定的许可,则象通常一样,一个异常被抛出。
//参考博客:https://www.jianshu.com/p/3fe79e24f8a1
//参考博客:https://www.iteye.com/blog/huangyunbin-1942509
return AccessController.doPrivileged((PrivilegedExceptionAction<Class<?>>) () ->
doResolveBeanClass(mbd, typesToMatch), getAccessControlContext());
}
else {
return doResolveBeanClass(mbd, typesToMatch);
}
}
//捕捉 无法使用特权异常的
catch (PrivilegedActionException pae) {
//这里没有理解,pae获取的异常一定是ClassNotFoundException?
ClassNotFoundException ex = (ClassNotFoundException) pae.getException();
//包装异常信息,抛出CannotLoadBeanClassException
throw new CannotLoadBeanClassException(mbd.getResourceDescription(), beanName, mbd.getBeanClassName(), ex);
}
//捕捉 未找到类异常
catch (ClassNotFoundException ex) {
//包装异常信息,抛出CannotLoadBeanClassException
throw new CannotLoadBeanClassException(mbd.getResourceDescription(), beanName, mbd.getBeanClassName(), ex);
}
//LinkageError:LinkageError的子类表明一个类对另一个类具有一定的依赖性。但是,后一类在前一类编译之后发生了
//不兼容的变化
catch (LinkageError err) {
//包装错误信息,抛出CannotLoadBeanClassException
throw new CannotLoadBeanClassException(mbd.getResourceDescription(), beanName, mbd.getBeanClassName(), err);
}
}
doResolveBeanClass(mbd, typesToMatch)
获取mbd配置的bean类名,将bean类名解析为Class对象,并将解析后的Class对象缓存在mdb中以备将来使用:
- 获取该工厂的加载bean用的类加载器【变量 beanClassLoader】
- 声明一个动态类加载器【变量 dynamicLoader】,默认引用beanClassLoader
- 声明一个表示mdb的配置的bean类名需要重新被dynameicLoader加载的标记,默认不需要。【变量 freshResolve】
- 如果传入了typesToMatch,且该工厂有临时类加载器【变量 tempClassLoader】:
- 改变dynamicLoader引用为tempClassLoader
- 标记mdb的配置的bean类名需要重新被dynameicLoader加载
- 如果tempClassLoader属于DecoratingClassLoader实例,会对tempClassLoader进行强转为DecoratingClassLoader 【变量 dcl】,然后对typeToMatch在dcl中的排除,使其交由其父classLoader【默认情况下父classLoader是线程上下文类加载器】 进行常规方式处理
- 从mbd中获取配置的bean类名【变量名 className】
- 如果获取到className:
- 评估benaDefinition中包含的className,如果className是可解析表达式,会对其进行解析,否则直接返回className.【变量 evaluated】
- 如果className与evaluated不一样:
- 如果evaluated属于Class实例,强转evaluatedw为Class对象并返回出去
- 如果evaluated属于String实例,将evaluated作为className的值,然后标记mdb配置的bean类名需要重新 被dynameicLoader加载
- 否则:抛出非法状态异常:无效的类名表达式结果:evaluated
- 如果mdb的配置的bean类名需要重新被dynameicLoader加载:
- 如果dynameicLoader不为null,使用dynamicLoader加载className对应的类型,并返回加载成功的Class对象. 同时捕捉未找到类异常【变量ex】
- 如果抛出了ex.打印追踪日志:无法从dynamicLoader中加载类[className]:ex
- 调用ClassUtils.forName(className, dynamicLoader)来获取Class对象并返回出去
- 否则,使用beanClassLoader加载mbd所配置的Bean类名的Class对象并返回出去
/**
* <p>获取mbd配置的bean类名,将bean类名解析为Class对象,并将解析后的Class对象缓存在mdb中以备将来使用</p>
* @param mbd -- 合并的bean定义来确定其类
* @param typesToMatch -- 要匹配的类型,用于当该工厂有临时类加载器且该类加载器属于DecoratingClassLoader实例时,
* 对这些要匹配的类型进行在临时类加载器中的排除,以交由父ClassLoader以常规方式处理
* 【默认情况下父classLoader是线程上下文类加载器】。
* @return -- 解析的Bean类(如果没有,则为null)
* @throws ClassNotFoundException -- 如果我们无法加载类
*/
@Nullable
private Class<?> doResolveBeanClass(RootBeanDefinition mbd, Class<?>... typesToMatch)
throws ClassNotFoundException {
//获取该工厂的加载bean用的类加载器
ClassLoader beanClassLoader = getBeanClassLoader();
//初始化动态类加载器为该工厂的加载bean用的类加载器,如果该工厂有
// 临时类加载器器时,该动态类加载器就是该工厂的临时类加载器
ClassLoader dynamicLoader = beanClassLoader;
//表示mdb的配置的bean类名需要重新被dynameicLoader加载的标记,默认不需要
boolean freshResolve = false;
//如果有传入要匹配的类型
if (!ObjectUtils.isEmpty(typesToMatch)) {
// When just doing type checks (i.e. not creating an actual instance yet),
// use the specified temporary class loader (e.g. in a weaving scenario).
// 仅进行类型检查时(即尚未创建实际实例),请使用指定的临时类加载器
//获取该工厂的临时类加载器,该临时类加载器专门用于类型匹配
ClassLoader tempClassLoader = getTempClassLoader();
//如果成功获取到临时类加载器
if (tempClassLoader != null) {
//以该工厂的临时类加载器作为动态类加载器
dynamicLoader = tempClassLoader;
//标记mdb的配置的bean类名需要重新被dynameicLoader加载
freshResolve = true;
//DecoratingClassLoader:装饰ClassLoader的基类,提供对排除的包和类的通用处理
//如果临时类加载器是DecoratingClassLoader的基类
if (tempClassLoader instanceof DecoratingClassLoader) {
//将临时类加载器强转为DecoratingClassLoader实例
DecoratingClassLoader dcl = (DecoratingClassLoader) tempClassLoader;
//对要匹配的类型进行在装饰类加载器中的排除,以交由父ClassLoader以常规方式处理
for (Class<?> typeToMatch : typesToMatch) {
dcl.excludeClass(typeToMatch.getName());
}
}
}
}
//从mbd中获取配置的bean类名
String className = mbd.getBeanClassName();
//如果能成功获得配置的bean类名
if (className != null) {
//评估benaDefinition中包含的className,如果className是可解析表达式,会对其进行解析,否则直接返回className:
Object evaluated = evaluateBeanDefinitionString(className, mbd);
//如果className与解析后的值不一样
if (!className.equals(evaluated)) {
// A dynamically resolved expression, supported as of 4.2...
// 从4.2开始支持动态解析的表达式
// 如果evaluated属于Class实例
if (evaluated instanceof Class) {
//强转evaluatedw为Class对象并返回出去
return (Class<?>) evaluated;
}
// 如果evaluated属于String实例
else if (evaluated instanceof String) {
//将evaluated作为className的值
className = (String) evaluated;
//标记mdb的配置的bean类名需要重新被dynameicLoader加载
freshResolve = true;
}
else {
//抛出非法状态异常:无效的类名表达式结果:evaluated
throw new IllegalStateException("Invalid class name expression result: " + evaluated);
}
}
//如果mdb的配置的bean类名需要重新被dynameicLoader加载
if (freshResolve) {
// When resolving against a temporary class loader, exit early in order
// to avoid storing the resolved Class in the bean definition.
// 当使用临时类加载器进行解析时,请尽早退出以避免将已解析的类存储在BeanDefinition中
// 如果动态类加载器不为null
if (dynamicLoader != null) {
try {
//使用dynamicLoader加载className对应的类型,并返回加载成功的Class对象
return dynamicLoader.loadClass(className);
}
//捕捉 未找到类异常,
catch (ClassNotFoundException ex) {
if (logger.isTraceEnabled()) {
//打印追踪日志:无法从dynamicLoader中加载类[className]:ex
logger.trace("Could not load class [" + className + "] from " + dynamicLoader + ": " + ex);
}
}
}
//使用classLoader加载name对应的Class对象,该方式是Spring用于代替Class.forName()的方法,支持返回原始的类实例(如'int')
// 和数组类名 (如'String[]')。此外,它还能够以Java source样式解析内部类名(如:'java.lang.Thread.State'
// 而不是'java.lang.Thread$State')
return ClassUtils.forName(className, dynamicLoader);
}
}
// Resolve regularly, caching the result in the BeanDefinition...
// 定期解析,将结果缓存在BeanDefinition中...
// 使用classLoader加载当前BeanDefinitiond对象所配置的Bean类名的Class对象(每次调用都会重新加载,可通过
// AbstractBeanDefinition#getBeanClass 获取缓存):
return mbd.resolveBeanClass(beanClassLoader);
}
DecoratingClassLoader
evaluateBeanDefinitionString(className, mbd);
评估benaDefinition中包含的value,如果value是可解析表达式,会对其进行解析,否则直接返回value:
- 如果该工厂没有设置bean定义值中表达式的解析策略【beanExpressionResolver】,就职返回value【默认情况下, 工厂是配置StandardBeanExpressionResolver作为beanExpressionResolver】
- 如果beanDefinition不为null,获取beanDefinition的当前目标作用域名,然后将其作用域名装换为Scope对象,赋值给 【scope】
- 使用beanExpressionResolver解析value,并返回其解析结果。在解析过程中,会判断value是否为表达式,如果不是 就会直接返回value作为其解析结果
/**
* Resolution strategy for expressions in bean definition values.
* <p>bean定义值中表达式的解析策略</p>
* <p>SpingBoot默认使用的是StandardBeanExpressionResolver</p>
* */
@Nullable
private BeanExpressionResolver beanExpressionResolver;
/**
* Evaluate the given String as contained in a bean definition,
* potentially resolving it as an expression.
* <p>评估bean定义中包含的给定String,可能将其解析为表达式</p>
* @param value the value to check -- 要检查的值
* @param beanDefinition the bean definition that the value comes from -- 值所来自的bean定义
* @return the resolved value -- 解析后的值
* @see #setBeanExpressionResolver
*/
@Nullable
protected Object evaluateBeanDefinitionString(@Nullable String value, @Nullable BeanDefinition beanDefinition) {
//如果该工厂没有设置bean定义值中表达式的解析策略
if (this.beanExpressionResolver == null) {
//直接返回要检查的值
return value;
}
//值所来自的bean定义的当前目标作用域
Scope scope = null;
//如果有传入值所来自的bean定义
if (beanDefinition != null) {
//获取值所来自的bean定义的当前目标作用域名
String scopeName = beanDefinition.getScope();
//如果成功获得值所来自的bean定义的当前目标作用域名
if (scopeName != null) {
//获取scopeName对应的Scope对象
scope = getRegisteredScope(scopeName);
}
}
//评估value作为表达式(如果适用);否则按原样返回值
return this.beanExpressionResolver.evaluate(value, new BeanExpressionContext(this, scope));
}
getRegisteredScope(scopeName);
获取给定作用域名称对应的作用域对象(如果有):
- 如果scopeName为null,抛出异常
- 从映射的linkedHashMap【scopes】中获取scopeName对应的作用域对象并返回
/**
* Map from scope identifier String to corresponding Scope.
* <p>从作用域表示符String映射到相应的作用域</p>
* */
private final Map<String, Scope> scopes = new LinkedHashMap<>(8);
/**
* 获取给定作用域名称对应的作用域对象(如果有)
* @param scopeName the name of the scope -- 作用域名
* @return scopeName对应的作用域对象
*/
@Override
@Nullable
public Scope getRegisteredScope(String scopeName) {
//如果传入的作用域名为null,抛出异常
Assert.notNull(scopeName, "Scope identifier must not be null");
//从映射的linkedHashMap中获取传入的作用域名对应的作用域对象并返回
return this.scopes.get(scopeName);
}