什么是代理

什么是代理呢,其实很好理解,就是不直接访问目标,而是通过一个中间层来访问,就好像下面这样:

Java的静态代理

举个例子,如果我们一些水果,比如:香蕉、苹果等,写成Java代码,大概是下面这个样子:

//Fruit.java
/*** 水果的接口*/
public interface Fruit {/*** 获取水果的名字*/public String getName();
}//Apple.java
public class Apple implements Fruit {@Overridepublic String getName() {return "苹果";}
}//Banana.java
public class Banana implements Fruit {@Overridepublic String getName() {return "香蕉";}
}

但是吃水果,你要削皮吧,你不能每个水果都写一个子类,类处理削皮这个事情吧。因此,我们可以做一个代理 ,吃苹果之前,先把它削皮。 就像下面这样,把原来的水果包一层:


//PeelFruitProxy.java
/*** 代理,让每个水果去皮*/
public class PeelFruitProxy implements Fruit {private Fruit mFruit;public PeelFruit(Fruit fruit) {this.mFruit = fruit;}@Overridepublic String getName() {System.out.println("proxt:" + proxy.getClass().getName());return "去皮的" + mFruit.getName();}
}

添加了测试类,测试类如下:

//Main.java
public class Main {public static void main(String[] args) {Apple apple=new Apple();//原始的苹果Banana banana=new Banana();//原始的香蕉PeelFruitProxy peelApple=new PeelFruitProxy(apple);//代理,添加去皮功能PeelFruitProxy peelBanana=new PeelFruitProxy(banana);//代理,添加去皮功能System.out.println(peelApple.getName());System.out.println(peelBanana.getName());}
}

以上就是Java的静态代理,简单点说,就是把原来的目标对象包一层,加入新东西再去调用目标本身。 但是如果只是这样的静态代理,一个接口,就需要一个代理,实现起来是不是很繁琐。

Java的动态代理

在Java中,有一个干这个事情的类,叫做Proxy,可以直接使用反射方式,代理拦截。 先简单的介绍一下这个类,其实最常用的只有一个静态方法Proxt.newProxyInstance(),是这样的:

 public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)

首先我们要实现InvocationHandler,实现其中的invoke方法,在调用目标对象的时候,会先调用到invoke方法,需要实现者在这个方法中,在主动调用被调用者方法。

//FruitInvocationHandler.java
/*** 调用方法拦截器*/
public class FruitInvocationHandler implements InvocationHandler {private Fruit mFruit;public FruitInvocationHandler(Fruit fruit) {this.mFruit = fruit;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {String result = (String) method.invoke(mFruit, args);//需要在这个方法里面,主动调用被代理的对象。return "去皮的" + result;}
}

运行一下:

//Main.Java
public class Main {public static void main(String[] args) {Apple apple = new Apple();Fruit proxyApple = (Fruit) Proxy.newProxyInstance(Fruit.class.getClassLoader(), new Class[]{Fruit.class}, new FruitInvocationHandler(apple));System.out.println(proxyApple.getClass().getName());System.out.println(proxyApple.getName());Banana banana = new Banana();Fruit proxyBanana = (Fruit) Proxy.newProxyInstance(Fruit.class.getClassLoader(), new Class[]{Fruit.class}, new FruitInvocationHandler(banana));System.out.println(proxyApple.getClass().getName());System.out.println(proxyBanana.getName());}
}

这个方法,就是生成一个上文中的PeelFruitProxy(当然,我们看到的他名字叫:com.sun.proxy.$Proxy0),动态的生成,避免每次都需要写,这个也是叫他动态代理的原因,因为我们可以在运行时代理任意类。 很多程序中的AOP就是这样实现的,但是我们发现一些特点,newProxyInstance()的第二个参数,是一个interfaces的列表,为啥要有这个这个列表呢?

因为我们动态生成的代理类,也需要实现接口,这样才方便向下转型,使用其中的方法,不然,生成的类,类名就是com.sun.proxy.$Proxy0这种,并且是在内存中,无法调用生成的方法。 ** 所以,这种动态代理的方法,有一个致命的缺点,那就是被代理的类,必须要实现接口。**

CGLib代理

cglib is a powerful, high performance and quality Code Generation Library, It is used to extend JAVA classes and implements interfaces at runtime.

另一个大名鼎鼎的Java代理实现,就是CGLib(Code Generation Library),一个基于ASM的代码生成框架,可以用他来动态生成类,然后实现对方法的拦截,就可以避开JDK的动态代理, 必须要目标类实现接口的问题了。 也就是说,可以用CGLib来生成上文中的PeelFruitProxy

简单介绍一下怎么用,首先这个CGLib是一个三方的库,我们要把它依赖进来:

compile 'cglib:cglib:3.2.8'

最新版本可以在这里看(新版本)[https://github.com/cglib/cglib/releases] 然后我们来试一试,我们来实现一下上面的代理

//FruitMethodInterceptor.java
/*** CGLib代理的方法拦截器*/
public class FruitMethodInterceptor implements MethodInterceptor{@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {String result = (String) proxy.invokeSuper(obj, args);//主要,这里调用的是父类,也就是说, 生成的类和原始类是继承关系return "去皮的"+result;}
}//Main.java
public class Main {public static void main(String[] args) {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(Apple.class);enhancer.setCallback(new FruitMethodInterceptor());Apple apple = (Apple) enhancer.create();System.out.println(apple.getClass().getName());System.out.println(apple.getName());}
}

运行效果如下:

我们看到,实现了同样的功能,但是,Apple已经不是原来的Apple类了,变成了com.zjiecode.learn.java.proxy.Apple$$EnhancerByCGLIB$$44ade224,没错,我们正真使用的是这个类,而不是原来的Apple了,这个类继承自Apple,最后实现了对Apple类的代理。 这种方式,因为使用的是继承,所以,无需被代理的类实现接口。当然,他也可以通过接口来实现代理。

总结

  • 第一种代理,就不说了,只适合单一的一个接口的代理,在编译时就决定好了。
  • 第二、三种代理,都是动态时代理 ,但是我们看到也有差别:
    • JDK的动态代理 ,只能实现接口代理,并且是包装的被代理对象(类的实例),也就是说,在代理的过程中,有2个对象,一个代理对象,一个目标对象,目标对象被包装在代理对象里面。
    • CGLib的代理,是继承目标对象,生成了一个新的类,然后来实现代理,这样,在内存中就是有代理对象,没有目标对象了,使用的是直接继承的方式
  • 生成代理类是在运行时,有别于javapoet在编译时生成类。

学习源码

纸上谈来终觉浅,绝知此事要躬行。 文中提到的源代码,你都可以参考: https://github.com/zjiecode/learn-java/tree/feature/java-proxy

参考资料

https://blog.csdn.net/danchu/article/details/70238002 https://blog.csdn.net/lovejj1994/article/details/78080124 https://www.jianshu.com/p/9a61af393e41?from=timeline&isappinstalled=0 https://github.com/cglib/cglib/wiki

推荐

作为个人开发者,很多时候需要关注服务器报警,提醒等, 要直接把服务器消息推送到手机上,为此,我们开发了微信消息推送服务[wxpusher],可以用过API直接把消息实时push到微信上,点击查看介绍。

有木有用 ,关注一下呗,万一有需要的时候呢?

转载于:https://my.oschina.net/u/2428064/blog/2987373

Java代理的几种方式相关推荐

  1. spring中AOP动态代理的两种方式

    AOP动态代理的两种方式 Spring AOP动态代理的方式(spring的AOP默认是JDK Proxy) 浅谈这两种动态代理 JDK的动态代理,需要有实现接口 动态代理--JDK Proxy ⚫ ...

  2. Java技术分享:升级所安装Java版本的两种方式

    在进行Java开发的时候我们可能会需要升级所安装的Java版本,那么你知道应该如何安装吗?小千今天就来给大家介绍两种方式. 一.卸载掉原本安装的Java,下载最新安装包安装即可. 这个步骤就不介绍了, ...

  3. Java操作Excel三种方式POI、Hutool、EasyExcel

    Java操作Excel三种方式POI.Hutool.EasyExcel 1. Java操作Excel概述 1.1 Excel需求概述 1.2 Excel操作三种方式对比 2. ApachePOIExc ...

  4. java创建对象的五种方式

    java创建对象的五种方式 一.使用new关键字 二.使用clone方法 三.使用反序列化 四.使用反射 五.使用Unsafe 一.使用new关键字 如 User user=new User(); 执 ...

  5. Linux之Ubuntu20.04安装Java JDK8的两种方式

    Linux之Ubuntu20.04远程安装Java JDK8的两种方式 安装openjdk8 更新软件包列表: sudo apt-get update 安装openjdk-8-jdk: sudo ap ...

  6. java实现线程三种方式_详解三种java实现多线程的方式

    java中实现多线程的方法有两种:继承Thread类和实现runnable接口. 1.继承Thread类,重写父类run()方法 public class thread1 extends Thread ...

  7. Java循环的三种方式分享

    转自: Java循环的三种方式分享 下文笔者讲述java循环的三种方式分享,如下所示 一.while循环 while语法:while(循环条件){循环体}while关键字处理循环先判断循环条件当条件成 ...

  8. Java 创建对象的 6 种方式,总有一种适合你

    创建对象的 6 种方式 假设有个女朋友类: @Data @NoArgsConstructor @AllArgsConstructor class GirlFriend {private String ...

  9. 【java】之3种方式实现Object和Map之间的转换

    转载:[java]之3种方式实现Object和Map之间的转换 - 古越剑箫 - 博客园

  10. Java连接Oracle两种方式thin与oci区别

    Java连接Oracle两种方式thin与oci区别 前几天同事跑过来跟我说, 机房中的一台tomcat服务器跟oracle数据库机连接很慢,查看控制台中的hibernate日志, 基本上是一条sql ...

最新文章

  1. aes256加密java_使用Java和JCEKS进行AES-256加密
  2. 做技术知道了哪些事情代表自己成熟了?
  3. 微软BI 之SSRS 系列 - 使用 LookupSet 和 Adjacent Group 等高级技巧在报表中跨 Dataset 分组查询...
  4. 几种常见的长度单位整理
  5. 从Clarifai的估值聊聊深度学习
  6. Flutter之CupertinoSwitch和Switch开关组件的简单使用
  7. UITableView(二)
  8. 先来先服务算法、运行时间最短者优先算法和最高响应比优先调度算法_磁盘调度算法...
  9. Opencv获取DroidCamx视频流并改变分辨率
  10. 一个时代的剪影-----汉 (作者:金立扬)
  11. 深入解析淘宝Diamond之客户端架构
  12. 如何在Mac OS X上创建一个Service服务进程
  13. 【解决方案】macOS 打开微信视频电话其他应用音量变小问题
  14. 教妹学Java(十四):switch 语句详解
  15. 大数据分析师高级证书_大数据分析师(ACP)认证考试大纲
  16. element表格左对齐方法
  17. JAVA 如何将class文件转换成java文件
  18. 砼匠商砼ERP大屏展示效果图
  19. Linux打印当前目录
  20. python 根据地址求经纬度 谷歌_js获取ip地址利用谷歌地图获得经纬度

热门文章

  1. mysql中查询编辑器_万能数据库查看器|Universal SQL Editor(万能SQL编辑器)下载 v1.8 官方版 - 比克尔下载...
  2. LinUX接收蓝牙音频,Win10 v2004已重新支持蓝牙A2DP音频串流接收功能
  3. html编写在线打字通,HTML5代码打字练习、HTML5案例 - 03
  4. 计算机实验导论,《计算机科学导论》实验
  5. 斐讯K2 新版固件刷机教程
  6. 概率论——马尔科夫链
  7. Python如何用几行代码实现在线翻译
  8. NXP K60单片机Altium Designer电路设计教程(智能车)
  9. 三菱plc程序三菱FX3U三轴伺服电机程序,包含轴点动,回零,相对与绝对定位
  10. qt mysql图形界面_qt数据库界面