`
dylan0514sina.cn
  • 浏览: 92329 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

MethodInjection 动态方法替换原理

 
阅读更多
singleton实例依赖于prototype实例,ProtoType含义是无效的,必须钩入向Spring容器请求,也就是调用getBean方法。换句话说,我们必须改变依赖关系的注入。




例如A的scope是 Singleton ,B的scope是Prototype,当A依赖B时,B实例会在第一次初始化请求创建,之后的对A的调用间接依赖的B仍旧是同一实例,这并非B所声明的scope=Prototype含义。

// 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;
 }
}

上述createCommand方法里调用了getBean方法改变了依赖注入方式。但是还有一点“瑕疵”:依赖了ApplicationContext。这种任务Spring也提供了,因为诞生初衷就是为了减少代码的耦合程度,所以有了后来的控制反转。Look up method解决了这个问题。只要对每个要使用getBean代码的方法配置一个Bean(不支持一个方法对应多个Bean),注意替换的方法是无参的
<!-- 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>

现在代码不再依赖于Spring
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();
}


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


实现
之前提到过AbstractBeanFactory,其子类AbstractAutowireCapableBeanFactory实现了instantiateBean方法



再进入SimpleInstantiationStrategy,可以看到如果有方法覆盖将调用instantiateWithMethodInjection,否则使用反射机制实例化
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);
		}
	}

SimpleInstantiationStrategy子类CglibSubclassingInstantiationStrategy实现了instantiateWithMethodInjection方法,显然method注入的两种方式都是使用Cglib实现的。
setSuperclass:设置要代理的父类
setCallbackFilter:设置过滤器,有一个accept方法返回拦截器的索引值:
lookup method  该方法使用lookup method
replace method 改方法使用replace method
not found 不对方法拦截
setCallbacks:设置拦截器
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);
		}

lookup method拦截
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());
			}			
		}

replace method拦截
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);
			}
		}
  • 大小: 6.3 KB
  • 大小: 54.4 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics