3、Spring源码之lookup-method replace-method

问题抛出:

1、假如spring想在一个单例对象中引用原型对象如何实现呢?

我们都知道spring默认的对象都是单例对象、默认放到一级缓存中,假如一个单例Bean想要引用原型Bean, 就需要使用lookup-method 、replace-method.

注:源码跟踪示例使用配置文件方式 毕竟使用的方式千篇一律 有趣的原理才是万里挑一


源码跟踪:

提示:源码跟踪禁忌抠每一行代码 弄清源码功能整体的脉络思路就好

  1. doCreateBean方法里面调用一个方法prepareMethodOverrides.
    public void prepareMethodOverrides() throws BeanDefinitionValidationException {
    		// Check that lookup methods exist and determine their overloaded status.
    		if (hasMethodOverrides()) {
    			getMethodOverrides().getOverrides().forEach(this::prepareMethodOverride);
    		}
    }

    ①通过跟踪代码逻辑:当前Bean的定义信息是否有methodOverrides,其实就是指的是Bean是否有lookup-method标签和reoplace-method标签。进入到prepareMethodOverride这个方法我当时都懵了,也没有对这两个标签进行处理逻辑啊,这里就是做了一个标记。官方翻译[将重写标记为未重载,以避免arg类型检查的开销。] 说实话我没看懂什么意思 不影响这里脉络.  这里我的理解标记MethodOverride的overloaded的属性为false 方便Cglib方法拦截器MethodInterceptor去处理MethodOverride 。

    ②这里为什么又提到Cglib呢 因为如果一个Bean有lookup-method 或replace-method,spring首先将这个Bean生成代理对象 放到一级缓存,这个代理对象设置了三个方法拦截器分别是NoOp、LookupOverrideMethodInterceptor、LookupOverrideMethodInterceptor

    ③这里还要说明 比如这个代理对象 具体会执行那个方法拦截器呢? 这里就要用到Cglib的过滤器(setCallbackFilter)了 这里是MethodOverrideCallbackFilter,通过accept方法返回的int下标,用于指定调用哪个拦截器进行拦截处理。这里下标取值的方法 就是用到①里面的标记MethodOverride的overloaded的属性为false逻辑

  2. 实例化Bean的方法 instantiate
    @Override
    	public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
    		// Don't override the class with CGLIB if no overrides.
    		if (!bd.hasMethodOverrides()) {
    			Constructor<?> constructorToUse;
    			synchronized (bd.constructorArgumentLock) {
    				constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
    				if (constructorToUse == null) {
    					// 通过bd,即bean定义,拿到bean的class
    					final Class<?> clazz = bd.getBeanClass();
    					if (clazz.isInterface()) {
    						// 很显然,接口类型的bean是无法实例化的,不要问为什么,问了会显得你很无知
    						throw new BeanInstantiationException(clazz, "Specified class is an interface");
    					}
    					try {
    						if (System.getSecurityManager() != null) {
    							constructorToUse = AccessController.doPrivileged(
    									(PrivilegedExceptionAction<Constructor<?>>) clazz::getDeclaredConstructor);
    						}
    						else {
    							// 通过bean的class,拿到其public的无参构造器
    							constructorToUse = clazz.getDeclaredConstructor();
    						}
    						bd.resolvedConstructorOrFactoryMethod = constructorToUse;
    					}
    					catch (Throwable ex) {
    						// 如果没有指定默认public构造方法,那就jdk自动加上
    						// 如果指定了很多public构造方法,但是却没有无参的,就会报下面这个错
    						throw new BeanInstantiationException(clazz, "No default constructor found", ex);
    					}
    				}
    			}
    			// 下面就有意思了,通过bean工具类,将bean的默认共有无参构造器作为参数,去实例化bean
    			return BeanUtils.instantiateClass(constructorToUse);
    		}
    		else {
    			// Must generate CGLIB subclass.
    			return instantiateWithMethodInjection(bd, beanName, owner);
    		}
    	}

    判断Bean的定义信息是否有lookup-method 或replace-method标签 如果有 要生成代理对象放到一级缓存中

  3. 如何生成代理CglibSubclassingInstantiationStrategy.instantiate方法逻辑
    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);
    				}
    				catch (Exception ex) {
    					throw new BeanInstantiationException(this.beanDefinition.getBeanClass(),
    							"Failed to invoke constructor for CGLIB enhanced subclass [" + subclass.getName() + "]", ex);
    				}
    			}
    			// SPR-10785: set callbacks directly on the instance instead of in the
    			// enhanced class (via the Enhancer) in order to avoid memory leaks.
    			Factory factory = (Factory) instance;
    			factory.setCallbacks(new Callback[] {NoOp.INSTANCE,
    					new LookupOverrideMethodInterceptor(this.beanDefinition, this.owner),
    					new ReplaceOverrideMethodInterceptor(this.beanDefinition, this.owner)});
    			return instance;
    		}

    ①首先创建EnhancedClass对象 这个对象设置CallbackFilter过滤器 主要作用 代理对象调用的方法,用具体哪个方法拦截器处理

    ②生成代理对象 设置方法拦截器 NoOp、LookupOverrideMethodInterceptor、LookupOverrideMethodIntercepto

    ③ 代理对象放入一级缓存
     
  4. 方法拦截器具体做了什么呢?LookupOverrideMethodInterceptor举例
    		@Override
    		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);
    			Assert.state(lo != null, "LookupOverride not found");
    			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()));
    			}
    		}

    ①根据调用的方法对象 获取LookupOverride对象 
    ②根据配置lookup-method标签获取Bean的名称 到Spring容器中获取Bean你对象
    ③我们知道如果一个Bean是一个原型类型 那么spring是不向缓存放的 ,每次根据Bean的定义信息重新获取、从而达到单例对象引用原型

案例:

  • 配置文件
    	<bean id="apple" class="com.zhangfuyin.methodOverride.Apple" scope="prototype"/>
    	<bean id="fruitPlant" class="com.zhangfuyin.methodOverride.FruitPlant">
    		<lookup-method name="getFruit" bean="apple"/>
    	</bean>
  • java对象
    public class Apple extends Fruit{
    
    	public Apple(){
    		System.out.println("I got  fresh apple");
    	}
    
    
    }
    
    
    ==========================================================
    
    public class Fruit {
    
    	public Fruit(){
    		System.out.println("I got fruit");
    	}
    }
    
    ===========================================================
    
    public abstract  class FruitPlant {
    
    	public abstract Fruit getFruit();
    	
    }
    
    

  • 测试代码输出
    	public  static void main(String agrs[]){
    		ApplicationContext ac = new ClassPathXmlApplicationContext("classpath:methodoverride.xml");
    		FruitPlant fruitPlant1 = (FruitPlant)ac.getBean("fruitPlant");
    		System.out.println(fruitPlant1.getFruit().hashCode());
    		
    		
    		FruitPlant fruitPlant2 = (FruitPlant)ac.getBean("fruitPlant");
    		System.out.println(fruitPlant2.getFruit().hashCode());

    I got fruit
    I got  fresh apple
    1087072654
    I got fruit
    I got  fresh apple
    864221358
     

 可以看出 两次打印对象hashcode值不一样 从而单例对象引用原型模式

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值