java.lang.reflect.Proxy 、java.lang.reflect.InvocationHandler
不知道怎么去解释了,如果使用Proxy 获取一个类的实例,那么在调用这个类的方法前会先执行InvocationHandler 的invoke方法,那么我们就可以利用这个特性来实现自己的AOP了,下面的例子将介绍如何实现Spring 中AOP的前通知、后通知和环绕通知。实现思路如下:
1.DynaProxy动态代理类实现接口InvocationHandler,getBean方法 返回要代理的类,(这个类必须是基于接口的),invoke方法被代理的类执行其方法前一定要调用的方法,在这个方法里面可以在控制被调用的方法是否执行,或者做点其他事情。写日志,性能计算,权限控制等,setMethodAdvice方法,绑定自己定义的通知类。
2.MethodAdvice,加载自己定义的消息通知。目前支持前通知,后通知,和环绕通知,要实现这个三个功能只需要实现对应的接口,(^_^这些接口也是我自己定义的),然后通用方法AddAdvice加载进去,这里支持多个消息通知,对于多个消息通知执行的顺序是环绕通知,前通知,后通知。
3.定义了四个接口Advice、AroundAdvice、BeforeAdvice、AfterAdvice
4.实现3个Advice的实现类,分别对应环绕通知,前通知,后通知。
5.实现自己的消息通知类MyAdvice
6.编写测试类Test
下面看具体的代码实现
- /**
- *
- */
- packagecom.advice;
- importjava.lang.reflect.InvocationHandler;
- importjava.lang.reflect.Method;
- importjava.lang.reflect.Proxy;
- /**
- *@authorzjb
- *
- */
- publicclassDynaProxyimplementsInvocationHandler{
- privateMethodAdvicemethodAdvice;
- privateObjectdelegate;
- @Override
- publicObjectinvoke(Objectporxy,Methodmethod,Object[]args)
- throwsThrowable{
- methodAdvice.setArgs(args);
- methodAdvice.setMethod(method);
- methodAdvice.setProxy(this);
- methodAdvice.setPos(-1);
- methodAdvice.setDone(false);
- returnmethodAdvice.proceed();
- }
- publicObjectgetBean(StringclassName){
- try{
- delegate=Class.forName(className).newInstance();
- methodAdvice.setTarget(delegate);
- returnProxy.newProxyInstance(
- delegate.getClass().getClassLoader(),
- delegate.getClass().getInterfaces(),this);
- }catch(Exceptione){
- e.printStackTrace();
- }
- returnnull;
- }
- publicMethodAdvicegetMethodAdvice(){
- returnmethodAdvice;
- }
- publicvoidsetMethodAdvice(MethodAdvicemethodAdvice){
- this.methodAdvice=methodAdvice;
- }
- }
- packagecom.advice;
- importjava.lang.reflect.InvocationTargetException;
- importjava.lang.reflect.Method;
- importjava.util.ArrayList;
- importjava.util.List;
- importcom.advice.impl.AfterAdviceImpl;
- importcom.advice.impl.AroundAdviceImpl;
- importcom.advice.impl.BeforeAdviceImpl;
- publicclassMethodAdvice{
- privateList<Advice>adviceList=newArrayList<Advice>();
- privateObjecttarget;
- privateMethodmethod;
- privateObject[]args;
- privateintpos=-1;
- privatebooleandone;
- privateDynaProxyproxy;
- publicvoidAddAdvice(Object[]advices){
- adviceList.clear();
- List<Advice>aroundAdvice=newArrayList<Advice>();
- List<Advice>beforeAdvice=newArrayList<Advice>();
- List<Advice>afterAdvice=newArrayList<Advice>();
- for(Objecto:advices){
- if(oinstanceofAroundAdvice){
- AroundAdviceImpltemp=newAroundAdviceImpl((AroundAdvice)o);
- aroundAdvice.add(temp);
- }
- if(oinstanceofBeforeAdvice){
- BeforeAdviceImplba=newBeforeAdviceImpl((BeforeAdvice)o);
- beforeAdvice.add(ba);
- }
- if(oinstanceofAfterAdvice){
- AfterAdviceImplaa=newAfterAdviceImpl((AfterAdvice)o);
- afterAdvice.add(aa);
- }
- }
- adviceList.addAll(aroundAdvice);
- adviceList.addAll(beforeAdvice);
- adviceList.addAll(afterAdvice);
- }
- publicObjectproceed(){
- pos++;
- if(pos>=adviceList.size()&&!done){
- returninvoke();
- }
- Objecto=null;
- //如果执行到后通知了,那就去执行目标方法
- if(!done&&adviceList.get(pos)instanceofAfterAdvice){
- o=invoke();
- }
- adviceList.get(pos).invoke(this);
- returno;
- }
- privateObjectinvoke(){
- done=true;
- try{
- returnmethod.invoke(target,args);
- }catch(IllegalArgumentExceptione){
- //TODOAuto-generatedcatchblock
- e.printStackTrace();
- }catch(IllegalAccessExceptione){
- //TODOAuto-generatedcatchblock
- e.printStackTrace();
- }catch(InvocationTargetExceptione){
- //TODOAuto-generatedcatchblock
- e.printStackTrace();
- }
- returnnull;
- }
- publicDynaProxygetProxy(){
- returnproxy;
- }
- publicvoidsetProxy(DynaProxyproxy){
- this.proxy=proxy;
- }
- publicvoidsetTarget(Objecttarget){
- this.target=target;
- }
- publicvoidsetArgs(Object[]args){
- this.args=args;
- }
- publicvoidsetMethod(Methodmethod){
- this.method=method;
- }
- publicObjectgetTarget(){
- returntarget;
- }
- publicMethodgetMethod(){
- returnmethod;
- }
- publicObject[]getArgs(){
- returnargs;
- }
- publicintgetPos(){
- returnpos;
- }
- publicvoidsetPos(intpos){
- this.pos=pos;
- }
- publicbooleanisDone(){
- returndone;
- }
- publicvoidsetDone(booleandone){
- this.done=done;
- }
- }
- packagecom.advice;
- publicinterfaceAdvice{
- publicObjectinvoke(MethodAdvicemethodAdvice);
- }
- packagecom.advice;
- publicinterfaceAroundAdviceextendsAdvice{
- }
- packagecom.advice;
- importjava.lang.reflect.Method;
- publicinterfaceBeforeAdvice{
- publicvoidbefore(Methodm,Object[]args,Objecttarget);
- }
- packagecom.advice;
- importjava.lang.reflect.Method;
- publicinterfaceAfterAdvice{
- publicvoidafter(Methodm,Object[]args,Objecttarget);
- }
- packagecom.advice.impl;
- importcom.advice.Advice;
- importcom.advice.MethodAdvice;
- publicclassAroundAdviceImplimplementsAdvice{
- privateAdviceadvice;
- @SuppressWarnings("unused")
- privateAroundAdviceImpl(){};
- publicAroundAdviceImpl(Adviceadvice){
- this.advice=advice;
- }
- @Override
- publicObjectinvoke(MethodAdvicemethodAdvice){
- returnadvice.invoke(methodAdvice);
- }
- }
- packagecom.advice.impl;
- importcom.advice.Advice;
- importcom.advice.BeforeAdvice;
- importcom.advice.MethodAdvice;
- publicclassBeforeAdviceImplimplementsAdvice{
- privateBeforeAdvicebeforeAdvice;
- @SuppressWarnings("unused")
- privateBeforeAdviceImpl(){
- }
- publicBeforeAdviceImpl(BeforeAdvicebeforeAdvice){
- this.beforeAdvice=beforeAdvice;
- }
- @Override
- publicObjectinvoke(MethodAdvicemethodAdvice){
- beforeAdvice.before(methodAdvice.getMethod(),methodAdvice.getArgs(),methodAdvice.getTarget());
- returnmethodAdvice.proceed();
- }
- }
- packagecom.advice.impl;
- importcom.advice.Advice;
- importcom.advice.AfterAdvice;
- importcom.advice.MethodAdvice;
- publicclassAfterAdviceImplimplementsAdvice{
- privateAfterAdviceafterAdvice;
- @SuppressWarnings("unused")
- privateAfterAdviceImpl(){}
- publicAfterAdviceImpl(AfterAdviceafterAdvice){
- this.afterAdvice=afterAdvice;
- }
- @Override
- publicObjectinvoke(MethodAdvicemethodAdvice){
- ObjectretVal=methodAdvice.proceed();
- afterAdvice.after(methodAdvice.getMethod(),methodAdvice.getArgs(),methodAdvice.getTarget());
- returnretVal;
- }
- }
- packagecom.myadvice.test;
- importjava.lang.reflect.Method;
- importjava.util.Arrays;
- importbsh.EvalError;
- importbsh.Interpreter;
- importcom.advice.AfterAdvice;
- importcom.advice.AroundAdvice;
- importcom.advice.BeforeAdvice;
- importcom.advice.MethodAdvice;
- publicclassMyAdviceimplementsBeforeAdvice,AfterAdvice,AroundAdvice{
- /**
- *后通知
- */
- @Override
- publicvoidafter(Methodm,Object[]args,Objecttarget){
- System.out.println("后通知,调用的方法:"+m.getName()+",参数:"+Arrays.toString(args));
- }
- /**
- *环绕通知,最维强大的通知,可以控制目标方法是否执行,也可以改变方法的返回值
- */
- @Override
- publicObjectinvoke(MethodAdvicemethodAdvice){
- System.out.println("[环绕通知]");
- /**
- *这里我们禁止李四发表任何的评论
- */
- if(methodAdvice.getMethod().getName().equals("comment")"李四".equals(methodAdvice.getArgs()[0])){
- System.out.println("屏蔽李四所有的评论");
- StringreturnType=methodAdvice.getMethod().getReturnType().getName();
- if("int".equals(returnType)||"long".equals(returnType)||"float".equals(returnType)||"double".equals(returnType)||"byte".equals(returnType)||"short".equals(returnType)){
- //利用BeanShell构造一个内置变量返回,这里想了好久,没有想到什么方法可以根据
- //指定数据类型返回指定的变量
- Interpreteri=newInterpreter();
- try{
- returni.eval("("+returnType+")0");
- }catch(EvalErrore){
- //TODOAuto-generatedcatchblock
- e.printStackTrace();
- }
- }elseif("boolean".equals(returnType)){
- returnfalse;
- }
- returnnull;
- }else{
- returnmethodAdvice.proceed();
- }
- }
- /**
- *前通知
- */
- @Override
- publicvoidbefore(Methodm,Object[]args,Objecttarget){
- System.out.println("前通知,调用的方法:"+m.getName()+",参数:"+Arrays.toString(args));
- }
- }
- packagecom.myadvice.test;
- importcom.advice.DynaProxy;
- importcom.advice.MethodAdvice;
- importcom.test.BookBiz;
- importcom.test.BookBizImpl;
- publicclassTest{
- /**
- *@paramargs
- */
- publicstaticvoidmain(String[]args){
- DynaProxyproxy=newDynaProxy();
- MethodAdvicemethodAdvice=newMethodAdvice();
- methodAdvice.AddAdvice(newObject[]{newMyAdvice()});
- proxy.setMethodAdvice(methodAdvice);
- BookBizbookBiz=(BookBiz)proxy.getBean(BookBizImpl.class.getName());
- bookBiz.buy("张三","Spring深入潜出",50);
- bookBiz.comment("李四","《恐怖世界》一点都不恐怖,很好看!");
- bookBiz.comment("张三","《Spring深入潜出》还是写得不错的!");
- }
- }
运行Test类得到下面的结果:
[环绕通知]
前通知,调用的方法:buy,参数:[张三, Spring深入潜出, 50.0]
业务方法buy开始执行
·张三购买图书:Spring深入潜出
·张三增加积分:5
·向物流系统下发货单
业务方法buy结束
后通知,调用的方法:buy,参数:[张三, Spring深入潜出, 50.0]
[环绕通知]
屏蔽李四所有的评论
[环绕通知]
前通知,调用的方法:comment,参数:[张三, 《Spring深入潜出》还是写得不错的!]
业务方法comment开始执行
·张三发表书评《Spring深入潜出》还是写得不错的!
业务方法comment结束
后通知,调用的方法:comment,参数:[张三, 《Spring深入潜出》还是写得不错的!]
从结果可以看出和使用spring的一样。
这个又个小小的问题,为什么spring里面前通知后通知的接口方法要包含Object object, Method method这两个参数,如果又这里两个参数那么就可以在前后通知里面去执行目标方法了,这样不是很好。还有就是使用jdk的代理,被代理的类都必须是基于接口的,重要也不是很方便,如果使用cglib动态代理类,那么被代理的类就可以不是基于接口的了,这样比较方便,下一篇将介绍如何使用cglib动态代理