博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring中引入增强(IntroductionAdvice)的底层实现原理
阅读量:6884 次
发布时间:2019-06-27

本文共 4534 字,大约阅读时间需要 15 分钟。

hot3.png

作为一名程序员,舆论一直在告诫我,要有自己的博客,要记录并懂得和别人分享你的技术心得。然而,遗憾的是,每当我有所收获,并为之兴奋,准备下笔要和别人分享之时,我蓦然发现,已经有N位程序员同仁,在N年前,就已经记录过并分享过无数多次该技术要点了。此时,我又何必多此一举的弄一个N+1呢?多年过去,依旧是白纸一张。

不过,在等待多年后,我已经按耐不住了,我的机会终于来了,这一次,无论外面如何春暖花开,如何艳阳高照,如何美女如云,我都视之如粪土。现在,我要做的是,与君分享技术心得。

Let's Go(来,死狗).

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文件配置。

        
                   
        
                    
        
    
        
                   

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

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.

总结:介绍如何使用的文章比较多,而介绍原理性的文章少一些,我比较喜欢介绍原理性的文章。希望本篇博文,对您有用。

版权提示:文章出自开源中国社区,若对文章感兴趣,可关注我的开源中国社区(http://my.oschina.net/zudajun)。(经过网络爬虫或转载的文章,经常丢失流程图、时序图,格式错乱等,还是看原版的比较好)

转载于:https://my.oschina.net/zudajun/blog/663962

你可能感兴趣的文章
简单入门Buffer
查看>>
OO第四阶段总结
查看>>
javascript总结02
查看>>
创建windows服务
查看>>
HTML5 入门基础
查看>>
【转载】读懂IL代码就这么简单(二)
查看>>
C++文件操作(fstream)
查看>>
用main函数传参做简单的计算器的代码
查看>>
python中struct.unpack的用法
查看>>
体绘制(Volume Rendering)概述之4:光线投射算法(Ray Casting)实现流程和代码(基于CPU的实现)...
查看>>
Python实践之(七)逻辑回归(Logistic Regression)
查看>>
PAT (Advanced Level) 1107. Social Clusters (30)
查看>>
【开源社群系统研发日记五】ThinkSNS+ 是如何计算字符显示长度的
查看>>
Nodejs日志管理log4js
查看>>
python获取昨日日期
查看>>
海康威视 - 萤石云开放平台 js 版
查看>>
关于分销平台
查看>>
剑指offer---12-**--数值的整数次方
查看>>
PAT - L2-010. 排座位(并查集)
查看>>
Python 学习笔记 - 线程(线程锁,信标,事件和条件)
查看>>