AOP示例(再续)

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

下面看具体的代码实现

  1. /**
  2.  * 
  3.  */
  4. package com.advice;
  5. import java.lang.reflect.InvocationHandler;
  6. import java.lang.reflect.Method;
  7. import java.lang.reflect.Proxy;
  8. /**
  9.  * @author zjb
  10.  *
  11.  */
  12. public class DynaProxy implements InvocationHandler {
  13.     
  14.     private MethodAdvice methodAdvice;
  15.     private Object delegate;
  16.     @Override
  17.     public Object invoke(Object porxy, Method method, Object[] args)
  18.             throws Throwable {
  19.         methodAdvice.setArgs(args);
  20.         methodAdvice.setMethod(method);
  21.         methodAdvice.setProxy(this);
  22.         methodAdvice.setPos(-1);
  23.         methodAdvice.setDone(false);
  24.         return methodAdvice.proceed();
  25.     }
  26.     public Object getBean(String className){
  27.         try {
  28.             delegate=Class.forName(className).newInstance();
  29.             methodAdvice.setTarget(delegate);
  30.             return Proxy.newProxyInstance(
  31.                     delegate.getClass().getClassLoader(),
  32.                     delegate.getClass().getInterfaces(), this);
  33.         } catch (Exception e) {
  34.             e.printStackTrace();
  35.         } 
  36.         return null;
  37.     }
  38.     public MethodAdvice getMethodAdvice() {
  39.         return methodAdvice;
  40.     }
  41.     public void setMethodAdvice(MethodAdvice methodAdvice) {
  42.         this.methodAdvice = methodAdvice;
  43.     }
  44.     
  45.     
  46. }
  1. package com.advice;
  2. import java.lang.reflect.InvocationTargetException;
  3. import java.lang.reflect.Method;
  4. import java.util.ArrayList;
  5. import java.util.List;
  6. import com.advice.impl.AfterAdviceImpl;
  7. import com.advice.impl.AroundAdviceImpl;
  8. import com.advice.impl.BeforeAdviceImpl;
  9. public class MethodAdvice {
  10.     private List<Advice> adviceList=new ArrayList<Advice>();
  11.     private Object target;
  12.     private Method method;
  13.     private Object[] args;
  14.     private int pos=-1;
  15.     private boolean done;
  16.     private DynaProxy proxy;
  17.     
  18.     public void AddAdvice(Object [] advices){
  19.         adviceList.clear();
  20.         List<Advice> aroundAdvice=new ArrayList<Advice>();
  21.         List<Advice> beforeAdvice=new ArrayList<Advice>();
  22.         List<Advice> afterAdvice=new ArrayList<Advice>();
  23.         for(Object o:advices){
  24.             if (o instanceof AroundAdvice) {
  25.                 AroundAdviceImpl temp= new AroundAdviceImpl((AroundAdvice)o);
  26.                 aroundAdvice.add(temp);
  27.             }
  28.             if (o instanceof BeforeAdvice) {
  29.                 BeforeAdviceImpl ba= new BeforeAdviceImpl((BeforeAdvice)o);
  30.                 beforeAdvice.add(ba);
  31.             }
  32.             if (o instanceof AfterAdvice) {
  33.                 AfterAdviceImpl aa= new AfterAdviceImpl((AfterAdvice)o);
  34.                 afterAdvice.add(aa);
  35.             }
  36.         }
  37.         adviceList.addAll(aroundAdvice);
  38.         adviceList.addAll(beforeAdvice);
  39.         adviceList.addAll(afterAdvice);
  40.     }
  41.     
  42.     public Object proceed(){
  43.         pos++;
  44.         if(pos>=adviceList.size()&&!done){
  45.              return invoke();
  46.         }
  47.         Object o=null;
  48.         //如果执行到后通知了,那就去执行目标方法
  49.         if(!done&& adviceList.get(pos) instanceof AfterAdvice){
  50.             o=invoke();
  51.         }
  52.         adviceList.get(pos).invoke(this);
  53.         return o;
  54.         
  55.     }
  56.     private Object invoke(){
  57.         done=true;
  58.         try {
  59.             return method.invoke(target, args);
  60.         } catch (IllegalArgumentException e) {
  61.             // TODO Auto-generated catch block
  62.             e.printStackTrace();
  63.         } catch (IllegalAccessException e) {
  64.             // TODO Auto-generated catch block
  65.             e.printStackTrace();
  66.         } catch (InvocationTargetException e) {
  67.             // TODO Auto-generated catch block
  68.             e.printStackTrace();
  69.         }
  70.         return null;
  71.     }
  72.     public DynaProxy getProxy() {
  73.         return proxy;
  74.     }
  75.     public void setProxy(DynaProxy proxy) {
  76.         this.proxy = proxy;
  77.     }
  78.     public void setTarget(Object target) {
  79.         this.target = target;
  80.     }
  81.     public void setArgs(Object[] args) {
  82.         this.args = args;
  83.     }
  84.     public void setMethod(Method method) {
  85.         this.method = method;
  86.     }
  87.     public Object getTarget() {
  88.         return target;
  89.     }
  90.     public Method getMethod() {
  91.         return method;
  92.     }
  93.     public Object[] getArgs() {
  94.         return args;
  95.     }
  96.     public int getPos() {
  97.         return pos;
  98.     }
  99.     public void setPos(int pos) {
  100.         this.pos = pos;
  101.     }
  102.     public boolean isDone() {
  103.         return done;
  104.     }
  105.     public void setDone(boolean done) {
  106.         this.done = done;
  107.     }
  108. }
  1. package com.advice;
  2. public interface Advice {
  3.     public Object invoke(MethodAdvice methodAdvice) ;
  4. }
  1. package com.advice;
  2. public interface AroundAdvice extends Advice {
  3. }
  1. package com.advice;
  2. import java.lang.reflect.Method;
  3. public interface BeforeAdvice {
  4.     public void before(Method m, Object[] args, Object target) ;
  5. }
  1. package com.advice;
  2. import java.lang.reflect.Method;
  3. public interface AfterAdvice {
  4.     public void after(Method m, Object[] args, Object target) ;
  5. }
  1. package com.advice.impl;
  2. import com.advice.Advice;
  3. import com.advice.MethodAdvice;
  4. public class AroundAdviceImpl implements Advice {
  5.     private Advice advice;
  6.     
  7.     @SuppressWarnings("unused")
  8.     private AroundAdviceImpl(){};
  9.     public AroundAdviceImpl(Advice advice){
  10.         this.advice=advice;
  11.     }
  12.     
  13.     @Override
  14.     public Object invoke(MethodAdvice methodAdvice) {
  15.         return advice.invoke(methodAdvice);
  16.     }
  17. }
  1. package com.advice.impl;
  2. import com.advice.Advice;
  3. import com.advice.BeforeAdvice;
  4. import com.advice.MethodAdvice;
  5. public class BeforeAdviceImpl implements  Advice {
  6.     
  7.     private BeforeAdvice beforeAdvice;
  8.     @SuppressWarnings("unused")
  9.     private BeforeAdviceImpl(){
  10.         
  11.     }
  12.     public BeforeAdviceImpl(BeforeAdvice beforeAdvice){
  13.         this.beforeAdvice=beforeAdvice;
  14.     }
  15.     @Override
  16.     public Object invoke(MethodAdvice methodAdvice){
  17.         beforeAdvice.before(methodAdvice.getMethod(), methodAdvice.getArgs(), methodAdvice.getTarget());
  18.         return methodAdvice.proceed();
  19.     }
  20. }

 

  1. package com.advice.impl;
  2. import com.advice.Advice;
  3. import com.advice.AfterAdvice;
  4. import com.advice.MethodAdvice;
  5. public class AfterAdviceImpl implements Advice {
  6.     private AfterAdvice afterAdvice;
  7.     @SuppressWarnings("unused")
  8.     private AfterAdviceImpl(){}
  9.     
  10.     public AfterAdviceImpl(AfterAdvice afterAdvice){
  11.         this.afterAdvice=afterAdvice;
  12.     }
  13.     @Override
  14.     public Object invoke(MethodAdvice methodAdvice){
  15.         Object retVal=methodAdvice.proceed();
  16.         afterAdvice.after(methodAdvice.getMethod(), methodAdvice.getArgs(), methodAdvice.getTarget());
  17.         return retVal;
  18.     }
  19. }
  1. package com.myadvice.test; 
  2. import java.lang.reflect.Method;
  3. import java.util.Arrays;
  4. import bsh.EvalError;
  5. import bsh.Interpreter;
  6. import com.advice.AfterAdvice;
  7. import com.advice.AroundAdvice;
  8. import com.advice.BeforeAdvice;
  9. import com.advice.MethodAdvice;
  10. public class MyAdvice implements BeforeAdvice,AfterAdvice,AroundAdvice{ 
  11.     /** 
  12.      * 后通知 
  13.      */
  14.     @Override
  15.     public void after(Method m, Object[] args, Object target){
  16.         System.out.println("后通知,调用的方法:"+m.getName()+",参数:"+Arrays.toString(args));
  17.         
  18.     }
  19.     /** 
  20.      * 环绕通知,最维强大的通知,可以控制目标方法是否执行,也可以改变方法的返回值 
  21.      */ 
  22.     @Override
  23.     public Object invoke(MethodAdvice methodAdvice) {
  24.         System.out.println("[环绕通知]"); 
  25.         /** 
  26.          * 这里我们禁止李四发表任何的评论 
  27.          */ 
  28.         if(methodAdvice.getMethod().getName().equals("comment")"李四".equals(methodAdvice.getArgs()[0])){ 
  29.             System.out.println("屏蔽李四所有的评论"); 
  30.             String returnType=methodAdvice.getMethod().getReturnType().getName(); 
  31.             if("int".equals(returnType)||"long".equals(returnType)||"float".equals(returnType)||"double".equals(returnType)||"byte".equals(returnType)||"short".equals(returnType)){ 
  32.                 //利用BeanShell 构造一个内置变量返回,这里想了好久,没有想到什么方法可以根据 
  33.                 //指定数据类型 返回指定的变量 
  34.                 Interpreter i = new Interpreter(); 
  35.                 try {
  36.                     return i.eval("("+returnType+")0");
  37.                 } catch (EvalError e) {
  38.                     // TODO Auto-generated catch block
  39.                     e.printStackTrace();
  40.                 } 
  41.             }else if("boolean".equals(returnType)){ 
  42.                 return false
  43.             } 
  44.             return null
  45.         }else
  46.             return methodAdvice.proceed();
  47.         } 
  48.     }
  49.     
  50.     /** 
  51.      * 前通知 
  52.      */ 
  53.     @Override
  54.     public void before(Method m, Object[] args, Object target) {
  55.         System.out.println("前通知,调用的方法:"+m.getName()+",参数:"+Arrays.toString(args));
  56.     }    
  1. package com.myadvice.test;
  2. import com.advice.DynaProxy;
  3. import com.advice.MethodAdvice;
  4. import com.test.BookBiz;
  5. import com.test.BookBizImpl;
  6. public class Test {
  7.     /**
  8.      * @param args
  9.      */
  10.     public static void main(String[] args) {
  11.         DynaProxy proxy= new DynaProxy();
  12.         MethodAdvice methodAdvice= new MethodAdvice();
  13.         methodAdvice.AddAdvice(new Object[]{new MyAdvice()});
  14.         proxy.setMethodAdvice(methodAdvice);
  15.         BookBiz bookBiz=(BookBiz)proxy.getBean(BookBizImpl.class.getName());
  16.         bookBiz.buy("张三""Spring深入潜出"50); 
  17.         bookBiz.comment("李四""《恐怖世界》一点都不恐怖,很好看!"); 
  18.         bookBiz.comment("张三""《Spring深入潜出》还是写得不错的!");
  19.     }
  20. }

运行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动态代理

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值