A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 一步一脚印 中级黑马   /  2013-11-14 21:54  /  1482 人查看  /  1 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

aop,学JAVA的大多知道,面向切面编程,意思就是,我们去坐动车,本来是一路畅通的,可是从火车站口到坐上动车的这段路上,经常要各种检查。这样的检查,就有点类似拦截器的味道。这种生活实例就是面向切面编程。它的好处很多,无论是从维护代码的阅读性方面,还是今后维护是否要动源码方面,以及对系统性能,事物,安全性等有着非常重要的作用。

闲话不说,贴上代码
  1. public class LogInvoHandler implements InvocationHandler {

  2.         private Object target; // 代理目标
  3.         private Object proxy; // 代理对象
  4.        
  5.         /*
  6.          * 将真实对象与抽象对象以KEY-VALUE的形式存放到Map中。往后可以一一对应。
  7.          */
  8.         private static Map<Class<?>,LogInvoHandler> invoHandlers = new HashMap<Class<?>,LogInvoHandler>();
  9.        
  10.         private static Logger logger;
  11.        
  12.         private LogInvoHandler() {

  13.         }
  14.        
  15.         /**
  16.          * @see 获得代理类实例
  17.          * @param <T> 需要代理的类
  18.          * @param clazz 需要代理的类字节码
  19.          * @return a proxy instance with the specified invocation handler of a proxy class
  20.          *                    that is defined by the specified class loader and that implements the specified interfaces
  21.          * @author Genekhun(林煜坤)
  22.          */
  23.         public static<T> T getProxyInstance(Class<T> clazz){
  24.                
  25.                 logger = Logger.getLogger(clazz);//日志记录对象
  26.                
  27.                 LogInvoHandler invoHandler = invoHandlers.get(clazz);
  28.                 if(invoHandler == null){
  29.                         invoHandler = new LogInvoHandler();
  30.                        
  31.                         try {
  32.                                
  33.                                 T tar = clazz.newInstance();
  34.                                 invoHandler.setTarget(tar);
  35.                                 invoHandler.setProxy(Proxy.newProxyInstance(
  36.                                                 tar.getClass().getClassLoader(),
  37.                                                 tar.getClass().getInterfaces(),
  38.                                                 invoHandler));
  39.                                
  40.                         } catch (InstantiationException e) {
  41.                                 logger.error("日志记录代理发生异常");
  42.                                 throw new CreateLogProxyException("日志记录代理发生异常",e);
  43.                         } catch (IllegalAccessException e) {
  44.                                 logger.error("日志记录代理发生异常");
  45.                                 throw new CreateLogProxyException("日志记录代理发生异常",e);
  46.                         }
  47.                        
  48.                         invoHandlers.put(clazz, invoHandler);
  49.                 }
  50.                 return (T)invoHandler.getProxy();
  51.         }
  52.        
  53.         public Object invoke(Object proxy, Method method, Object[] args)
  54.                         throws Throwable {       

  55.                 Object result = method.invoke(target, args);
  56.                
  57.                 //打印日志
  58.                 logger.info("___invoke method:"+method.getName()+
  59.                                 "; agrs: "+ (args == null? "null":Arrays.asList(args).toString()+
  60.                                                 "; return: " + result));
  61.                
  62.                 return result;
  63.         }
复制代码
这里我们定义了一个代理对象和代理目标。
动态代理的原理,有点像AJAX的原理,AJAX是创建一个XHP代理,让代理去获取需要的数据然后返回。
现实生活中也有很多这样的事例,比如某天你的邮件到了,可是你在开会,没法自己去拿,一般人都会叫自己的朋友去帮忙带领。这样的行为就是代理。
动态代理也是这样,创建一个代理对象和目标。
这里我们还得知道Class对象, 我们知道对象的字节码里面蕴含了该对象的所有信息。所有我们要得到一个和真实目标有相同的功能,就必须要拿到它的所有信息。所以getProxyInstance(Class<T> clazz)这个方法中的参数,你们应该明白了吧!
那么怎么将真实目标的所有信息都能复制到代理身上,这里就用到了反射机制的知识。我相信如果你学完张老师的反射机制你 应该能明白的,这里我就不做详述了。
得到了代理目标的信息,接下来我们要获取代理对象的一些信息,比如去哪里获取信息,要获得什么信息等。

接下来我们分析 一下 方法
  1. invoHandler.setProxy(Proxy.newProxyInstance(
  2.                tar.getClass().getClassLoader(),
  3.                tar.getClass().getInterfaces(),
  4.                invoHandler));
复制代码
从方法名就能看上,这个操作是获得代理对象的。 那么要获得被代理对象的信息,我们肯定要拿到它的字节码,第一个参数,就是被代理对象的加载器也就是它的字节码,第二个参数是代理对象要进行的操作,人家叫你去做东西,你总得知道做什么是把。第三个参数 InvocationHandler 对象,意思呢就是调用处理器,都叫处理器了你们应该明白作用了吧。

经过上面的分析,我们是不是拿到了代理目标的所有信息以及代理对象的所有信息。那么接下来就是开始两者联系的这么一个过程。
invoke,对象上的调用函数。看过源码的同学应该会知道,类与类之间调用就是通过这个方法,那么我们重载这个方法,就是说明我们现在有了比较邪恶的意图了。是把!就是要把它拦截下来,就好比前面说的,你去车站的中间把你拦住,要做个安检。同理!我们就在这个方法体中加入我们想要的操作。
就是这么一个简单的原理。

我可能不太幽默,不知道大家满不满意对我的描述呢?欢迎交流。!!

评分

参与人数 1技术分 +1 收起 理由
FFF + 1 赞一个!

查看全部评分

1 个回复

倒序浏览
值得学习ing!
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马