科普:Spring中引入增强(IntroductionAdvice)的实现原理

Spring中有五种增强:BeforeAdvide(前置增强)、AfterAdvice(后置增强)、ThrowsAdvice(异常增强)、RoundAdvice(环绕增强)、IntroductionAdvice(引入增强)

RoundAdvice(环绕增强):就是BeforeAdvide(前置增强)、AfterAdvice(后置增强)的组合使用叫环绕增强。

前四种增强都比较简单,我们今天要介绍的是IntroductionAdvice(引入增强)的概念及原理。

引入增强(Introduction Advice)的概念:一个Java类,没有实现A接口,在不修改Java类的情况下,使其具备A接口的功能。

1.Cglib实现引入增强

记住,我的目的不是告诉你怎么在Spring中使用引入增强功能(这不是我的风格),而是探究引入增强功能的底层实现原理。

public interface IHello {

 public void sayHello();
}

上面是接口功能,CeremonyService是需要增强的类,在不改变CeremonyService类的情况下,使其具备IHello接口功能。

public class CeremenyService {

 public void sayBye() {
 System.out.println("Say bye from Ceremeny.");
 }

}

看起来要像下面这样:

CeremenyService cs;

IHello ih = (IHello) cs;
ih.sayHello();

即,CeremenyService居然变成了IHello类型。

我们编写一个重要的拦截器,来实现此功能。

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import x.y.IHello;

public class IntroInterceptor implements MethodInterceptor, IHello {
 // 实现了IHello增强接口的对象
 private Object delegate;

 public IntroInterceptor() {
 this.delegate = this;
 }

 public IntroInterceptor(Object delegate) {
 this.delegate = delegate;
 }

 @Override
 public void sayHello() {
 System.out.println("Say hello from delegate.");
 }

 @Override
 public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
 Class<?> clz = method.getDeclaringClass();
 if (clz.isAssignableFrom(IHello.class)) {
 // 如果实现了IHello增强接口,则调用实现类delegate的方法
 return method.invoke(delegate, args);
 }
 return methodProxy.invokeSuper(obj, args);
 }
}

我们来编写一个测试类。

public static void main(String[] args) {
 Enhancer en = new Enhancer();
 en.setSuperclass(CeremenyService.class);
 en.setInterfaces(new Class[] { IHello.class });
 en.setCallback(new IntroInterceptor());

 CeremenyService cs = (CeremenyService) en.create();
 cs.sayBye();

 IHello ih = (IHello) cs;
 ih.sayHello();
}

en.setInterfaces(new Class[] { IHello.class });非常重要,表示Cglib生成代理类,将要实现的接口集合。

于是生成的代理类Class,类似于:public class CeremenyService$$EnhancerByCGLIB$$86859be5 extends CeremenyService implements IHello

输出结果:

Say bye from Ceremeny.
Say hello from delegate.

这就是大名鼎鼎的引入增强(Introduction Advice)的底层实现原理。

2. Spring framework引入增强源码解读

Spring的xml文件配置。

<bean id="ceremonyService" class="x.y.service.CeremonyService" />
<bean id="ceremonyIntroAdvice" class="x.y.advice.CeremonyIntroAdvice" />

<bean id="ceremonyProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
 <property name="interfaces" value="x.y.IHello"/> <!-- 需要动态实现的接口 -->
 <property name="target" ref="ceremonyService"/> <!-- 目标类 -->
 <property name="interceptorNames" value="ceremonyIntroAdvice"/> <!-- 引入增强 -->
 <property name="proxyTargetClass" value="true"/> <!-- 代理目标类(默认为 false,代理接口) -->
</bean>

我们需要自定义一个拦截器。

import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.support.DelegatingIntroductionInterceptor;

import x.y.IHello;

@SuppressWarnings("serial")
public class CeremonyIntroAdvice extends DelegatingIntroductionInterceptor implements IHello {

 @Override
 public Object invoke(MethodInvocation mi) throws Throwable {
 return super.invoke(mi);
 }

 @Override
 public void sayHello() {
 System.out.println("Say hello.");
 }

}

在Spring中,要实现引入增强,需要继承自DelegatingIntroductionInterceptor。

下面看看该DelegatingIntroductionInterceptor类的invoke()方法源码。

@Override
 public Object invoke(MethodInvocation mi) throws Throwable {
 // 检测是否是引入增强
 if (isMethodOnIntroducedInterface(mi)) {
 // 执行实现了引入增强接口的delegate对象的增强方法
 Object retVal = AopUtils.invokeJoinpointUsingReflection(this.delegate, mi.getMethod(), mi.getArguments());

 // Massage return value if possible: if the delegate returned itself,
 // we really want to return the proxy.
 if (retVal == this.delegate && mi instanceof ProxyMethodInvocation) {
 Object proxy = ((ProxyMethodInvocation) mi).getProxy();
 if (mi.getMethod().getReturnType().isInstance(proxy)) {
 retVal = proxy;
 }
 }
 return retVal;
 }

 return doProceed(mi);
 }

AopUtils.invokeJoinpointUsingReflection()方法内部,其实就是反射方法调用。

try {
 ReflectionUtils.makeAccessible(method);
 return method.invoke(target, args);
}

最后写一个测试方法,来测试一下。

public static void main(String[] args) {

 FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext(
 "D:/workspace/Spring4.2.5/bin/applicationContext.xml");

 CeremonyService service = context.getBean("ceremonyProxy", CeremonyService.class);
 service.sayBye();

 IHello hello = (IHello) service;
 hello.sayHello();

 context.close();
}

输出:

Say bye.
Say hello.		

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值