MethodInjection 动态方法替换原理

本文探讨了Spring框架中singleton与prototype作用域的关系,并介绍了如何通过lookup method和replace method解决依赖注入问题,同时展示了如何利用CGLIB实现方法级别的依赖注入。
摘要由CSDN通过智能技术生成
[size=medium]singleton实例依赖于prototype实例,ProtoType含义是无效的,必须钩入向Spring容器请求,也就是调用getBean方法。换句话说,我们必须改变依赖关系的注入。[/size]

[img]http://dl2.iteye.com/upload/attachment/0086/0504/c7ba82d0-3e2d-3db4-8542-2f250108be1a.jpg[/img]

[size=medium]
例如A的scope是 Singleton ,B的scope是Prototype,当A依赖B时,B实例会在第一次初始化请求创建,之后的对A的调用间接依赖的B仍旧是同一实例,这并非B所声明的scope=Prototype含义。[/size]
// a class that uses a stateful Command-style class to perform some processing
package fiona.apple;

// Spring-API imports
import org.springframework.beans.BeansException;
import org.springframework.context.Applicationcontext;
import org.springframework.context.ApplicationContextAware;

public class CommandManager implements ApplicationContextAware {

private ApplicationContext applicationContext;

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

protected Command createCommand() {
// notice the Spring API dependency!
return this.applicationContext.getBean("command", Command.class);
}

public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.applicationContext = applicationContext;
}
}

[size=medium]上述createCommand方法里调用了getBean方法改变了依赖注入方式。但是还有一点“瑕疵”:依赖了ApplicationContext。这种任务Spring也提供了,因为诞生初衷就是为了减少代码的耦合程度,所以有了后来的控制反转。Look up method解决了这个问题。只要对每个要使用getBean代码的方法配置一个Bean(不支持一个方法对应多个Bean),注意替换的方法是无参的[/size]
<!-- a stateful bean deployed as a prototype (non-singleton) -->
<bean id="command" class="fiona.apple.AsyncCommand" scope="prototype">
<!-- inject dependencies here as required -->
</bean>

<!-- commandProcessor uses statefulCommandHelper -->
<bean id="commandManager" class="fiona.apple.CommandManager">
<lookup-method name="createCommand" bean="command"/>
</bean>

[size=medium]现在代码不再依赖于Spring[/size]
package fiona.apple;

// no more Spring imports!

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();
}


[size=medium]很少使用但很强大的同宗replace method
lookup method只是替换了getBean这样的简单代码,而replace method它将深入到指定bean之后的方法调用中,即MethodReplacer的方法reimplement,这里可以做更多的事,显然它支持并且一定比较可能重载的方法。[/size]

/** meant to be used to override the existing computeValue(String)
implementation in MyValueCalculator
*/
public class ReplacementComputeValue implements MethodReplacer {

public Object reimplement(Object o, Method m, Object[] args) throws Throwable {
// get the input value, work with it, and return a computed result
String input = (String) args[0];
...
return ...;
}
}


<bean id="myValueCalculator" class="x.y.z.MyValueCalculator">

<!-- arbitrary method replacement -->
<replaced-method name="computeValue" replacer="replacementComputeValue">
<arg-type>String</arg-type>
</replaced-method>
</bean>

<bean id="replacementComputeValue" class="a.b.c.ReplacementComputeValue"/>


[size=medium]实现
之前提到过AbstractBeanFactory,其子类AbstractAutowireCapableBeanFactory实现了instantiateBean方法[/size]
[img]http://dl2.iteye.com/upload/attachment/0086/0510/5c3b2d07-f714-302f-8e46-9f9d43d490c2.jpg[/img]

[size=medium]再进入SimpleInstantiationStrategy,可以看到如果有方法覆盖将调用instantiateWithMethodInjection,否则使用反射机制实例化[/size]
public Object instantiate(RootBeanDefinition beanDefinition, String beanName, BeanFactory owner) {
// Don't override the class with CGLIB if no overrides.
if (beanDefinition.getMethodOverrides().isEmpty()) {
Constructor<?> constructorToUse;
synchronized (beanDefinition.constructorArgumentLock) {
constructorToUse = (Constructor<?>) beanDefinition.resolvedConstructorOrFactoryMethod;
if (constructorToUse == null) {
final Class clazz = beanDefinition.getBeanClass();
if (clazz.isInterface()) {
throw new BeanInstantiationException(clazz, "Specified class is an interface");
}
try {
if (System.getSecurityManager() != null) {
constructorToUse = AccessController.doPrivileged(new PrivilegedExceptionAction<Constructor>() {
public Constructor run() throws Exception {
return clazz.getDeclaredConstructor((Class[]) null);
}
});
}
else {
constructorToUse = clazz.getDeclaredConstructor((Class[]) null);
}
beanDefinition.resolvedConstructorOrFactoryMethod = constructorToUse;
}
catch (Exception ex) {
throw new BeanInstantiationException(clazz, "No default constructor found", ex);
}
}
}
return BeanUtils.instantiateClass(constructorToUse);
}
else {
// Must generate CGLIB subclass.
return instantiateWithMethodInjection(beanDefinition, beanName, owner);
}
}

[size=medium]SimpleInstantiationStrategy子类CglibSubclassingInstantiationStrategy实现了instantiateWithMethodInjection方法,显然method注入的两种方式都是使用Cglib实现的。
setSuperclass:设置要代理的父类
setCallbackFilter:设置过滤器,有一个accept方法返回拦截器的索引值:
lookup method 该方法使用lookup method
replace method 改方法使用replace method
not found 不对方法拦截
setCallbacks:设置拦截器[/size]
public Object instantiate(Constructor ctor, Object[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.beanDefinition.getBeanClass());
enhancer.setCallbackFilter(new CallbackFilterImpl());
enhancer.setCallbacks(new Callback[] {
NoOp.INSTANCE,
new LookupOverrideMethodInterceptor(),
new ReplaceOverrideMethodInterceptor()
});

return (ctor == null) ?
enhancer.create() :
enhancer.create(ctor.getParameterTypes(), args);
}

[size=medium]lookup method拦截[/size]
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);
return owner.getBean(lo.getBeanName());
}
}

[size=medium]replace method拦截[/size]
private class ReplaceOverrideMethodInterceptor extends CglibIdentitySupport implements MethodInterceptor {

public Object intercept(Object obj, Method method, Object[] args, MethodProxy mp) throws Throwable {
ReplaceOverride ro = (ReplaceOverride) beanDefinition.getMethodOverrides().getOverride(method);
// TODO could cache if a singleton for minor performance optimization
MethodReplacer mr = (MethodReplacer) owner.getBean(ro.getMethodReplacerBeanName());
return mr.reimplement(obj, method, args);
}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值