Spring前置准备(九)——Spring中的Bean类加载器
我们重新回忆一下这个方法,我们讲完了obtainFreshBeanFactory,继续解析prepareBeanFactory方法的分析:
@Override
public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");// Prepare this context for refreshing.prepareRefresh();// 设置refreshed字段为true,refreshed字段的作用目前还没找到// 返回当前工厂对象ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// 填充bean工厂的一些基本参数// Prepare the bean factory for use in this context.prepareBeanFactory(beanFactory);try {// Allows post-processing of the bean factory in context subclasses.postProcessBeanFactory(beanFactory);StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");// Invoke factory processors registered as beans in the context.// 在上下文中调用作为 Bean 注册的工厂处理器。invokeBeanFactoryPostProcessors(beanFactory);// Register bean processors that intercept bean creation.registerBeanPostProcessors(beanFactory);beanPostProcess.end();// Initialize message source for this context.initMessageSource();// Initialize event multicaster for this context.initApplicationEventMulticaster();// Initialize other special beans in specific context subclasses.onRefresh();// 检查侦听器 bean 并注册它们。// Check for listener beans and register them.registerListeners();// 实例化所有剩余的(非lazy-init初始化)单例// Instantiate all remaining (non-lazy-init) singletons.finishBeanFactoryInitialization(beanFactory);// Last step: publish corresponding event.finishRefresh();}catch (BeansException ex) {if (logger.isWarnEnabled()) {logger.warn("Exception encountered during context initialization - " +"cancelling refresh attempt: " + ex);}// Destroy already created singletons to avoid dangling resources.destroyBeans();// Reset 'active' flag.cancelRefresh(ex);// Propagate exception to caller.throw ex;}finally {// Reset common introspection caches in Spring's core, since we// might not ever need metadata for singleton beans anymore...resetCommonCaches();contextRefresh.end();}}
}
prepareBeanFactory
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {// Tell the internal bean factory to use the context's class loader etc.// 设置bean 加载器,主要用于强制解析bean的时候重新加载当前类并返回class对象beanFactory.setBeanClassLoader(getClassLoader());// el表达式相关映射的处理beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));// Configure the bean factory with context callbacks.beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));// ignoreDependencyInterface 方法主要用于忽略特定接口类型的依赖注入,忽略后不对指定的类型进行注入beanFactory.ignoreDependencyInterface(EnvironmentAware.class);beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);beanFactory.ignoreDependencyInterface(MessageSourceAware.class);beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);beanFactory.ignoreDependencyInterface(ApplicationStartupAware.class);// BeanFactory interface not registered as resolvable type in a plain factory.// MessageSource registered (and found for autowiring) as a bean.beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);beanFactory.registerResolvableDependency(ResourceLoader.class, this);beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);beanFactory.registerResolvableDependency(ApplicationContext.class, this);// Register early post-processor for detecting inner beans as ApplicationListeners.beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));// Detect a LoadTimeWeaver and prepare for weaving, if found.if (!NativeDetector.inNativeImage() && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));// Set a temporary ClassLoader for type matching.beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));}// Register default environment beans.if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());}if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());}if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());}if (!beanFactory.containsLocalBean(APPLICATION_STARTUP_BEAN_NAME)) {beanFactory.registerSingleton(APPLICATION_STARTUP_BEAN_NAME, getApplicationStartup());}}
首先是beanFactory.setBeanClassLoader(getClassLoader());这段代码,Java API中说明到,这个方法是设置Spring框架的类加载器,这个方法是属于ConfigurableBeanFactory的,其中设置bean的累加载器对应的还有获取加载器的方法:
/*** Set the class loader to use for loading bean classes.* Default is the thread context class loader.* <p>Note that this class loader will only apply to bean definitions* that do not carry a resolved bean class yet. This is the case as of* Spring 2.0 by default: Bean definitions only carry bean class names,* to be resolved once the factory processes the bean definition.* @param beanClassLoader the class loader to use,* or {@code null} to suggest the default class loader*/void setBeanClassLoader(@Nullable ClassLoader beanClassLoader);/*** Return this factory's class loader for loading bean classes* (only {@code null} if even the system ClassLoader isn't accessible).* @see org.springframework.util.ClassUtils#forName(String, ClassLoader)*/@NullableClassLoader getBeanClassLoader();
点击其实现类会发现据具体实现是在AbstractBeanFactory中实现的:
getBeanClassLoadergetBeanClassLoaderpublic abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {// 代码省略......../** ClassLoader to resolve bean class names with, if necessary. */@Nullableprivate ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();// 代码省略........@Overridepublic void setBeanClassLoader(@Nullable ClassLoader beanClassLoader) {this.beanClassLoader = (beanClassLoader != null ? beanClassLoader : ClassUtils.getDefaultClassLoader());}@Override@Nullablepublic ClassLoader getBeanClassLoader() {return this.beanClassLoader;}// 代码省略........
}
并且我们可以看出,如果不使用setBeanClassLoader方法进行设置,则使用默认的ClassLoader,该方法首先获取当前线程默认的类加载器,如果获取失败则直接使用ClassUtils 的类加载器,获取失败则使用系统类加载器:
@Nullable
public static ClassLoader getDefaultClassLoader() {ClassLoader cl = null;try {// 获取当前线程的累加载器cl = Thread.currentThread().getContextClassLoader();}catch (Throwable ex) {// Cannot access thread context ClassLoader - falling back...}if (cl == null) {// No thread context class loader -> use class loader of this class.// 没有线程类加载器,则使用ClassUtils的类加载器cl = ClassUtils.class.getClassLoader();if (cl == null) {// getClassLoader() returning null indicates the bootstrap ClassLoader// getClassLoader() 返回 null 表示bootstrap ClassLoadertry {cl = ClassLoader.getSystemClassLoader();}catch (Throwable ex) {// Cannot access system ClassLoader - oh well, maybe the caller can live with null...}}}return cl;
}
也就是通过getBeanClassLoader方法可以获取到Spring框架的类加载器,我通过debug的方式试了一下一共有以下几个地方用到了getBeanClassLoader方法:
- prepareBeanFactory方法
- AbstractBeanFactory 中的doResolveBeanClass方法
- StandardBeanExpressionResolver中的evaluate方法
- AbstractApplicationEventMulticaster 中的setBeanFactory方法
- BeanDefinitionValueResolver 的resolveTargetType方法
prepareBeanFactory方法中对于getBeanClassLoader方法的应用
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {// Tell the internal bean factory to use the context's class loader etc.// 设置bean的类加载器beanFactory.setBeanClassLoader(getClassLoader());// 这里的beanFactory.getBeanClassLoader()方法也是调用的AbstractBeanFactory的getBeanClassLoader方法beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
}
而StandardBeanExpressionResolver类的主要作用是解析EL表达式(现在几乎没什么应用场景,了解一下就行,很老的东西了),举一个简单的例子,首先定义一个User 类:
public class User {private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}
}
再定义一个AnotherBean类:
public class AnotherBean {private String userName;public String getUserName() {return userName;}public void setUserName(String userName) {this.userName = userName;}
}
然后在 Spring 配置文件(以 XML 为例)中使用 SpEL 表达式:
<bean id="user" class="com.example.User"><property name="name" value="John"/>
</bean>
<bean id="anotherBean" class="com.example.AnotherBean"><property name="userName" value="#{user.name}"/>
</bean>
在上述配置中,StandardBeanExpressionResolver会解析#{user.name}表达式,获取user Bean 的name属性值,并将其设置给anotherBean的userName属性。继续上面的代码:
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
StandardBeanExpressionResolver构造方法内容如下:
public StandardBeanExpressionResolver(@Nullable ClassLoader beanClassLoader) {this.expressionParser = new SpelExpressionParser(new SpelParserConfiguration(null, beanClassLoader));
}
然后经过我一层层往下点,看到了这个构造方法:
public SpelParserConfiguration(@Nullable SpelCompilerMode compilerMode, @Nullable ClassLoader compilerClassLoader,boolean autoGrowNullReferences, boolean autoGrowCollections, int maximumAutoGrowSize) {this.compilerMode = (compilerMode != null ? compilerMode : defaultCompilerMode);this.compilerClassLoader = compilerClassLoader;this.autoGrowNullReferences = autoGrowNullReferences;this.autoGrowCollections = autoGrowCollections;this.maximumAutoGrowSize = maximumAutoGrowSize;
}
可以看到,通过SpelParserConfiguration类中的构造方法Bean的类加载器被赋值给了:compilerClassLoader这个变量,而这个变量还有另一个对应的getCompilerClassLoader方法:
@Nullable
public ClassLoader getCompilerClassLoader() {return this.compilerClassLoader;
}
并且我发现只用一个compileExpression方法调用到了它(请看下方代码中的注释,这里的this.configuration.getCompilerClassLoader()调用到了getCompilerClassLoader方法):
/*** Perform expression compilation. This will only succeed once exit descriptors for* all nodes have been determined. If the compilation fails and has failed more than* 100 times the expression is no longer considered suitable for compilation.* @return whether this expression has been successfully compiled*/
public boolean compileExpression() {CompiledExpression compiledAst = this.compiledAst;if (compiledAst != null) {// Previously compiledreturn true;}if (this.failedAttempts.get() > FAILED_ATTEMPTS_THRESHOLD) {// Don't try againreturn false;}synchronized (this) {if (this.compiledAst != null) {// Compiled by another thread before this thread got into the sync blockreturn true;}try {// 这里的this.configuration.getCompilerClassLoader()调用到了getCompilerClassLoader方法SpelCompiler compiler = SpelCompiler.getCompiler(this.configuration.getCompilerClassLoader());compiledAst = compiler.compile(this.ast);if (compiledAst != null) {// Successfully compiledthis.compiledAst = compiledAst;return true;}else {// Failed to compilethis.failedAttempts.incrementAndGet();return false;}}catch (Exception ex) {// Failed to compilethis.failedAttempts.incrementAndGet();// If running in mixed mode, revert to interpretedif (this.configuration.getCompilerMode() == SpelCompilerMode.MIXED) {this.compiledAst = null;this.interpretedCount.set(0);return false;}else {// Running in SpelCompilerMode.immediate mode - propagate exception to callerthrow new SpelEvaluationException(ex, SpelMessage.EXCEPTION_COMPILING_EXPRESSION);}}}
}
而getCompiler方法内容如下:
public static SpelCompiler getCompiler(@Nullable ClassLoader classLoader) {ClassLoader clToUse = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());// Quick check for existing compiler without lock contentionSpelCompiler compiler = compilers.get(clToUse);if (compiler == null) {// Full lock now since we're creating a child ClassLoadersynchronized (compilers) {compiler = compilers.get(clToUse);if (compiler == null) {compiler = new SpelCompiler(clToUse);compilers.put(clToUse, compiler);}}}return compiler;
}
点击compilers字段我们会发现,它是一个map集合,这里的类加载器的作用只是做了一层映射,并且通过类加载器作为key,获取了什么:
public final class SpelCompiler implements Opcodes {// A compiler is created for each classloader, it manages a child class loader of that// 为每个类加载器创建一个编译器,它管理该类加载器的子类加载器// classloader and the child is used to load the compiled expressions.// classloader 和子项用于加载编译后的表达式。private static final Map<ClassLoader, SpelCompiler> compilers = new ConcurrentReferenceHashMap<>();
}
由于现在几乎不再用到EL表达式这里不再详细了解,获取了什么,之了解到这个类加载器在StandardBeanExpressionResolver中仅仅只是用于获取表到达式中的某个事物就好,有需要的可以自己再深挖一下代码
AbstractBeanFactory 中的doResolveBeanClass方法
在 Spring 框架中,doResolveBeanClass方法主要用于解析Bean定义中的类信息。当 Spring 加载Bean定义时,有时需要根据各种情况来确定Bean实际对应的类。这个方法在处理Bean的类加载相关逻辑中起着重要作用。
关于freshResolve参数
boolean freshResolve = false;
freshResolve参数通常可以理解为 “是否进行全新的解析”,它决定是否以一种全新的、不依赖之前可能缓存结果的方式去解析Bean的类。
具体含义:
- 当freshResolve为true时,意味着强制 Spring 重新去解析Bean的类,即使之前可能已经缓存了该Bean类的解析结果,
- 这在某些情况下很有用,比如当Bean的定义可能发生了动态变化,或者需要确保获取到最新的类解析结果时。
- 例如,在一些支持动态刷新Bean定义的场景中,就可能会将freshResolve设置为true,以保证解析到最新的Bean类信息。
- 相反,当freshResolve为false时,Spring 可能会首先尝试从缓存中获取之前解析好的Bean类,如果缓存中有,则直接返回,而不会重新进行复杂的解析操作,这样可以提高效率。
该方法中的下面部分代码获取了Bean的类加载器:
ClassLoader beanClassLoader = getBeanClassLoader();
而通过上面我们知道freshResolve的作用是是否强制Spring 重新去解析Bean的类,也就是说这里的类加载器的作用则是freshResolve为ture的时候强制重新按照类名称加载当前类(下面的代码取自doResolveBeanClass方法中的代码片段),
if (freshResolve) {// When resolving against a temporary class loader, exit early in order// to avoid storing the resolved Class in the bean definition.if (dynamicLoader != null) {try {// 这里使用类加载器强制加载当前类return dynamicLoader.loadClass(className);}catch (ClassNotFoundException ex) {if (logger.isTraceEnabled()) {logger.trace("Could not load class [" + className + "] from " + dynamicLoader + ": " + ex);}}}return ClassUtils.forName(className, dynamicLoader);
}
所以说这里的类加载器的作用是用于在某些情况下需要强制解析bean的时候对class文件进行加载,从代码层面来看一共存在两种情况:
- typesToMatch 字段不为空的情况下,并且tempClassLoader不为空的情况下会强制加载class对象
- className不等于空,并且不是EL表达式的情况下(主要通过:if (!className.equals(evaluated))进行判断),并且获取到的evaluated参数是一个字符串类型的值
为什么说if (!className.equals(evaluated))判断了当前是否是EL表达式
首先我们要了解evaluateBeanDefinitionString这个方法的作用,evaluateBeanDefinitionString 方法在Spring框架中主要用于对 Bean 定义中的字符串表达式进行解析和求值。
- 这里evaluateBeanDefinitionString方法尝试解析className。
- 如果className是一个 SpEL 表达式,该方法会对其求值并返回结果;如果className不是 SpEL 表达式(即不包含#{}),该方法会直接返回className本身。
- 而如果className是EL表达式,则一定会存在
#{}这个符号,解析完成后的className的值一定不存在#{}符号,所以通过该判断我们就能知道刚开始的className是否是一个EL表达式
之后部分就是判断evaluated变量是否是String类型的逻辑了:
if (evaluated instanceof String str) {className = str;freshResolve = true;
}
这里的**evaluated instanceof String str **的作用是,如果是String类型则创建一个新的字符串str对象,然后将evaluated变量的值赋值给str,也是是说将最后的结果是将evaluated的值赋值给了className
为什么在evaluated 的值等于字符串的时候就要强制加载class呢?
evaluateBeanDefinitionString 方法用于解析可能包含SpEL表达式的类名字符串。如果解析结果 evaluated 是一个字符串,这意味着原始的类名是通过表达式动态生成的。例如,
#{systemProperties['env'] == 'dev'? 'com.example.DevMyService' : 'com.example.ProdMyService'}`
而这种表达式可能会导致类的需要加载的类可能出现动态变化,为了确保使用最新的类定义,需要强制重新加载。
doResolveBeanClass 方法内容如下:
@Nullable
private Class<?> doResolveBeanClass(RootBeanDefinition mbd, Class<?>... typesToMatch)throws ClassNotFoundException {ClassLoader beanClassLoader = getBeanClassLoader();ClassLoader dynamicLoader = beanClassLoader;// 这个变量的主要作用是,是否以一种全新的、不依赖之前可能缓存结果的方式去解析Bean的类。// 当freshResolve为true时,意味着强制 Spring 重新去解析Bean的类,即使之前可能已经缓存// 了该Bean类的解析结果。这在某些情况下很有用,比如当Bean的定义可能发生了动态变化,或// 者需要确保获取到最新的类解析结果时。例如,在一些支持动态刷新Bean定义的场景中,就可// 能会将freshResolve设置为true,以保证解析到最新的Bean类信息。相反,当freshResolve为false时,// Spring 可能会首先尝试从缓存中获取之前解析好的Bean类,如果缓存中有,则直接返回,而不会重新进行复杂的解析操作,这样可以提高效率。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).// 当只是进行类型检查时(即尚未创建实际实例),请使用指定的临时类加载器(例如在weaving场景中)。// 我翻了一下setTempClassLoader引用的位置,发现这里的tempClassLoader和getBeanClassLoader()应该是同一个。ClassLoader tempClassLoader = getTempClassLoader();if (tempClassLoader != null) {dynamicLoader = tempClassLoader;freshResolve = true;if (tempClassLoader instanceof DecoratingClassLoader dcl) {for (Class<?> typeToMatch : typesToMatch) {dcl.excludeClass(typeToMatch.getName());}}}}String className = mbd.getBeanClassName();//if (className != null) {Object evaluated = evaluateBeanDefinitionString(className, mbd); // evaluated 翻译过来是评估的意思,if (!className.equals(evaluated)) {// A dynamically resolved expression, supported as of 4.2...if (evaluated instanceof Class<?> clazz) {return clazz;}else if (evaluated instanceof String str) {className = str;freshResolve = true;}else {throw new IllegalStateException("Invalid class name expression result: " + evaluated);}}if (freshResolve) {// When resolving against a temporary class loader, exit early in order// to avoid storing the resolved Class in the bean definition.if (dynamicLoader != null) {try {return dynamicLoader.loadClass(className);}catch (ClassNotFoundException ex) {if (logger.isTraceEnabled()) {logger.trace("Could not load class [" + className + "] from " + dynamicLoader + ": " + ex);}}}return ClassUtils.forName(className, dynamicLoader);}}// Resolve regularly, caching the result in the BeanDefinition...return mbd.resolveBeanClass(beanClassLoader);
}
其他classloader
其他的classloader由于我使用debug没有断掉,所以这里先跳过,回头再看
