Object evaluated = evaluateBeanDefinitionString(className, mbd);
if(!className.equals(evaluated)) {
// A dynamically resolved expression, supported as of 4.2...
if(evaluated instanceofClass<?> clazz) {
returnclazz;
}
elseif(evaluated instanceofString str) {
className = str;
freshResolve = true;
}
else{
thrownewIllegalStateException( "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) {
returndynamicLoader.loadClass(className);
}
returnClassUtils.forName(className, dynamicLoader);
}
}
// Resolve regularly, caching the result in the BeanDefinition...
returnmbd.resolveBeanClass(beanClassLoader);
【Spring容器原始Bean是如何创建的?】}
按理说,根据我们配置的类的全路径加载出来一个 Class 应该是非常容易的,直接 Class.forName 就可以了 。
但是!!!
如果对 Spring 用法比较熟悉的小伙伴就知道,配置 Class 全路径的时候,我们不仅可以像下面这样老老实实配置:
< beanclass= "org.JAVAboy.bean.Book"/>
我们甚至可以使用 SpEL 来配置 Bean 名称,例如我有如下类:
publicclassBeanNameUtils{
publicString getName{
return"org.javaboy.bean.User";
}
}
这里有一个 getName 方法,这个方法返回的是一个类的全路径,现在我们在 XML 文件中可以这样配置:
< beanclass= "org.javaboy.bean.BeanNameUtils"id= "beanNameUtils"/>
< beanclass= "#{beanNameUtils.name}"id= "user"/>
在 XML 的 class 属性中,我们可以直接使用 SpEL 去引用一个方法的执行,用该方法的返回值作为 class 的值 。
了解了 Spring 中的这个玩法,再去看上面的源码就很好懂了:
- 首先调用 mbd.getBeanClassName; 去获取到类路径 。
- 接下来调用 evaluateBeanDefinitionString 方法进行 SpEL 运算,这个运算的目的是为了解析 className 中的 SpEL 表达式,当然,一般情况下 className 就是一个普通的字符串,不是 SpEL 表达式,那么解析完成之后就还是原本的字符串 。如果是 className 是一个 SpEL,那么合法的解析结果分为两种:
- 首先就是解析之后拿到了一个 Class,那这个就是我们想要的结果,直接返回即可 。
- 要么就是解析出来是一个字符串,松哥上面举的例子就是这种情况,那么就把这个字符串赋值给 className,并且将 freshResolve 属性设置为 true,然后在接下来的 if 分支中去加载 Class 。
- 首先就是解析之后拿到了一个 Class,那这个就是我们想要的结果,直接返回即可 。
@Nullable
publicClass<?> resolveBeanClass( @NullableClassLoader classLoader) throwsClassNotFoundException {
String className = getBeanClassName;
if(className == null) {
returnnull;
}
Class<?> resolvedClass = ClassUtils.forName(className, classLoader);
this.beanClass = resolvedClass;
returnresolvedClass;
}
这个方法就相当直白了,根据 className 加载出来 Class 对象,然后给 beanClass 属性也设置上值,这就和一开始的 if (mbd.hasBeanClass) 对应上了 。
好了,到此,我们总算是根据 className 拿到 Class 对象了 。
2.2 Supplier 和 factory-method
好了,回到一开始的源码中,接下来该执行如下两行代码了:
Supplier<?> instanceSupplier = mbd.getInstanceSupplier;
if(instanceSupplier != null) {
returnobtainFromSupplier(instanceSupplier, beanName);
}
if(mbd.getFactoryMethodName != null) {
returninstantiateUsingFactoryMethod(beanName, mbd, args);
}
这两个松哥在前面的文章中和小伙伴们已经讲过了(Spring5 中更优雅的第三方 Bean 注入):前面的 obtainFromSupplier 方法是 Spring5 开始推出来的 Supplier,通过回调的方式去获取一个对象;第二个方法 instantiateUsingFactoryMethod 则是通过配置的 factory-method 来获取到一个 Bean 实例 。
对这两个方法不熟悉的小伙伴可以参考前面的文章:Spring5 中更优雅的第三方 Bean 注入 。
2.3 re-create 逻辑
继续回到一开始的源码中,接下来是一段 re-create 的处理逻辑,如下:
推荐阅读
- SpringBoot中使用Cache提升接口性能详解
- 通过Docker和Kubernetes实现容器化的自动伸缩
- 花生油的保存方法 花生油的保存方法用什么容器放花生油最好
- 杨梅泡酒要用什么酒 杨梅泡酒用什么容器比较好
- 微波炉热菜用什么器皿 用微波炉热菜用什么容器
- 简单起泡胶怎么做? 简单起泡胶怎么做
- 微波炉不能用什么容器 微波炉不能用什么容器加热饭菜
- 玻璃容器能放微波炉吗 玻璃容器能放微波炉吗安全吗
- 使用微波炉可以使用金属容器和空载吗 使用微波炉能不能用金属容器
- 空气炸锅能放什么容器 空气炸锅放什么容器最好