静态代理

如上图,在程序执行之前。程序猿就要编写Proxy。然后进行编译,即在程序执行之前,代理类的字节码文件就已经生成了(Proxy类的class文件已经存在了)。

静态代理尽管在增强现有的接口业务功能方面有非常大长处,可是大量使用这样的静态代理,会使系统内的类的规模大量增大,不易维护。而且Proxy类和RealSubject类功能本质上是一样的。仅仅只是Proxy起到了一个中介的作用,这样的代理在系统中的存在导致了系统结构的臃肿和松散。

为了解决问题。产生了动态代理。动态代理是在系统执行中,在须要代理的地方依据接口以及RealSubject动态的生成代理Proxy类,用完之后就会销毁,避免了Proxy类在系统中冗余的问题了。

来看以下的样例:

通过静态代理的实现。手写字节码动态生成代理:

/*
* 售票服务接口
*/
public interface TicketService { //售票
public void sellTicket(); //问询
public void inquire(); //退票
public void withdraw();
}
/**
* 售票服务接口实现类,车站
* @author Emily-T
*
*/
public class Station implements TicketService { @Override
public void sellTicket() {
System.out.println("----------售票------------");
} @Override
public void inquire() {
System.out.println("--------------问询-------------"); } @Override
public void withdraw() {
System.out.println("-------------退票--------------"); } }
/**
* 车票代售点
*
* @author Emily-T
*
*/
public class StationProxy implements TicketService { private Station station; // get,set与这样的构造函数的有什么差别?
public StationProxy(Station station) {
this.station = station;
} @Override
public void sellTicket() { //  1.做真正业务前,提示信息           
this.showAlertInfo("××××您正在使用车票代售点进行购票,每张票将会收取5元手续费!××××");
//  2.调用真实业务逻辑           
station.sellTicket();
//  3.后处理           
this.takeHandlingFee();
this.showAlertInfo("××××欢迎您的光临。再见!××××\n");
} @Override
public void inquire() {
//  1做真正业务前。提示信息          
this.showAlertInfo("××××欢迎光临本代售点,问询服务不会收取不论什么费用,本问询信息仅供參考,详细信息以车站真实数据为准。××××");
//  2.调用真实逻辑           
station.inquire();
//  3。后处理           
this.showAlertInfo("××××欢迎您的光临。再见!××××\n"); } @Override
public void withdraw() {
//  1。真正业务前处理           
this.showAlertInfo("××××欢迎光临本代售点,退票除了扣除票额的20%外。本代理处额外加收2元手续费! ××××");
//  2.调用真正业务逻辑           
station.withdraw();
//  3.后处理           
this.takeHandlingFee();
} /*
*  展示额外信息       
*/
private void showAlertInfo(String info) {
System.out.println(info);
} /*
* 收取手续费
*/
private void takeHandlingFee() {
System.out.println("收取手续费,打印发票。 。 。 。。。\n");
}
}
public class Test {

	public static void main(String[] args) throws Exception {
createProxy(); } private static void createProxy() throws Exception{
ClassPool pool = ClassPool.getDefault(); CtClass cc = pool.makeClass("com.ltt.proxy.StationProxy"); //设置接口
CtClas interface1 = pool.get("com.ltt.proxy.TicketService");
cc.setInterfaces(new CtClass[]{interface1}); //设置field
CtField field = CtField.make("private com.ltt.proxy.Station station;",cc); cc.addField(field); CtClass stationClass = pool.get("com.ltt.proxy.Station");
CtClass[] arrays = new CtClass[]{stationClass};
CtConstructor ctc = CtNewConstructor.make(arrays,null,CtNewConstructor.PASS_NONE,null,null,cc); //设置构造函数内部信息
ctc.stBody("{this.station=$1;}");
cc.addConstructor(ctc); //创建收取手续takeHandlingFee方法
CtMethod takeHandlingFee = CtMethod.make("private void takeHandlingFee(){}",cc);
takeHandlingFee.setBody("System.out.println(\"收取手续费,打印发票\");");
cc.addMethod(takeHandlingFee); //创建showAlertInfo方法
CtMethod showInfo = CtMethod.make("private void showAlertInfo(String info){}",cc);
showInfo.setBody("System.out.println($1);");
cc.addMethod(showInfo); //售票
CtMethod sellTicket = CtMethod.make("public void sellTicket(){}",cc);
sellTicket.setBody("{this.showAlertInfo(\"xxx您正在使用车票代售点进行购票。每张票将会收取5元手续费!xxx\");"+"station.sellTicket();"
+"this.takeHandlingFee();"
+"this.showAlertInfo(\"xxx欢迎您的光临,再见! xxxx\");}"); cc.addMethod(sellTicket); //加入inquire方法
CtMethod inquire = CtMethod.make("public void inquire(){}",cc);
inquire.setBody("{this.showAlertInfo(\"xxxx欢迎光临本代售点,问询服务不会收取不论什么费用。本问询信息仅供參考,"
+ "详细信息以车站真实数据为准!xxxx\");"
+ "station.inquire();"
+"this.showAlertInfo(\"xxxx欢迎您的光临。再见!xxxx\");}");
cc.addMethod(inquire); //加入widthraw方法
CtMethod withdraw = CtMethod.make("public void withdraw(){}",cc);
withdraw.setBody("{this.showAlertInfo(\"xxxx欢迎光临本代售点,退票除了扣除票额的20%外,本代理处额外加收2元手续费!xxxx\");"
+"station.withdraw();"
+"station.takeHandlingFee();}");
cc.addMethod(withdraw); //获取动态生成的class
Class c = cc.toClass();
//获取构造器
Constructor constructor = c.getConstructor(Station.class);
//通过构造器实例化
TicketService o = (TicketService) constructor.newInstance(new Station());
o.inquire();
cc.writeFile("D://Test"); } }

通过上面的代码发现。我们手动创建的代理,里面都有非常多的业务逻辑。冗余性代码非常多,可扩展性非常差,并且本来是为了降低冗余代码。解耦的。这样反而添加了冗余代码以及代理生成的难度。假设是业务逻辑非常复杂的业务,这样的做法是不可取的。

那么以下我们进一步抽象,把手动创建代理抽象封装。就有了如今的JDK或者是CGLIB动态代理。

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGl1dGVuZ3RlbmcxMzA=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">

从图中能够看出,在调用真正的方法前后插入业务代码。也就是在触发(invoke)真实角色方法之前或者之后做一些额外的业务。为了构造出具有通用型和简单性的代理类,能够将全部的触发真实角色动作交给一个触发的管理器,让这个管理器统一地管理触发。

这样的管理器就是InvocationHandler.

在静态代理中,代理Proxy中的方法都制定了调用特定的RealSubject中相应的方法。动态代理是将自己的方法功能实现交给InvocationHandler角色。外界对Proxy角色中的每个方法的调用,Proxy都会交给InvocationHandler来处理。InvocationHandler调用详细对象的角色方法。

代理类应该和真实对象实现功能同样。所以有两种方式:

1、代理类和真实对象实现同样的接口

2、通过继承。代理类继承RealSubject。这样Proxy有了RealSubject的功能,代理类还能够重写RealSubject中的方法实现多态。

那么这两种方式各自是:第一种是JDK动态代理的实现方式,另外一种是CGLIB的实现方式。

从原理上我们不难理解JDK动态代理以及CGLIB动态代理。都是通过一步步的抽象封装从而达到解决某类问题。产生详细应用方案的过程。

Java深入浅出系列(四)——深入剖析动态代理--从静态代理到动态代理的演化的更多相关文章

  1. java多线程系列(四)---Lock的使用

    Lock的使用 前言:本系列将从零开始讲解java多线程相关的技术,内容参考于<java多线程核心技术>与<java并发编程实战>等相关资料,希望站在巨人的肩膀上,再通过我的理 ...

  2. java多线程系列(四)---ReentrantLock的使用

    Lock的使用 前言:本系列将从零开始讲解java多线程相关的技术,内容参考于<java多线程核心技术>与<java并发编程实战>等相关资料,希望站在巨人的肩膀上,再通过我的理 ...

  3. VisualGDB系列7:使用VS创建Linux静态库和动态库

    根据VisualGDB官网(https://visualgdb.com)的帮助文档大致翻译而成.主要是作为个人学习记录.有错误的地方,Robin欢迎大家指正. 本文介绍如何在VS中创建静态库和动态库, ...

  4. 深入理解JAVA集合系列四:ArrayList源码解读

    在开始本章内容之前,这里先简单介绍下List的相关内容. List的简单介绍 有序的collection,用户可以对列表中每个元素的插入位置进行精确的控制.用户可以根据元素的整数索引(在列表中的位置) ...

  5. Java集合系列(四):HashMap、Hashtable、LinkedHashMap、TreeMap的使用方法及区别

    本篇博客主要讲解Map接口的4个实现类HashMap.Hashtable.LinkedHashMap.TreeMap的使用方法以及三者之间的区别. 注意:本文中代码使用的JDK版本为1.8.0_191 ...

  6. Java爬虫系列四:使用selenium-java爬取js异步请求的数据

    在之前的系列文章中介绍了如何使用httpclient抓取页面html以及如何用jsoup分析html源文件内容得到我们想要的数据,但是有时候通过这两种方式不能正常抓取到我们想要的数据,比如看如下例子. ...

  7. 【Java集合系列四】HashSet和LinkedHashSet解析

    2017-07-29 16:58:13 一.简介 1.Set概念 Set可以理解为集合,非常类似数据概念中的集合,集合三大特征:1.确定性:2.互异性:3.无序性,因此Set实现类也有类似的特征. 2 ...

  8. Java多线程系列四——控制线程执行顺序

    假设有线程1/线程2/线程3,线程3必须在线程1/线程2执行完成之后开始执行,有两种方式可实现 Thread类的join方法:使宿主线程阻塞指定时间或者直到寄生线程执行完毕 CountDownLatc ...

  9. 【Java多线程系列四】控制线程执行顺序

    假设有线程1/线程2/线程3,线程3必须在线程1/线程2执行完成之后开始执行,有两种方式可实现 Thread类的join方法:使宿主线程阻塞指定时间或者直到寄生线程执行完毕 CountDownLatc ...

随机推荐

  1. C#基础 特殊集合

    //stack 干草堆 //先进后出 进 push 出 pop //初始化 //Stack ss = new Stack(); //ss.Push(1); //ss.Push(2); //ss.Pus ...

  2. CDH搭建Hadoop分布式服务器集群(java新手小白)

    1首先对于一个java还白的小白,先理解CDH与Hadoop的关系 一.Hadoop版本选择. Hadoop大致可分为Apache Hadoop和第三方发行第三方发行版Hadoop,考虑到Hadoop ...

  3. PostgreSQL与MySQL比较

    特性 MySQL PostgreSQL 实例 通过执行 MySQL 命令(mysqld)启动实例.一个实例可以管理一个或多个数据库.一台服务器可以运行多个 mysqld 实例.一个实例管理器可以监视 ...

  4. js一键导出Excel

    HTML: 1 <div class="container"> 2 <table id="backViewTable" class=" ...

  5. 重新学习Java——对象和类(二)

    上一节回归了如何以面向对象的思想去使用一些Java中的公共类,也设计了一些自己的类并介绍了设计类的基本方法和技巧,这一节我们将继续回顾这些内容,并争取从中获得新的体验和感受. 1. 静态域与静态方法 ...

  6. Fiddler—重复发送一个请求的设置

    https://jingyan.baidu.com/article/b2c186c829a85dc46ff6ff60.html 选中一个request——>Reissue Sequentaill ...

  7. BZOJ 1058: [ZJOI2007]报表统计 multiset + 卡常

    Code: #include<bits/stdc++.h> #define maxn 600000 #define inf 1000000000 using namespace std; ...

  8. 巧用TWaver 3D 矢量图形功能

    的确,提起TWaver,大家想到的首先是“电信拓扑图组件”.其实,由于其灵活的MVC架构.矢量化设计.方便定制等特点,TWaver可以做的还有很多.例如房地产行业常见到的“户型图”. 户型推荐是销售接 ...

  9. 监听微信端,手机端,ios端的浏览器返回事件,pc端关闭事件

    直接上代码了,可以监听微信端,手机端,iOS端的浏览器返回事件,关闭事件不支持 当进入该页面,我们就给这个history压入一个本地的连接.当点击返回.后退及上一页的操作时,就进行监听,在监听代码中实 ...

  10. 模拟--P1427 小鱼的数字游戏

    题目描述 小鱼最近被要求参加一个数字游戏,要求它把看到的一串数字(长度不一定,以0结束,最多不超过100个,数字不超过2^32-1),记住了然后反着念出来(表示结束的数字0就不要念出来了).这对小鱼的 ...