通俗易懂详解Java代理及代码实战
一、概述
代理模式是Java常用的设计模式之一,实现代理模式要求代理类和委托类(被代理的类)具有相同的方法(提供相同的服务),代理类对象自身并不实现真正的核心逻辑,而是通过调用委托类对象的相关方法来处理核心逻辑,而代理类对象主要负责为委托类对象过滤消息、预处理消息、转发消息给委托类、事后处理消息等等。通常代理类会与委托类存在关联关系。
二、静态代理
public interface SaleCar {
void sale();
}
2.hafiz真正卖车子实现类
public class HafizSaleCar implements SaleCar {
@Override
public void sale() {
System.out.println("hafiz sale his car...");
}
}
3.二手车交易中介类
public class CarTradeProxy implements SaleCar {
private HafizSaleCar owner;
public CarTradeProxy(HafizSaleCar owner) {
this.owner = owner;
}
@Override
public void sale() {
System.out.println("proxy add price...");
owner.sale();
}
}
4.测试类
public class Client {
public static void main(String[] args) {
HafizSaleCar owner = new HafizSaleCar();
CarTradeProxy proxy = new CarTradeProxy(owner);
proxy.sale();
}
}
5.测试结果

从上面的代码中,我们可以看出,其实代理类(CarTradeProxy)和委托类(HafizSaleCar)好像区别并不大,我们直接创建一个HafizSaleCar对象,然后调用它的sale()方法不就好了?细心的同学你会发现,其实代理在真正调用委托类的方法之前做了中介加价的操作,这也就意味着我们使用代理模式实现在委托类的基础上增加额外的逻辑操作。
以上就是一个很简单的静态代理的实现过程。但是这个时候我又有了一个新需求,我想用我手里的存款以及买车子赚的钱来给自己买一套新房子,那我又不想东奔西跑找房源,于是我又把买房这件事委托给了房产中介,下面我们就来实现这个逻辑。
1.再定义一个买房的接口
public interface BuyHouse {
void buy();
}
2.重写委托类,实现卖车和买房两个接口
public class HafizTrade implements SaleCar, BuyHouse {
@Override
public void buy() {
System.out.println("hafiz buy house...");
}
@Override
public void sale() {
System.out.println("hafiz sale car...");
}
}
可以看到,我现在既要卖掉我的车子,又要购买新的房子。
3.再创建一个买房子的中介代理类
public class HouseTradeProxy implements BuyHouse {
private HafizTrade customer;
public HouseTradeProxy(HafizTrade customer) {
this.customer = customer;
}
@Override
public void buy() {
System.out.println("proxy add price...");
customer.buy();
}
}
4.卖车子的代理类修改如下
public class CarTradeProxy implements SaleCar {
private HafizTrade owner;
public CarTradeProxy(HafizTrade owner) {
this.owner = owner;
}
@Override
public void sale() {
System.out.println("proxy add price...");
owner.sale();
}
}
5.新的测试类
public class Client {
public static void main(String[] args) {
HafizTrade trader = new HafizTrade();
CarTradeProxy carTradeProxy = new CarTradeProxy(trader);
carTradeProxy.sale();
System.out.println("-----------------------------------------------");
HouseTradeProxy houseTradeProxy = new HouseTradeProxy(trader);
houseTradeProxy.buy();
System.out.println("-----------------------------------------------");
}
}
6.测试结果

这样通过静态代理的方式,我们的确也可以很完美的解决我们的问题,但当我们有越来越多的委托类需要代理,而且代理做的工作又一样,那是不是会多出来很多的代理类,我们开发者会疯掉的,这时候我们就想:如果我们可以只做一次,就能代理一类委托类该多好啊?那么这个时候,动态代理就应运而生了,它可以使得我们只定义一次就能为一类委托类做代理。
三、动态代理
静态代理要求我们在程序发布上线运行之前,就要开发好对应委托类的代理类,而动态代理是我们在程序发布之前,并没有创建好对应的代理类,而是在运行的时候动态的创建代理类。
动态代理实现方式有两种:jdk自带动态代理实现以及cglib实现。jdk代理只适合代理实现接口的目标对象,cglib可以代理没有实现接口的目标对象。
四、基于JDK实现动态代理
1.实现步骤
1).通过实现 InvocationHandler 接口创建自己的调用处理器
2).通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类
3).通过反射机制获得动态代理类的构造函数(jdk自带,不需手动处理)
4).通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入(jdk自带,不需手动处理)
2.创建代理处理器
public class ProxyHandler implements InvocationHandler {
private Object target;
public ProxyHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("proxy add price...");
Object result = method.invoke(target, args);
return result;
}
}
3.测试类
public class Client {
public static void main(String[] args) {
HafizTrade trader = new HafizTrade();
ProxyHandler handler = new ProxyHandler(trader);
Class<? extends HafizTrade> clazz = trader.getClass();
ClassLoader classLoader = clazz.getClassLoader();
Class<?>[] interfaces = clazz.getInterfaces();
SaleCar carProxy = (SaleCar)Proxy.newProxyInstance(classLoader, interfaces, handler);
carProxy.sale();
System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++++");
BuyHouse houseProxy = (BuyHouse)Proxy.newProxyInstance(classLoader, interfaces, handler);
houseProxy.buy();
System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++++");
}
}
4.测试结果

5.原理
生成一个代理类,这个代理类继承Proxy类并且实现了我们定义的接口,代理对象调用方法的时候,调用这个代理对象的一个成员InvocationHandler(上面我们传入了一个InvocationHandler实现对象)的方法,也就是我们包装了委托类后的方法。
五、基于cglib实现动态代理
1.实现步骤
1).通过实现CGLib包提供的MethodInterceptor接口,重写intercept方法,创建自己的方法拦截器
2).通过CGLib中的Enhancer的creat方法创建动态代理对象
2.添加cglib的maven依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
3.自定义ProxyInterceptor
public class ProxyInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("Trade proxy add price...");
Object result = methodProxy.invokeSuper(o, objects);
return result;
}
}
4.测试client
public class Client {
public static void main(String[] args) {
ProxyInterceptor proxy = new ProxyInterceptor();
HafizTrade tradeProxy = (HafizTrade)Enhancer.create(HafizTrade.class, proxy);
tradeProxy.sale();
System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++++");
tradeProxy.buy();
}
}
5.测试结果

6.原理
首先通过asm字节码生成框架生成代理类Class的二进制字节码,然后通过Class.forName加载二进制字节码,生成Class对象,最后通过反射机制获取实例构造,并初始化代理类对象。
六、总结
动态代理可以使得我们一次可以解决一批需要创建代理的问题,使得代码更加灵活,提高了程序的扩展性。动态代理在主流java框架中也非常常用,比如最著名的spring,它在AOP的功能就是使用动态代理实现,还有Dubbo等这样的RPC服务框架,在客户端都是通过代理完成服务的真正调用。了解和学会代理以及实现方式能帮助我们更好地理解主流框架。
关于动态代理的实现细节,可以参考:http://www.360doc.com/content/14/0801/14/1073512_398598312.shtml#
通俗易懂详解Java代理及代码实战的更多相关文章
- 详解java动态代理机制以及使用场景
详解java动态代理机制以及使用场景 https://blog.csdn.net/u011784767/article/details/78281384 深入理解java动态代理的实现机制 https ...
- 详解 Java 中的三种代理模式
代理模式 代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能. 这里使用 ...
- Protocol Buffer技术详解(Java实例)
Protocol Buffer技术详解(Java实例) 该篇Blog和上一篇(C++实例)基本相同,只是面向于我们团队中的Java工程师,毕竟我们项目的前端部分是基于Android开发的,而且我们研发 ...
- 详解Java中的clone方法
详解Java中的clone方法 参考:http://blog.csdn.net/zhangjg_blog/article/details/18369201/ 所谓的复制对象,首先要分配一个和源对象同样 ...
- java基础(十五)----- Java 最全异常详解 ——Java高级开发必须懂的
本文将详解java中的异常和异常处理机制 异常简介 什么是异常? 程序运行时,发生的不被期望的事件,它阻止了程序按照程序员的预期正常执行,这就是异常. Java异常的分类和类结构图 1.Java中的所 ...
- 异常处理器详解 Java多线程异常处理机制 多线程中篇(四)
在Thread中有异常处理器相关的方法 在ThreadGroup中也有相关的异常处理方法 示例 未检查异常 对于未检查异常,将会直接宕掉,主线程则继续运行,程序会继续运行 在主线程中能不能捕获呢? 我 ...
- 第三节:带你详解Java的操作符,控制流程以及数组
前言 大家好,给大家带来带你详解Java的操作符,控制流程以及数组的概述,希望你们喜欢 操作符 算数操作符 一般的 +,-,*,/,还有两个自增 自减 ,以及一个取模 % 操作符. 这里的操作算法,一 ...
- 第十八节:详解Java抽象类和接口的区别
前言 对于面向对象编程来说,抽象是它的特征之一. 在Java中,实现抽象的机制分两种,一为抽象类,二为接口. 抽象类为abstract class,接口为Interface. 今天来学习一下Java中 ...
- 【转帖】windows命令行中java和javac、javap使用详解(java编译命令)
windows命令行中java和javac.javap使用详解(java编译命令) 更新时间:2014年03月23日 11:53:15 作者: 我要评论 http://www.jb51.ne ...
随机推荐
- 好文推荐系列--------(1)bower---管理你的客户端依赖
好文原文地址:http://segmentfault.com/a/1190000000349555 编者注:我们发现了比较有趣的系列文章<30天学习30种新技术>,准备翻译,一天一篇更新, ...
- 【慕课网实战】Spark Streaming实时流处理项目实战笔记二之铭文升级版
铭文一级: 第二章:初识实时流处理 需求:统计主站每个(指定)课程访问的客户端.地域信息分布 地域:ip转换 Spark SQL项目实战 客户端:useragent获取 Hadoop基础课程 ==&g ...
- oss上传大文件
最近公司做工程项目,实现文件云存储上传. 网上找了一天,发现网上很多代码都存在相似问题,最后终于找到了一个满足我需求的项目. 工程如下: 这里对项目的文件传输功能做出分析,怎么实现文件上传的,如何进行 ...
- linux上搭建redis
环境centos7及redis-4.0.2.tar.gz 第一步首先在/usr/local/mypackage下mkdir redis 通过工具上传redis安装包 解压安装包 tar -zxvf r ...
- noip第5课资料
- [转]Windows中使用命令行方式编译打包Android项目
http://my.oschina.net/liux/blog/37875 网上很多用Ant来编译打包Android应用的文章,毕竟Ant是纯Java语言编写的,具有很好的跨平台性.今天想写个纯win ...
- 关于jdbc连接MySQL数据问题
1.解压MySQL后配置环境变量 MYSQL_HOME:D:\win7\Program Files(x86)\mysql-5.6.21-win32(mysql根目录) 添加path:%MYSQL_HO ...
- JSON 全解
和js对象的区别 json只是一种数据格式,不支持undefined,字符串必须使用双引号,需要对/进行转义/. js属性名可不加"" json属性名必须加"" ...
- js-数组方法push
<script type="text/javascript"> var arr=[1,2,3,4,5] arr.push(6,7) ...
- Android记录10--android.os.NetworkOnMainThreadException异常解决办法
2013年11月1日小光棍节 有一段时间没有发表新的博客了,最近一直在忙着开发新浪微博客户端遇到很多问题比较头痛,比如说本篇博客要讲的NetworkOnMainThreadException这个异常, ...