溯源 Uses CGLIB to generate subclasses dynamically if methods need to be overridden by the container

标题来自于CglibSubclassingInstantiationStrategy

看spring IOC 实现,bean-definition 实例化过程中,有这么个过程,根据override method 决定用Cglib 还是JDK 反射。

/**
 * Return an instance of the bean with the given name in this factory.
 * 根据MethodOverrides决定使用反射还是cglib 来实例化。why ???
 * /
public Object instantiate(RootBeanDefinition beanDefinition, String beanName, BeanFactory owner) {
    // Don't override the class with CGLIB if no overrides.
    if (beanDefinition.getMethodOverrides().isEmpty()) {
        // ...
        return BeanUtils.instantiateClass(constructorToUse);
    }
    else {
        // Must generate CGLIB subclass.
        return instantiateWithMethodInjection(beanDefinition, beanName, owner);
    }
}
.......

误会了,此MethodOverride 非 @Override.

......

那么,MethodOverride 来自哪里?

先看看MethodOverride 子类LookupOverride、ReplaceOverride。

这些类型对象会在解析spring配置xml 过程中构造, 参考BeanDefinitionParserDelegate.parseLookupOverrideSubElements()。

......

那么,为什么会有配置MethodOverride 的需求?

通过参考spring.io 的文档,确实有一种情景,需要执行方法中入参全新的instance 而不是singletons instance。直接上原文:

In most application scenarios, most beans in the container are singletons. When a singleton bean needs to collaborate with another singleton bean, or a non-singleton bean needs to collaborate with another non-singleton bean, you typically handle the dependency by defining one bean as a property of the other. A problem arises when the bean lifecycles are different. Suppose singleton bean A needs to use non-singleton (prototype) bean B, perhaps on each method invocation on A. The container only creates the singleton bean A once, and thus only gets one opportunity to set the properties. The container cannot provide bean A with a new instance of bean B every time one is needed.

solution 1. You can make bean A aware of the container by implementing the ApplicationContextAware interface, and by making a getBean("B") call to the container ask for (a typically new) bean B instance every time bean A needs it.

solution 2. Method Injection, a somewhat advanced feature of the Spring IoC container, allows this use case to be handled in a clean fashion.

......

那么动态的method 注入bean 是怎么实现?

直接上源码,强大的Cglib 用来实现动态method inject简直完美:

/**
 * Create a new instance of a dynamically generated subclass implementing the
 * required lookups.
 */
public Object instantiate(Constructor
   
    ctor, Object[] args) {
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(this.beanDefinition.getBeanClass());
    enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
    enhancer.setCallbackFilter(new CallbackFilterImpl()); // 决定是否执行动态method inject
    enhancer.setCallbacks(new Callback[] {
    		NoOp.INSTANCE,                                // case1: 不响应
    		new LookupOverrideMethodInterceptor(),        // case2: 执行动态method inject
    		new ReplaceOverrideMethodInterceptor()        // case3: 动态替换method
    });
    
    return (ctor != null ? enhancer.create(ctor.getParameterTypes(), args) : enhancer.create());
}

/**
 * CGLIB MethodInterceptor to override methods, replacing them with an
 * implementation that returns a bean looked up in the container.
 */
private class LookupOverrideMethodInterceptor extends CglibIdentitySupport implements MethodInterceptor {

    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) beanDefinition.getMethodOverrides().getOverride(method);
        // 方法调用时获取new bean
        return owner.getBean(lo.getBeanName());
    }
}
......

demo/example,怎么用?

public abstract class CommandManager {

 public Object process(Object commandState) {
    // grab a new instance of the appropriate Command interface
    Command command = createCommand();
    // set the state on the (hopefully brand new) Command instance
    command.setState(commandState);
    return command.execute();
 }

  // okay... but where is the implementation of this method???
 protected abstract Command createCommand();
}

    
    

    
    

     
     

    
    


    
    

    
    

     
     

    
    

总结:

关于method inject 的方式没有用过,首先是开阔了思路。如果真的遇到这个情景,想到的应该还是solution 1来实现,没想到spring 有如此巧妙的应用。
同时也开阔了使用cglib 的思路,应该是以后工具类或者架构设计的进阶积累。
spring 的源码自以为大致了解套路了,原来还有这样的clean fashion 的设计,应该更用心去读源码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值