之前写了一篇关于IOC的博客——《Spring容器IOC解析及简单实现》,今天再来聊聊AOP。大家都知道Spring的两大特性是IOC和AOP。

IOC负责将对象动态的注入到容器,从而达到一种需要谁就注入谁,什么时候需要就什么时候注入的效果,可谓是招之则来,挥之则去。想想都觉得爽,如果现实生活中也有这本事那就爽歪歪了,至于有多爽,各位自己脑补吧;而AOP呢,它实现的就是容器的另一大好处了,就是可以让容器中的对象都享有容器中的公共服务。那么容器是怎么做到的呢?它怎么就能让在它里面的对象自动拥有它提供的公共性服务呢?答案就是我们今天要讨论的内容——动态代理。

动态代理其实并不是什么新鲜的东西,学过设计模式的人都应该知道代理模式,代理模式是一种静态代理,而动态代理就是利用反射和动态编译将代理模式变成动态的。原理跟动态注入一样,代理模式在编译的时候就已经确定代理类将要代理谁,而动态代理在运行的时候才知道自己要代理谁。

Spring的动态代理有两种:一是JDK的动态代理;另一个是cglib动态代理(通过修改字节码来实现代理)。今天咱们主要讨论JDK动态代理的方式。JDK的代理方式主要就是通过反射跟动态编译来实现的,下面咱们就通过代码来看看它具体是怎么实现的。

假设我们要对下面这个用户管理进行代理:

//用户管理接口
package com.shuijing.proxy;public interface UserMgr {void addUser();void delUser();
}//用户管理的实现
package com.shuijing.proxy;public class UserMgrImpl implements UserMgr {@Overridepublic void addUser() {System.out.println("添加用户.....");}@Overridepublic void delUser() {System.out.println("删除用户.....");}}

按照代理模式的实现方式,肯定是用一个代理类,让它也实现UserMgr接口,然后在其内部声明一个UserMgrImpl,然后分别调用addUser和delUser方法,并在调用前后加上我们需要的其他操作。但是这样很显然都是写死的,我们怎么做到动态呢?别急,接着看。

我们知道,要实现代理,那么我们的代理类跟被代理类都要实现同一接口,但是动态代理的话我们根本不知道我们将要代理谁,也就不知道我们要实现哪个接口,那么要怎么办呢?我们只有知道要代理谁以后,才能给出相应的代理类,那么我们何不等知道要代理谁以后再去生成一个代理类呢?想到这里,我们好像找到了解决的办法,就是动态生成代理类!

这时候我们亲爱的反射又有了用武之地,我们可以写一个方法来接收被代理类,这样我们就可以通过反射知道它的一切信息——包括它的类型、它的方法等等(如果你不知道怎么得到,请先去看看我写的反射的博客《反射一》《反射二》)。

JDK动态代理的两个核心分别是InvocationHandler和Proxy,下面我们就用简单的代码来模拟一下它们是怎么实现的:

InvocationHandler接口:

package com.shuijing.proxy;import java.lang.reflect.Method;public interface InvocationHandler {public void invoke(Object o, Method m);
}

实现动态代理的关键部分,通过Proxy动态生成我们具体的代理类:

package com.shuijing.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.shuijing.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.shuijing.proxy.InvocationHandler h;" + rt +                            methodStr + rt +"}";String fileName = "d:/src/com/shuijing/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.shuijing.proxy.$Proxy1");Constructor ctr = c.getConstructor(InvocationHandler.class);Object m = ctr.newInstance(h);return m;}}

这个类的主要功能就是,根据被代理对象的信息,动态组装一个代理类,生成$Proxy1.java文件,然后将其编译成$Proxy1.class。这样我们就可以在运行的时候,根据我们具体的被代理对象生成我们想要的代理类了。这样一来,我们就不需要提前知道我们要代理谁。也就是说,你想代理谁,想要什么样的代理,我们就给你生成一个什么样的代理类。

然后,在客户端我们就可以随意的进行代理了。

package com.shuijing.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.shuijing.proxy;import java.lang.reflect.Method;public class TransactionHandler implements InvocationHandler {private Object target;public TransactionHandler(Object target) {super();this.target = target;}@Overridepublic void invoke(Object o, Method m) {System.out.println("开启事务.....");try {m.invoke(target);} catch (Exception e) {e.printStackTrace();}System.out.println("提交事务.....");}}

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

不知道我讲的够不够清楚,欢迎大家积极交流、讨论。

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

  1. Spring 容器AOP的实现原理——动态代理

    本文来自极客学院 Spring 容器AOP的实现原理--动态代理 之前写了一篇关于IOC的博客--<Spring容器IOC解析及简单实现>,今天再来聊聊AOP.大家都知道Spring的两大 ...

  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. 推荐学java——Spring之AOP

    tips:本文首发在公众号逆锋起笔 ,本文源代码在公众号回复aop 即可查看. 什么是AOP? AOP (Aspect Orient Programming),直译过来就是 面向切面编程.AOP 是一 ...

  8. 菜鸟学SSH(十五)——简单模拟Hibernate实现原理

    之前写了Spring的实现原理,今天我们接着聊聊Hibernate的实现原理,这篇文章只是简单的模拟一下Hibernate的原理,主要是模拟了一下Hibernate的Session类.好了,废话不多说 ...

  9. Spring的AOP与IOC原理

    一.IOC(Inversion of Control):控制反转 传统模式下使用类的方法与属性,我们需要new出这个类的对象,然后使用对象进行方法调用,这种方法耦合度极高,为了降低耦合度,Spring ...

最新文章

  1. web 平台搭建-LNMP-源码包(CentOS-6)
  2. J0ker的CISSP之路:How CISSP(3)
  3. VS2005的Command Window 调试命令的总结(转载)
  4. 【微信小程序】scroll-view与Page下拉冲突
  5. 【CPP 小技巧 (一)FPS】统计处理一张图像算法消耗的时间 3 种方法
  6. oracle中的序列 cache,oracle row cache lock 之sequence
  7. javascript java html_JS入门篇(二):在html中如何使用Javascript
  8. mybatis 报错 with invalid types () or values 0. Cause: java.lang.NoSuchMethodException:
  9. Vue实现简单Tab标签页组件
  10. linux shell 常用命令总结
  11. python数据分析百度云资源_数据分析师视频教程百度云网盘下载
  12. tcs标准编写软件_tcs2010下载-tcs2010(中国标准编写模板) 免费版 - 河东下载站
  13. 移动办公OA系统选型的任何疑问,这篇文章帮您搞定!
  14. 贴片电容耐压和额定电压
  15. 用DEM制作通用三维地形模型
  16. The APR based Apache Tomcat Native library which allows optimal performance in production ...解决方案
  17. Flask构建微信订餐小程序②-全站统计
  18. 可以发热的“电”羽绒服!60秒升温15度,自带充电宝,脏了还能机洗
  19. 左移寄存器vhdl_基于VHDL的移位寄存器设计
  20. Code Jam Problem D. GoroSort

热门文章

  1. 输出1000以内的质数
  2. 软考中级可以在广州落户吗?|软考中级可以在广州落户吗?
  3. Android之调节手机屏幕亮度
  4. 关联规则挖掘——Apriori算法的基本原理以及改进
  5. 什么是 Multicast|组播
  6. 用于表检测和结构识别的深度学习:综述
  7. Slim模型部署多GPU
  8. [五校联考4]淬炼神体
  9. 《网络编程综合实践》:高校爬虫(厦大,南理,华大)
  10. android分析之自定义圆形头像