本文来自极客学院 Spring 容器AOP的实现原理——动态代理

之前写了一篇关于IOC的博客——《Spring容器IOC解析及简单实现》,今天再来聊聊AOP。大家都知道Spring的两大特性是IOC和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.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("提交事务.....");  }  }  

从代码中不难看出,我们代理的功能里没有涉及到任何被代理对象的具体信息,这样有什么好处呢?这样的好处就是将代理要做的事情跟被代理的对象完全分开,这样一来我们就可以在代理和被代理之间随意的进行组合了。也就是说同一个功能我们只需要一个。同样的功能只有一个,那么这个功能不就是公共的功能吗?不管容器中有多少给对象,都可以享受容器提供的服务了。这就是容器的好处。

Spring 容器AOP的实现原理——动态代理相关推荐

  1. 菜鸟学SSH——Spring容器AOP的实现原理——动态代理

    之前写了一篇关于IOC的博客--<Spring容器IOC解析及简单实现>,今天再来聊聊AOP.大家都知道Spring的两大特性是IOC和AOP. IOC负责将对象动态的注入到容器,从而达到 ...

  2. 【Spring】AOP底层实现原理 —— 动态代理类的创建(JDK、CGlib)、工厂加工原始对象

    一.AOP概念 AOP (Aspect Oriented Programing) 面向切面编程 = Spring动态代理开发 以切面为基本单位的程序开发,通过切面间的彼此协同,相互调用,完成程序的构建 ...

  3. 【Spring AOP】AOP 底层实现原理 —— 动态代理类的创建(JDK、CGlib)、工厂如何加工原始对象

    AOP编程 AOP 编程 AOP 概念 AOP 编程的开发步骤 切面的名词解释 AOP 的底层实现原理 动态代理类的创建 JDK 的动态代理(原理 + 编码) CGlib 的动态代理 Spring 工 ...

  4. spring中aop默认使用jdk动态代理,springboot2以后默认使用cglib来实现动态代理详解

    Spring5 AOP 默认使用 Cglib 了?我第一次听到这个说法是在一个微信群里: 真的假的?查阅文档 刚看到这个说法的时候,我是保持怀疑态度的. 大家都知道 Spring5 之前的版本 AOP ...

  5. spring框架中JDK和CGLIB动态代理区别

    转载:https://blog.csdn.net/yhl_jxy/article/details/80635012 前言 JDK动态代理实现原理(jdk8):https://blog.csdn.net ...

  6. Spring BPP中优雅的创建动态代理Bean

    Spring BPP中优雅的创建动态代理Bean 一.前言 本文章所讲并没有基于Aspectj,而是直接通过Cglib以及ProxyFactoryBean去创建代理Bean.通过下面的例子,可以看出C ...

  7. Spring AOP的两种动态代理方式的原理和实现(JDK和CGLIB)

    代理机制 Spring 的AOP 部分使用使用JDK动态代理,部分使用CGLIB来为目标对象创建代理.如果被代理的目标对象实现了至少一个接口,则会使用JDK动态代理:如果目标对象没有实现任何接口,则会 ...

  8. AOP的实现原理 —— 静态代理 和 动态代理( Spring AOP)

    文章目录 一.AOP是什么? 二.静态代理 -- AspectJ 2.1.举例说明 三. 动态代理 3.1.JDK 动态代理 3.1.1. 核心类: 3.1.2. 示例1--JDK动态代理 3.2.C ...

  9. aop实现原理-动态代理CGLib代理

    那接下来的话,那咱们Spring当中,这个AOP,咱们先说概念,咱们先不写具体代码,咱们就是聊,聊Spring的AOP,Spring能帮我们干啥呢,我告诉你,Spring他作为对象的容器,Spring ...

最新文章

  1. angularjs与server交互
  2. 时间复杂度、渐进记法、主定理
  3. HihoCoder - 1831 - 80 Days(暴力)
  4. c++ 字符串合并_C语言输入字符和字符串(所有函数大汇总)
  5. 步进电机五根线怎么接_步进驱动和伺服驱动的区别
  6. NOIP 2007 普及组初赛试题(C++)(无答案)
  7. nodejs ffmpeg 超级工具 rtsp fmp4 rtmp
  8. Netty工作笔记0004---BIO简介,介绍说明
  9. 如何从程序员到架构师?
  10. python中隔行输出_在Python中的分隔行上打印列表元素
  11. 数据可视化:CSV格式,JSON格式
  12. [2018.10.13 T2] 工作计划
  13. Origin 软件去除demo 水印
  14. 微信如何推送html文件,微信推送怎么附上文件_怎样在微信公众号上传附件图文步骤...
  15. smtplib 抄送邮件_Smtp发送及抄送邮件
  16. 乐Pro3 乐视X720/乐视X722通刷官方线刷包_救砖包_解账户锁
  17. 国产手机会使用鸿蒙,小米等国产手机会使用鸿蒙系统吗?
  18. 【华为云技术分享】深度理解AI概念、算法及如何进行AI项目开发
  19. activity has leaked window
  20. 冷库用电量计算机方式,冷库耗电量计算方法(附计算公式)

热门文章

  1. jQuery中排除指定元素,同时选择剩下的所有元素
  2. 软工概论第十三周总结
  3. 论网站更新与seo优化的关系
  4. 对象不支持“abigimage”属性或方法
  5. SQL效率低下原因主要有
  6. asp.net性能的技巧
  7. JDBC 2.0中的高级数据类型
  8. 浅入浅出webpack
  9. 通用Logging框架设计
  10. kafka 、 zookeeper 集群(一)