序言
在继续深入Spring的对象创建流程之前,这篇文章先简单介绍一下lookupMethod
标签的用法及作用。
准备的xml
自定义名为methodOverride.xml
的配置文件。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="apple" class="org.springframework.methodOverrides.lookup.Apple"></bean>
<bean id="banana" class="org.springframework.methodOverrides.lookup.Banana"></bean>
<bean id="fruitPlate1" class="org.springframework.methodOverrides.lookup.FruitPlate">
<lookup-method name="getFruit" bean="apple"/>
</bean>
<bean id="fruitPlate2" class="org.springframework.methodOverrides.lookup.FruitPlate">
<lookup-method name="getFruit" bean="banana"/>
</bean>
</beans>
Fruit类
public class Fruit {
public Fruit() {
System.out.println("I got Fruit");
}
}
Apple类
public class Apple extends Fruit {
public Apple() {
System.out.println("I got a fresh apple");
}
}
Banana类
public class Banana extends Fruit {
public Banana() {
System.out.println("I got a fresh bananer");
}
}
FruitPlate
public abstract class FruitPlate{
// 抽象方法获取新鲜水果
public abstract Fruit getFruit();
}
测试类
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("methodOverride.xml");
FruitPlate fruitPlate1 = (FruitPlate) ac.getBean("fruitPlate1");
fruitPlate1.getFruit();
FruitPlate fruitPlate2 = (FruitPlate) ac.getBean("fruitPlate2");
fruitPlate2.getFruit();
}
运行结果
我们来看看配置的<lookup-method>标签
是如何进行的区分和覆盖。
lookup-method标签
我们在FruitPlate
类中调用getFruit
方法会返回Fruit
类,而我们在lookup-method
标签中配置调用getFruit
方法会返回Apple
类。
所以我们这次从FruitPlate
对象的创建入手,看看整个流程中做了什么。
FruitPlate的创建
依然是 getBean ——》 doGetBean ——》 createBean 的整体流程的逻辑,我们直接看createBean
。
createBean
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
//删除无用代码
RootBeanDefinition mbdToUse = mbd;
mbdToUse.prepareMethodOverrides();
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
return beanInstance;
}
prepareMethodOverrides
如果bean对象配置了lookup-method、replace-method标签,说明要进行方法覆盖操作,设置标志位。
public void prepareMethodOverrides() throws BeanDefinitionValidationException {
// Check that lookup methods exist and determine their overloaded status.
//methodOverrides变量不为null,说明有方法覆盖
if (hasMethodOverrides()) {
//设置methodOverrides标记位
getMethodOverrides().getOverrides().forEach(this::prepareMethodOverride);
}
}
protected void prepareMethodOverride(MethodOverride mo) throws BeanDefinitionValidationException {
int count = ClassUtils.getMethodCountForName(getBeanClass(), mo.getMethodName());
if (count == 0) {
}
else if (count == 1) {
// Mark override as not overloaded, to avoid the overhead of arg type checking.
//标记overload为未重载,避免arg类型检查开销
mo.setOverloaded(false);
}
}
doCreateBean
调用createBeanInstance
方法取bean的策略实例。
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
//去掉无用代码
// Instantiate the bean.
//这个beanWrapper是用来持有创建出来的bean对象的
BeanWrapper instanceWrapper = null;
//如果是单例对象,从factoryBeanInstanceCache缓存中移除该信息
if (mbd.isSingleton()) {
// 如果是单例对象,从factoryBean实例缓存中移除当前bean定义信息
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
// 没有就创建实例
if (instanceWrapper == null) {
// 根据执行bean使用对应的策略创建新的实例,如,工厂方法,构造函数主动注入、简单初始化
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
return exposedObject;
}
createBeanInstance
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
// 去除无用代码
// 使用默认无参构造函数创建对象,如果没有无参构造且存在多个有参构造且没有@AutoWired注解构造,会报错
return instantiateBean(beanName, mbd);
}
instantiateBean
忽略securityManager
,getInstantiationStrategy
中创建并返回了CglibSubclassingInstantiationStrategy
对象。
protected BeanWrapper instantiateBean(String beanName, RootBeanDefinition mbd) {
try {
Object beanInstance;
if (System.getSecurityManager() != null) {
beanInstance = AccessController.doPrivileged(
(PrivilegedAction<Object>) () -> getInstantiationStrategy().instantiate(mbd, beanName, this),
getAccessControlContext());
}
else {
// 获取实例化策略并且进行实例化操作
beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, this);
}
// 包装成BeanWrapper
BeanWrapper bw = new BeanWrapperImpl(beanInstance);
initBeanWrapper(bw);
return bw;
}
}
getInstantiationStrategy
方法创建了一个CglibSubclassingInstantiationStrategy
类型对象。使用Cglib形式的动态代理创建对象。
private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();
protected InstantiationStrategy getInstantiationStrategy() {
return this.instantiationStrategy;
}
而我们的CglibSubclassingInstantiationStrategy
中就有对应的变量用来进行标记。
/**
* 如果没有override方法覆盖的话,那么索引位置为0
*
* Index in the CGLIB callback array for passthrough behavior,
* in which case the subclass won't override the original class.
*/
private static final int PASSTHROUGH = 0;
/**
* 如果有lookup-method的覆盖,那么索引位置为1
*
* Index in the CGLIB callback array for a method that should
* be overridden to provide <em>method lookup</em>.
*/
private static final int LOOKUP_OVERRIDE = 1;
/**
* Index in the CGLIB callback array for a method that should
* be overridden using generic <em>method replacer</em> functionality.
*
* 如果有replace-method的覆盖,那么索引位置为2
*/
private static final int METHOD_REPLACER = 2;
instantiate
因为我们配置了lookup-method
标签,所以会有方法覆盖,此时hasMethodOverrides()
方法会返回true,取反之后走else。
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
// Don't override the class with CGLIB if no overrides.
if (!bd.hasMethodOverrides()) {
//去除无用代码
return BeanUtils.instantiateClass(constructorToUse);
}
else {
// Must generate CGLIB subclass.
// 必须生成CGLIB子类
return instantiateWithMethodInjection(bd, beanName, owner);
}
}
此时我们将要加载的bean就是xml文件中配置的FruitPlate
,methodOverrides
变量存放的就是我们想要进行覆盖的getFruit()
instantiateWithMethodInjection
而因为我们创建了CglibSubclassingInstantiationStrategy
类型对象,所以此时的instantiateWithMethodInjection
方法的实现是CglibSubclassingInstantiationStrategy
的方法。
protected Object instantiateWithMethodInjection(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
@Nullable Constructor<?> ctor, Object... args) {
// Must generate CGLIB subclass...
// 必须生成一个cglib的子类
return new CglibSubclassCreator(bd, owner).instantiate(ctor, args);
}
封装beanDefinition
和BeanFactory
到CglibSubclassCreator
对象中,类中包含3种方法的CALLBACK_TYPES
。
private static final Class<?>[] CALLBACK_TYPES = new Class<?>[]
{NoOp.class, LookupOverrideMethodInterceptor.class, ReplaceOverrideMethodInterceptor.class};
CglibSubclassCreator(RootBeanDefinition beanDefinition, BeanFactory owner) {
this.beanDefinition = beanDefinition;
this.owner = owner;
}
instantiate
此时,instance实例就是我们通过Cglib创建的FruitPlate
对象,并设置callBacks属性值。
public Object instantiate(@Nullable Constructor<?> ctor, Object... args) {
Class<?> subclass = createEnhancedSubclass(this.beanDefinition);
Object instance;
if (ctor == null) {
instance = BeanUtils.instantiateClass(subclass);
}
else {
try {
Constructor<?> enhancedSubclassConstructor = subclass.getConstructor(ctor.getParameterTypes());
instance = enhancedSubclassConstructor.newInstance(args);
}
}
Factory factory = (Factory) instance;
factory.setCallbacks(new Callback[] {NoOp.INSTANCE,
new LookupOverrideMethodInterceptor(this.beanDefinition, this.owner),
new ReplaceOverrideMethodInterceptor(this.beanDefinition, this.owner)});
return instance;
}
此时要返回的instance
就是通过Cglib创建的FruitPlate
对象,并且callback属性中也设置了LookupOverrideMethodInterceptor
用来进行方法的拦截。
createEnhancedSubclass
Cglib规定写法,创建Enhancer
对象并设置callBack
属性。
private Class<?> createEnhancedSubclass(RootBeanDefinition beanDefinition) {
//cglib规定用法,对原始class进行增强,并设置callback
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(beanDefinition.getBeanClass());
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
if (this.owner instanceof ConfigurableBeanFactory) {
ClassLoader cl = ((ConfigurableBeanFactory) this.owner).getBeanClassLoader();
enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(cl));
}
//过滤,自定义逻辑来指定调用的callback下标
enhancer.setCallbackFilter(new MethodOverrideCallbackFilter(beanDefinition));
//只是产生class就直接指定callback类型,跟上面指定的callbackFilter对应
enhancer.setCallbackTypes(CALLBACK_TYPES);
return enhancer.createClass();
}
}
创建好了FruitPlate
对象后,main方法中再次调用fruitPlate1.getFruit();
方法。
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("methodOverride.xml");
FruitPlate fruitPlate1 = (FruitPlate) ac.getBean("fruitPlate1");
fruitPlate1.getFruit();
FruitPlate fruitPlate2 = (FruitPlate) ac.getBean("fruitPlate2");
fruitPlate2.getFruit();
}
通过索引LOOKUP_OVERRIDE
和LookupOverrideMethodInterceptor
对<lookup-method>
标签中配置的方法进行拦截。
获取 lo 所对应的 BeanName,调用getBean()
进行Bean实例的创建,从而调用Apple
类中的getFruit()
方法,从而实现覆盖
public Object intercept(Object obj, Method method, Object[] args, MethodProxy mp) throws Throwable {
// Cast is safe, as CallbackFilter filters are used selectively.
LookupOverride lo = (LookupOverride) getBeanDefinition().getMethodOverrides().getOverride(method);
Object[] argsToUse = (args.length > 0 ? args : null); // if no-arg, don't insist on args at all
if (StringUtils.hasText(lo.getBeanName())) {
return (argsToUse != null ? this.owner.getBean(lo.getBeanName(), argsToUse) :
this.owner.getBean(lo.getBeanName()));
}
else {
return (argsToUse != null ? this.owner.getBean(method.getReturnType(), argsToUse) :
this.owner.getBean(method.getReturnType()));
}
}
然而,lookup-method的作用和效果远非如此,我们此时稍稍改动一下配置文件。将Apple的scope改为prototype。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="apple" class="org.springframework.methodOverrides.lookup.Apple" scope="prototype"></bean>
<bean id="fruitPlate1" class="org.springframework.methodOverrides.lookup.FruitPlate">
<lookup-method name="getFruit" bean="apple"/>
</bean>
</beans>
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("methodOverride.xml");
FruitPlate fruitPlate1 = (FruitPlate) ac.getBean("fruitPlate1");
Apple a1 =(Apple) fruitPlate1.getFruit();
FruitPlate fruitPlate2 = (FruitPlate) ac.getBean("fruitPlate1");
Apple a2 = (Apple) fruitPlate2.getFruit();
System.out.println(a1 == a2);
}
修改完配置文件和测试类后,此时的 a1 != a2,会输出 false。
原因在于,当lookup-method
标签生效调用getFruit()
进行拦截时走getBean()
方法创建对象时,因为此时Apple的scope = prototype,所以singletonObjects
缓存中不会缓存创建好的实例,每次都会重新创建,所以每次获取的都是新的对象。
所以lookup-method最重要的作用是利用了索引(LOOKUP_OVERRIDE)和拦截器(LookupOverrideMethodInterceptor)解决了单例引用原型的问题。