首先来谈为什么需要代理。

比如我有一个接口如下

public interface Moveable {void move();}

它有一个move方法。现在我有这样一个需求,当我要通过子类实现该接口调用move方法的时候,我想在move方法的前后加一些逻辑
比如加日志、事务、权限等。我不想改变或者有时候我们没有源码没法改变move方法的时候,这时候我们可以让一个代理类帮我们做类似这样的事情。

因此我们先从静态代理说起。先建一个Moveable接口的实现类

import java.util.Random;public class Tank implements Moveable {@Overridepublic void move() {System.out.println("Tank Moving...");try {Thread.sleep(new Random().nextInt(10000));} catch (InterruptedException e) {e.printStackTrace();}}}

然后我们再建一个代理类LogProxy

public class LogProxy implements Moveable {private Moveable moveable;public LogProxy(Moveable moveable) {this.moveable = moveable;}@Overridepublic void move() {System.out.println("Tank move begin......");moveable.move();System.out.println("Tank move end......");}}

让它实现Moveable接口,并且持有Tank类的引用也就是实现Moveable接口的子类。重写move方法,并在其前后加日志逻辑。这样一个静态代理类就写好了。下面我们看客户

端调用代码。

public class Client {public static void main(String[] args) throws Exception {Tank t = new Tank();Moveable m = (Moveable)new LogProxy(t);m.move();}
}

打印结果:

Tank move begin......
Tank Moving...
Tank move end......

静态代理是有缺陷的,如果我想给Tank加一个其他逻辑的代理,那么我需要再新建一个代理类,并实现Moveable接口。这样不够灵活也不够方便。我们想要这样一个代理,它可以代理实现任何接口的对象的任何方法。而不是像静态代理那样只能代理实现Moveable接口的move方法,那么下面我们就来看动态代理。

在分析动态代理之前我们来看一下JDK自带的动态代理如何使用,还是以上面的Moveable接口、Tank类为例。我们先建一个日志逻辑处理类LogHandler实现InvocationHandler接口。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class LogHandler implements InvocationHandler {  private Object target;public LogHandler(Object target) {    this.target = target;  }@overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable {//目标方法前的逻辑System.out.println("start......" );Object ret = null;try {//调用目标方法,本例中会调用move方法,这里用到反射技术ret = method.invoke(target, args);}catch(Exception e) {e.printStackTrace();throw e;}//目标方法后的逻辑System.out.println("end.....");return ret;}
}

然后客户端代码如下:

import java.lang.reflect.Proxy;
public class Client {public static void main(String[] args) {//委托类对象Moveable mgr = new Tank(); //处理类对象LogHandler h = new LogHandler(mgr);//代理类Proxy生一个实现了Moveable接口,并且包含新增了LogHandler处理逻辑的代理对象mMoveable m = Proxy.newProxyInstance(mgr.getClass().getClassLoader(), mgr.getClass().getInterfaces(), h);m.move();}
}

这样加了日志逻辑的move方法运行结果如下:
start......
Tank Moving...
end......

下面我们来分析一下Proxy以及它的newProxyInstance(...)方法都干了些什么……
我借用尚学堂马士兵的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 {public static Object newProxyInstance(ClassLoader loader,Class infce, InvocationHandler h) throws Exception { //JDK6 Complier API, CGLib, ASMString methodStr = "";String rt = "\r\n";//利用反射,获得infce接口中方法,本例中就是获得Moveable接口的方法moveMethod[] methods = infce.getMethods();//拼接infce中所有方法字符串,用来重写infce中的方法,本例中拼出来就是重写的move方法for(Method m : methods) {methodStr += "@Override" + rt + "public void " + m.getName() + "() {" + rt +"    try {" + rt +"    Method md = " + infce.getName() + ".class.getMethod(\"" + m.getName() + "\");" + rt +/*方法最核心的代码,h是构造$Proxy1时传入的handler,本例中就是LogHandler对象,new Object[] { null}是move方法需要的参数,本例不需要,故为空。这一步将会使我们编写的处理类逻辑LogHandler的invoke方法得到调用。从而达到我们最初要在move方法前加日志逻辑的的目的,下面要做的就是把我们拼好的字符串生成类并load到内存就可以了这样就实现了动态生成代理类并加自己想加的逻辑*/"    h.invoke(this, md,new Object[] { null});" + rt +"    }catch(Exception e) {e.printStackTrace();}" + rt +"}";}String src = "package com.bjsxt.proxy;" +  rt +"import java.lang.reflect.Method;" + rt +//这里动态实现infce接口,本例中就是Moveable,构造方法中让Proxy持有处理类Handler的引用"public class $Proxy1 implements " + infce.getName() + "{" + rt +"    public $Proxy1(InvocationHandler h) {" + rt +"        this.h = h;" + rt +"    }" + rt +"    com.bjsxt.proxy.InvocationHandler h;" + rt +//这里是需要重写Moveable中的方法,见上面该字符串的拼接过程               methodStr +"}";String fileName = "d:/src/com/bjsxt/proxy/$Proxy1.java";File f = new File(fileName);FileWriter fw = new FileWriter(f);fw.write(src);fw.flush();fw.close();//compile编译上面拼好的字符串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();//load into memory and create an instance加载进内存并创建对象URL[] urls = new URL[] {new URL("file:/" + "d:/src/")};URLClassLoader ul = new URLClassLoader(urls);Class c = ul.loadClass("com.bjsxt.proxy.$Proxy1");System.out.println(c);Constructor ctr = c.getConstructor(InvocationHandler.class);Object m = ctr.newInstance(h);//利用反射,这里就返回一个实现了infce接口也就是本例中Moveable接口的代理对像$Proxy1return m;}
}

需要注意的是这里的Proxy类和JDK中java.lang.reflect.Proxy并不相同。只是他们的原理大致相同,实现的细节并不同。JDK中生成$Proxy1并不是拼字符串,而是直接生成二进制码。

java动态代理原理剖析相关推荐

  1. Java 动态代理原理图解 (附:2种实现方式详细对比)

    动态代理在 Java 中有着广泛的应用,例如:Spring AOP 面向切面编程,Hibernate 数据查询.以及 RPC Dubbo 远程调用等,都有非常多的实际应用@mikechen 目录 Ja ...

  2. java动态代理原理及解析

    转载自 http://blog.csdn.net/scplove/article/details/52451899 代理:设计模式 代理模式是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制 ...

  3. java动态代理原理

    代理:设计模式 代理模式是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个真实对象的访问.代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理. 通过 ...

  4. java动态代理原理解析

    总结:一.应用:1.要代理的类必须有对应实现接口.2.被增强的代码要实现invocationHandle接口,实现接口的invoke方法,在方法里添加增强代码和通过调用method.invoke( p ...

  5. Java动态代理原理分析

    JDK动态代理原理分析 interface Foo {void foo(); }class Target implements Foo {@Overridepublic void foo() {Sys ...

  6. Java 动态代理原理

    java.lang.reflect.Proxy#newProxyInstance public static Object newProxyInstance(ClassLoader loader,Cl ...

  7. Java 动态代理 原理解析

    概要 AOP的拦截功能是由java中的动态代理来实现的.说白了,就是在目标类的基础上增加切面逻辑,生成增强的目标类(该切面逻辑或者在目标类函数执行之前,或者目标类函数执行之后,或者在目标类函数抛出异常 ...

  8. 【Java基础知识 14】java动态代理原理

  9. 深入理解Java Proxy和CGLIB动态代理原理

    点击上方关注,每天进步一点点 动态代理在Java中有着广泛的应用,比如Spring AOP,Hibernate数据查询.测试框架的后端mock.RPC,Java注解对象获取等.静态代理的代理关系在编译 ...

最新文章

  1. underscorejs之 _.indexBy(list, iteratee, [context])
  2. (C++)第一个字母变成第26个字母,第i个字母变成第(26-i+1)个字母,非字母字符不变。要求根据密码译回原文,并输出。
  3. java使用Maven加载JDBC:mysql-connector连接MySQL/MariaDB
  4. 框架SpringMVC笔记系列 二 传值
  5. HDU 1853 MCMF
  6. 【DBMS 数据库管理系统】数据仓库中 数据追加 ( 时标方法 | DELTA 文件法 | 前后映像文件法 | 日志文件法 )
  7. 机器学习知识点(十六)集成学习AdaBoost算法Java实现
  8. android studio wcf,将图像从android studio上传到Wcf Service
  9. A beginner’s guide to Cache synchronization strategies--转载
  10. ActiveMQ跟SpringBoot整合的双向队列
  11. 基准测试 ApacheBench ab学习
  12. springboot自动配置的原理_SpringBoot自动配置原理
  13. 网络流24题 洛谷 3355 骑士共存
  14. Qt creator5.7 OpenCV249之中值滤波(含源码下载)
  15. mySQL无锁队列_使用 MySQL 实现无锁任务队列(using MySQL as a job queue)
  16. Linux Shell 实现网页爬虫
  17. 离散数学_命题逻辑的演绎推理
  18. html表格内容自动换行符,html表格内容自动换行
  19. 云服务器流量是什么东西_云服务器带宽与流量都有什么关系?不限制流量服务器...
  20. 奇瑞汽车召回部分瑞虎3xe纯电动汽车 共计8580辆

热门文章

  1. nginx离线部署以及安装包
  2. H5+Css3学习内容
  3. 对称思维的妙用之从解题到本质(四)——用三个套路秒杀一众问题
  4. 巨头纷纷适配数字人民币使用场景,支付体系升级再下一城?
  5. html a 标签颜色设置颜色设置,font标签
  6. android调用qq聊天功能
  7. Java中entryset用法,keySet()、entrySet()和Map.Entry用法
  8. linux less命令详解(9个实用案例演示)
  9. linux下命令行浏览器w3m,Linux 终端浏览器 w3m
  10. git登录账号密码错误remote: Incorrect username or password (access token)