一、概述

  代理模式是Java常用的设计模式之一,实现代理模式要求代理类和委托类(被代理的类)具有相同的方法(提供相同的服务),代理类对象自身并不实现真正的核心逻辑,而是通过调用委托类对象的相关方法来处理核心逻辑,而代理类对象主要负责为委托类对象过滤消息、预处理消息、转发消息给委托类、事后处理消息等等。通常代理类会与委托类存在关联关系。

  按照代理的创建时期,代理可分为:静态代理和动态代理。静态代理由开发者手动创建,在程序运行前,已经存在;而动态代理不需要手动创建,它是在程序运行时动态的创建代理类。

二、静态代理

  我们直接用代码来说明什么叫静态代理,场景是我要卖掉我的车子,但是由于我很忙,所以卖掉车子的过程中不想每天被电话骚扰,于是我就在附近找了一个二手车交易的中介,希望在他的帮助下很轻松的卖掉车子。
1.卖车子接口
  1. public interface SaleCar {
  2. void sale();
  3. }

2.hafiz真正卖车子实现类

  1. public class HafizSaleCar implements SaleCar {
  2. @Override
  3. public void sale() {
  4. System.out.println("hafiz sale his car...");
  5. }
  6. }

3.二手车交易中介类

  1. public class CarTradeProxy implements SaleCar {
  2. private HafizSaleCar owner;
  3. public CarTradeProxy(HafizSaleCar owner) {
  4. this.owner = owner;
  5. }
  6. @Override
  7. public void sale() {
  8. System.out.println("proxy add price...");
  9. owner.sale();
  10. }
  11. }

4.测试类

  1. public class Client {
  2. public static void main(String[] args) {
  3. HafizSaleCar owner = new HafizSaleCar();
  4. CarTradeProxy proxy = new CarTradeProxy(owner);
  5. proxy.sale();
  6. }
  7. }

5.测试结果

从上面的代码中,我们可以看出,其实代理类(CarTradeProxy)和委托类(HafizSaleCar)好像区别并不大,我们直接创建一个HafizSaleCar对象,然后调用它的sale()方法不就好了?细心的同学你会发现,其实代理在真正调用委托类的方法之前做了中介加价的操作,这也就意味着我们使用代理模式实现在委托类的基础上增加额外的逻辑操作。

  以上就是一个很简单的静态代理的实现过程。但是这个时候我又有了一个新需求,我想用我手里的存款以及买车子赚的钱来给自己买一套新房子,那我又不想东奔西跑找房源,于是我又把买房这件事委托给了房产中介,下面我们就来实现这个逻辑。

1.再定义一个买房的接口

  1. public interface BuyHouse {
  2. void buy();
  3. }

2.重写委托类,实现卖车和买房两个接口

  1. public class HafizTrade implements SaleCar, BuyHouse {
  2. @Override
  3. public void buy() {
  4. System.out.println("hafiz buy house...");
  5. }
  6. @Override
  7. public void sale() {
  8. System.out.println("hafiz sale car...");
  9. }
  10. }

可以看到,我现在既要卖掉我的车子,又要购买新的房子。

3.再创建一个买房子的中介代理类

  1. public class HouseTradeProxy implements BuyHouse {
  2. private HafizTrade customer;
  3. public HouseTradeProxy(HafizTrade customer) {
  4. this.customer = customer;
  5. }
  6. @Override
  7. public void buy() {
  8. System.out.println("proxy add price...");
  9. customer.buy();
  10. }
  11. }

4.卖车子的代理类修改如下

  1. public class CarTradeProxy implements SaleCar {
  2. private HafizTrade owner;
  3. public CarTradeProxy(HafizTrade owner) {
  4. this.owner = owner;
  5. }
  6. @Override
  7. public void sale() {
  8. System.out.println("proxy add price...");
  9. owner.sale();
  10. }
  11. }

5.新的测试类

  1. public class Client {
  2. public static void main(String[] args) {
  3. HafizTrade trader = new HafizTrade();
  4. CarTradeProxy carTradeProxy = new CarTradeProxy(trader);
  5. carTradeProxy.sale();
  6. System.out.println("-----------------------------------------------");
  7. HouseTradeProxy houseTradeProxy = new HouseTradeProxy(trader);
  8. houseTradeProxy.buy();
  9. System.out.println("-----------------------------------------------");
  10. }
  11. }

6.测试结果

这样通过静态代理的方式,我们的确也可以很完美的解决我们的问题,但当我们有越来越多的委托类需要代理,而且代理做的工作又一样,那是不是会多出来很多的代理类,我们开发者会疯掉的,这时候我们就想:如果我们可以只做一次,就能代理一类委托类该多好啊?那么这个时候,动态代理就应运而生了,它可以使得我们只定义一次就能为一类委托类做代理。

三、动态代理

  静态代理要求我们在程序发布上线运行之前,就要开发好对应委托类的代理类,而动态代理是我们在程序发布之前,并没有创建好对应的代理类,而是在运行的时候动态的创建代理类。
  动态代理实现方式有两种:jdk自带动态代理实现以及cglib实现。jdk代理只适合代理实现接口的目标对象,cglib可以代理没有实现接口的目标对象。

四、基于JDK实现动态代理

1.实现步骤

  1).通过实现 InvocationHandler 接口创建自己的调用处理器

  2).通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类

  3).通过反射机制获得动态代理类的构造函数(jdk自带,不需手动处理)

  4).通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入(jdk自带,不需手动处理)

2.创建代理处理器

  1. public class ProxyHandler implements InvocationHandler {
  2. private Object target;
  3. public ProxyHandler(Object target) {
  4. this.target = target;
  5. }
  6. @Override
  7. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  8. System.out.println("proxy add price...");
  9. Object result = method.invoke(target, args);
  10. return result;
  11. }
  12. }

3.测试类

  1. public class Client {
  2. public static void main(String[] args) {
  3. HafizTrade trader = new HafizTrade();
  4. ProxyHandler handler = new ProxyHandler(trader);
  5. Class<? extends HafizTrade> clazz = trader.getClass();
  6. ClassLoader classLoader = clazz.getClassLoader();
  7. Class<?>[] interfaces = clazz.getInterfaces();
  8. SaleCar carProxy = (SaleCar)Proxy.newProxyInstance(classLoader, interfaces, handler);
  9. carProxy.sale();
  10. System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++++");
  11. BuyHouse houseProxy = (BuyHouse)Proxy.newProxyInstance(classLoader, interfaces, handler);
  12. houseProxy.buy();
  13. System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++++");
  14.  
  15. }
  16. }

4.测试结果

5.原理

  生成一个代理类,这个代理类继承Proxy类并且实现了我们定义的接口,代理对象调用方法的时候,调用这个代理对象的一个成员InvocationHandler(上面我们传入了一个InvocationHandler实现对象)的方法,也就是我们包装了委托类后的方法。

五、基于cglib实现动态代理

1.实现步骤

  1).通过实现CGLib包提供的MethodInterceptor接口,重写intercept方法,创建自己的方法拦截器

  2).通过CGLib中的Enhancer的creat方法创建动态代理对象

2.添加cglib的maven依赖

  1. <dependency>
  2. <groupId>cglib</groupId>
  3. <artifactId>cglib</artifactId>
  4. <version>2.2.2</version>
  5. </dependency>

3.自定义ProxyInterceptor

  1. public class ProxyInterceptor implements MethodInterceptor {
  2. @Override
  3. public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
  4. System.out.println("Trade proxy add price...");
  5. Object result = methodProxy.invokeSuper(o, objects);
  6. return result;
  7. }
  8. }

4.测试client

  1. public class Client {
  2. public static void main(String[] args) {
  3. ProxyInterceptor proxy = new ProxyInterceptor();
  4. HafizTrade tradeProxy = (HafizTrade)Enhancer.create(HafizTrade.class, proxy);
  5. tradeProxy.sale();
  6. System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++++");
  7. tradeProxy.buy();
  8. }
  9. }

5.测试结果

6.原理

  首先通过asm字节码生成框架生成代理类Class的二进制字节码,然后通过Class.forName加载二进制字节码,生成Class对象,最后通过反射机制获取实例构造,并初始化代理类对象。

六、总结

  动态代理可以使得我们一次可以解决一批需要创建代理的问题,使得代码更加灵活,提高了程序的扩展性。动态代理在主流java框架中也非常常用,比如最著名的spring,它在AOP的功能就是使用动态代理实现,还有Dubbo等这样的RPC服务框架,在客户端都是通过代理完成服务的真正调用。了解和学会代理以及实现方式能帮助我们更好地理解主流框架。

  关于动态代理的实现细节,可以参考:http://www.360doc.com/content/14/0801/14/1073512_398598312.shtml#

Java代理详解的更多相关文章

  1. Java Annotation详解 理解和使用Annotation

    系统中用到了java注解: 查了一下如何使用注解,到底注解是什么: (1)创建方法:MsgTrace Java Class==> 在Create New Class中: name:输入MsgTr ...

  2. Java ClassLoad详解

    Java ClassLoad详解 类加载器是 Java 语言的一个创新,也是 Java 语言流行的重要原因之一.它使得 Java 类可以被动态加载到 Java 虚拟机中并执行.类加载器从 JDK 1. ...

  3. 静态代理,动态代理,Cglib代理详解

    一.静态代理 新建一个接口 定义一个玩家方法: package com."".proxy.staticc; public interface Iplayer { public vo ...

  4. android java 设计模式详解 Demo

    android java 设计模式详解 最近看了一篇设计模式的文章,深得体会,在此基础我将每种设计模式的案例都写成Demo的形式,方便读者研究学习, 首先先将文章分享给大家: 设计模式(Design ...

  5. SpringBoot27 JDK动态代理详解、获取指定的类类型、动态注册Bean、接口调用框架

    1 JDK动态代理详解 静态代理.JDK动态代理.Cglib动态代理的简单实现方式和区别请参见我的另外一篇博文. 1.1 JDK代理的基本步骤 >通过实现InvocationHandler接口来 ...

  6. Java接口 详解(二)

    上一篇Java接口 详解(一)讲到了接口的基本概念.接口的使用和接口的实际应用(标准定义).我们接着来讲. 一.接口的应用—工厂设计模式(Factory) 我们先看一个范例: package com. ...

  7. Java ClassLoader详解(转载)

    Java ClassLoader详解 类加载器是 Java 语言的一个创新,也是 Java 语言流行的重要原因之一.它使得 Java 类可以被动态加载到 Java 虚拟机中并执行.类加载器从 JDK ...

  8. Java内部类详解

    Java内部类详解 说起内部类这个词,想必很多人都不陌生,但是又会觉得不熟悉.原因是平时编写代码时可能用到的场景不多,用得最多的是在有事件监听的情况下,并且即使用到也很少去总结内部类的用法.今天我们就 ...

  9. 黑马----JAVA迭代器详解

    JAVA迭代器详解 1.Interable.Iterator和ListIterator 1)迭代器生成接口Interable,用于生成一个具体迭代器 public interface Iterable ...

随机推荐

  1. C++STL(vector,map,set,list)成员函数整理

    / *最近ACM比赛,用到的时候忘记成员函数了,贼尴尬,给以后比赛做下准备 */ LIST: 构造函数 list<int> c0; //空链表 list<int> c1(3); ...

  2. CSS中2d转换:transition过渡放在:hover伪类中与应用在整个元素中区别

    css的2d转换十分强大,能够在不使用js的情况下,实现页面的元素与用户之间更多动态的交互,增强用户体验.其中使用最多的就是hover伪类. 1.创建一个页面的div元素: <!DOCTYPE ...

  3. asp.net mvc项目远程发布到windows server服务器

    文章参考 自学MVC看这里——全网最全ASP.NET MVC 教程汇总 图文详解远程部署ASP.NET MVC 5项目 配置Web部署处理程序 设备及环境 一台装有windows server 201 ...

  4. [NOIP模拟赛]约会date LCA(顺便填坑)

    这道题也算是厉害了,改了整整俩小时最后发现是深信的LCA打错了,悲伤啊!信仰崩塌了! 顺便复习LCA,给出模板 void init(){//p[i][j]表示i节点2^j的祖先 int j; for( ...

  5. Python打包EXE神器 pyinstaller

    最近由于项目需要,以前的python文件需要编辑为EXE供前端客户使用. 由于最早接触的是distutils,所以一开始准备使用distutils和py2exe搭配来进行python的exe化,也就是 ...

  6. css的定位和浮动

    定位 浮动 float代码 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> ...

  7. apache-DOS

    对DSO的理解还不是特别深刻,所以把自己查来的资料整理一下并想就此作一个总结.暂时先把资料堆到blog里面了,有时间再整理总结.   一.以下源于<Apache HTTP Server Vers ...

  8. JVM命令工具开发

    1.查看tomcat7_8080的gc状态 $ jps -v | awk '/tomcat7_8080/{print $1}' | xargs jstat -gcutil 2.查看tomcat7_80 ...

  9. 【Linux】系统版本信息

    查看操作系统版本信息 dream361@master:~$ cat /etc/issue Ubuntu 16.04.2 LTS \n \l dream361@master:~$ lsb_release ...

  10. java异常之后代码执行测试

    1.程序代码: public static void main(String[] args) { int[] a = {1,2,3}; for(int i=0;i<4;i++){ System. ...