AOP的实现原理——动态代理
IOC负责将对象动态的 注入到容器,从而达到一种需要谁就注入谁,什么时候需要就什么时候注入的效果,可谓是招之则来,挥之则去。想想都觉得爽,如果现实生活中也有这本事那就爽 歪歪了,至于有多爽,各位自己脑补吧;而AOP呢,它实现的就是容器的另一大好处了,就是可以让容器中的对象都享有容器中的公共服务。那么容器是怎么做到 的呢?它怎么就能让在它里面的对象自动拥有它提供的公共性服务呢?答案就是我们今天要讨论的内容——动态代理。
动 态代理其实并不是什么新鲜的东西,学过设计模式的人都应该知道代理模式,代理模式是一种静态代理,而动态代理就是利用反射和动态编译将代理模式变成动态 的。原理跟动态注入一样,代理模式在编译的时候就已经确定代理类将要代理谁,而动态代理在运行的时候才知道自己要代理谁。
Spring的动态代理有两种:一是JDK的动态代理;另一个是cglib动态代理(通过修改字节码来实现代理)。今天咱们主要讨论JDK动态代理的方式。JDK的代理方式主要就是通过反射跟动态编译来实现的,下面咱们就通过代码来看看它具体是怎么实现的。
假设我们要对下面这个用户管理进行代理:
- //用户管理接口
- package com.tgb.proxy;
- public interface UserMgr {
- void addUser();
- void delUser();
- }
- //用户管理的实现
- package com.tgb.proxy;
- public class UserMgrImpl implements UserMgr {
- @Override
- public void addUser() {
- System.out.println("添加用户.....");
- }
- @Override
- public void delUser() {
- System.out.println("删除用户.....");
- }
- }
按
照代理模式的实现方式,肯定是用一个代理类,让它也实现UserMgr接口,然后在其内部声明一个UserMgrImpl,然后分别调用addUser和
delUser方法,并在调用前后加上我们需要的其他操作。但是这样很显然都是写死的,我们怎么做到动态呢?别急,接着看。
我
们知道,要实现代理,那么我们的代理类跟被代理类都要实现同一接口,但是动态代理的话我们根本不知道我们将要代理谁,也就不知道我们要实现哪个接口,那么
要怎么办呢?我们只有知道要代理谁以后,才能给出相应的代理类,那么我们何不等知道要代理谁以后再去生成一个代理类呢?想到这里,我们好像找到了解决的办
法,就是动态生成代理类!
这时候我们亲爱的反射又有了用武之地,我们可以写一个方法来接收被代理类,这样我们就可以通过反射知道它的一切信息——包括它的类型、它的方法等等(如果你不知道怎么得到,请先去看看我写的反射的博客《反射一》《反射二》)。
JDK动态代理的两个核心分别是InvocationHandler和Proxy,下面我们就用简单的代码来模拟一下它们是怎么实现的:
InvocationHandler接口:
- package com.tgb.proxy;
- import java.lang.reflect.Method;
- public interface InvocationHandler {
- public void invoke(Object o, Method m);
- }
实现动态代理的关键部分,通过Proxy动态生成我们具体的代理类:
- package com.tgb.proxy;
- import java.io.File;
- import java.io.FileWriter;
- import java.lang.reflect.Constructor;
- import java.lang.reflect.Method;
- import java.net.URL;
- import java.net.URLClassLoader;
- import javax.tools.JavaCompiler;
- import javax.tools.StandardJavaFileManager;
- import javax.tools.ToolProvider;
- import javax.tools.JavaCompiler.CompilationTask;
- public class Proxy {
- /**
- *
- * @param infce 被代理类的接口
- * @param h 代理类
- * @return
- * @throws Exception
- */
- public static Object newProxyInstance(Class infce, InvocationHandler h) throws Exception {
- String methodStr = "";
- String rt = "\r\n";
- //利用反射得到infce的所有方法,并重新组装
- Method[] methods = infce.getMethods();
- for(Method m : methods) {
- methodStr += " @Override" + rt +
- " public "+m.getReturnType()+" " + m.getName() + "() {" + rt +
- " try {" + rt +
- " Method md = " + infce.getName() + ".class.getMethod(\"" + m.getName() + "\");" + rt +
- " h.invoke(this, md);" + rt +
- " }catch(Exception e) {e.printStackTrace();}" + rt +
- " }" + rt ;
- }
- //生成Java源文件
- String srcCode =
- "package com.tgb.proxy;" + rt +
- "import java.lang.reflect.Method;" + rt +
- "public class $Proxy1 implements " + infce.getName() + "{" + rt +
- " public $Proxy1(InvocationHandler h) {" + rt +
- " this.h = h;" + rt +
- " }" + rt +
- " com.tgb.proxy.InvocationHandler h;" + rt +
- methodStr + rt +
- "}";
- String fileName =
- "d:/src/com/tgb/proxy/$Proxy1.java";
- File f = new File(fileName);
- FileWriter fw = new FileWriter(f);
- fw.write(srcCode);
- fw.flush();
- fw.close();
- //将Java文件编译成class文件
- JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
- StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
- Iterable units = fileMgr.getJavaFileObjects(fileName);
- CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
- t.call();
- fileMgr.close();
- //加载到内存,并实例化
- URL[] urls = new URL[] {new URL("file:/" + "d:/src/")};
- URLClassLoader ul = new URLClassLoader(urls);
- Class c = ul.loadClass("com.tgb.proxy.$Proxy1");
- Constructor ctr = c.getConstructor(InvocationHandler.class);
- Object m = ctr.newInstance(h);
- return m;
- }
- }
这
个类的主要功能就是,根据被代理对象的信息,动态组装一个代理类,生成$Proxy1.java文件,然后将其编译成$Proxy1.class。这样我
们就可以在运行的时候,根据我们具体的被代理对象生成我们想要的代理类了。这样一来,我们就不需要提前知道我们要代理谁。也就是说,你想代理谁,想要什么
样的代理,我们就给你生成一个什么样的代理类。
然后,在客户端我们就可以随意的进行代理了。
- package com.tgb.proxy;
- public class Client {
- public static void main(String[] args) throws Exception {
- UserMgr mgr = new UserMgrImpl();
- //为用户管理添加事务处理
- InvocationHandler h = new TransactionHandler(mgr);
- UserMgr u = (UserMgr)Proxy.newProxyInstance(UserMgr.class,h);
- //为用户管理添加显示方法执行时间的功能
- TimeHandler h2 = new TimeHandler(u);
- u = (UserMgr)Proxy.newProxyInstance(UserMgr.class,h2);
- u.addUser();
- System.out.println("\r\n==========华丽的分割线==========\r\n");
- u.delUser();
- }
- }
运行结果:
- 开始时间:2014年-07月-15日 15时:48分:54秒
- 开启事务.....
- 添加用户.....
- 提交事务.....
- 结束时间:2014年-07月-15日 15时:48分:57秒
- 耗时:3秒
- ==========华丽的分割线==========
- 开始时间:2014年-07月-15日 15时:48分:57秒
- 开启事务.....
- 删除用户.....
- 提交事务.....
- 结束时间:2014年-07月-15日 15时:49分:00秒
- 耗时:3秒
这里我写了两个代理的功能,一个是事务处理,一个是显示方法执行时间的代理,当然都是非常简单的写法,只是为了说明这个原理。当然,我们可以想Spring那样将这些AOP写到配置文件,因为之前那篇已经写了怎么通过配置文件注入了,这里就不重复贴了。
到这里,你可能会有一个疑问:你上面说,只要放到容器里的对象,都会有容器的公共服务,我怎么没看出来呢?好,那我们就继续看一下我们的代理功能:
事务处理:
- package com.tgb.proxy;
- import java.lang.reflect.Method;
- public class TransactionHandler implements InvocationHandler {
- private Object target;
- public TransactionHandler(Object target) {
- super();
- this.target = target;
- }
- @Override
- public void invoke(Object o, Method m) {
- System.out.println("开启事务.....");
- try {
- m.invoke(target);
- } catch (Exception e) {
- e.printStackTrace();
- }
- System.out.println("提交事务.....");
- }
- }
从
代码中不难看出,我们代理的功能里没有涉及到任何被代理对象的具体信息,这样有什么好处呢?这样的好处就是将代理要做的事情跟被代理的对象完全分开,这样
一来我们就可以在代理和被代理之间随意的进行组合了。也就是说同一个功能我们只需要一个。同样的功能只有一个,那么这个功能不就是公共的功能吗?不管容器
中有多少给对象,都可以享受容器提供的服务了。这就是容器的好处。
AOP的实现原理——动态代理的更多相关文章
- 菜鸟学SSH(十四)——Spring容器AOP的实现原理——动态代理
之前写了一篇关于IOC的博客——<Spring容器IOC解析及简单实现>,今天再来聊聊AOP.大家都知道Spring的两大特性是IOC和AOP,换句话说,容器的两大特性就是IOC和AOP. ...
- 浅析Spring中AOP的实现原理——动态代理
一.前言 最近在复习Spring的相关内容,刚刚大致研究了一下Spring中,AOP的实现原理.这篇博客就来简单地聊一聊Spring的AOP是如何实现的,并通过一个简单的测试用例来验证一下.废话不 ...
- Spring 容器AOP的实现原理——动态代理
参考:http://wiki.jikexueyuan.com/project/ssh-noob-learning/dynamic-proxy.html(from极客学院) 一.介绍 Spring的动态 ...
- Spring容器AOP的实现原理——动态代理(转)
文章转自http://blog.csdn.net/liushuijinger/article/details/37829049#comments
- Hibernate学习--hibernate延迟加载原理-动态代理(阿里电面)
在正式说hibernate延迟加载时,先说说一个比较奇怪的现象吧:hibernate中,在many-to-one时,如果我们设置了延迟加载,会发现我们在eclipse的调试框中查看one对应对象时,它 ...
- 新秀学习SSH(十四)——Spring集装箱AOP其原理——动态代理
之前写了一篇文章IOC该博客--<Spring容器IOC解析及简单实现>,今天再来聊聊AOP.大家都知道Spring的两大特性是IOC和AOP. IOC负责将对象动态的注入到容器,从而达到 ...
- Spring AOP实现原理-动态代理
目录 代理模式 静态代理 动态代理 代理模式 我们知道,Spring AOP的主要作用就是不通过修改源代码的方式.将非核心功能代码织入来实现对方法的增强.那么Spring AOP的底层如何实现对方法的 ...
- 二)Spring AOP编程思想与动态代理
一.aop编程思想 1.面向切面,就是能够不动源码的情况下,从横切面切入新的代码功能. 2.实现原理是动态代理 动态代理的步骤 a.写生产厂家,实现接口,代理只能代理接口 b.动态代理类实现Invoc ...
- Spring事务管理机制的实现原理-动态代理
之前在做项目中遇到spring无法进行事务代理问题,最后发现是因为没有写接口,原因当时明白了,看到这篇文章写的清楚些,转过来 我们先来分析一下Spring事务管理机制的实现原理.由于Spring内置A ...
随机推荐
- AIX下RAC搭建 Oracle10G(一)检測系统环境
AIX下RAC搭建系列 环境 节点 节点1 节点2 小机型号 IBM P-series 630 IBM P-series 630 主机名 AIX203 AIX204 交换机 SAN光纤交换机 存储 S ...
- poj1014 Dividing (多重背包)
转载请注明出处:http://blog.csdn.net/u012860063 题目链接:id=1014">http://poj.org/problem?id=1014 Descrip ...
- C++11下的线程池以及灵活的functional + bind + lamda
利用boost的thread实现一个线程类,维护一个任务队列,以便可以承载非常灵活的调用.这个线程类可以方便的为后面的线程池打好基础.线程池还是动态均衡,没有什么别的.由于minGW 4.7 对 C+ ...
- LoadRuner性能测试之内存分析方法及步骤(Windows)
1.首先观察Available Mbytes(可用内存),至少要>=1/2的内存空间 2.然后观察Pages/sec值是不是很大 3.再观察Page Faules/sec是不是很大,其值表示 ...
- android studio 开发android app 真机调试
大家都知道开发android app 的时候可以有2种调试方式, 一种是Android Virtual Device(虚拟模拟器) ,另一种就是真机调试. 这里要说的是真机调试的一些安装步骤: 1. ...
- Android 自定义UI--指南针
有了之前的基础,下面开始实现一个简单的指南针.首先来看一下效果图, 我们可以粗略将这个指南针分为三个部分,一是圆形背景,二是刻度,三是文本.那么在写代码的时候,就可以声明三个Paint画笔来画以上三个 ...
- avalon学习笔记一 列表及条件过滤
好长时间都没有更新博客了,不是因为没有学习新的东西,而是到了新的单位每天玩命加班实在是太累了!经过一年的努力吧,终于可以轻松一下了.废话少说,直接干货吧! 由于是学习阶段,就直接拿了公司的二级页面做了 ...
- (转)添加服务引用和添加Web引用对比
在WindowsForm程序中添加服务引用和Web引用对比 为了验证书上有关Visual Studio 2010添加服务引用和Web引用的区别,进行实验. 一.建立一个Web服务程序项目新建项目,选择 ...
- #JavaScript对象与继承
JavaScript对象与继承 JavaScript是我在C语言之后接触的第二门编程语言,大一暑假的时候在图书馆找了一本中国人写的JavaScript程序设计来看.那个时候在编程方面几乎还是小白,再加 ...
- jquery mobile转场时加载js失效
jquery mobile拦截了所有的http请求,并使用ajax请求取代传统的http.请求发出后,框架会将请求的内容插入到页面中data- role="page"的部分,取代原 ...