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

静态代理

如上图,在程序执行之前。程序猿就要编写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" >

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

这样的管理器就是InvocationHandler.

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

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

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

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

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

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

时间: 2024-08-27 08:29:48

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

java多线程系列(四)

Lock的使用 前言:本系列将从零开始讲解java多线程相关的技术,内容参考于<java多线程核心技术>与<java并发编程实战>等相关资料,希望站在巨人的肩膀上,再通过我的理解能让知识更加简单易懂. 目录 认识cpu.核心与线程 java多线程系列(一)之java多线程技能 java多线程系列(二)之对象变量的并发访问 java多线程系列(三)之等待通知机制 java多线程系列(四)之ReentrantLock的使用 ReentrantLock(重入锁) public class

动态语言、静态语言及动态语言的“鸭子类型“

动态语言与静态语言: 所谓动态和静态,是指程序中类型.类型又是指class,int等定义,比如变量的类型,函数的入参类型,返回值类型.动态类型语言,表示在编写程序的时候,无需制定类型,是运行时检查:静态类型语言,表示在编写程序的时候,需要显式地制定各种类型,在编译的时候就会被检查. 有人说静态类型语言叫强类型语言,是指需要进行变量/对象类型声明的语言,一般情况下需要编译执行. 例如C/C++/Java/C# 有人说动态类型语言叫弱类型语言,是指不需要进行变量/对象类型声明的语言,一般情况下不需要

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

在开始本章内容之前,这里先简单介绍下List的相关内容. List的简单介绍 有序的collection,用户可以对列表中每个元素的插入位置进行精确的控制.用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素.列表通常允许重复的元素,且允许null元素的存放. ArrayList的简单介绍 JDK中这样定义ArrayList:List接口的大小可变数据的实现. 主要有以下特点: 1.有序 2.线程不安全 3.元素可以重复 4.可以存放null值 顾名思义,取名ArrayLis

Java NIO系列(四) - Selector

前言 Selector 是 Java NIO 中的一个组件,用于检查一个或多个通道 Channel 的状态是否处于可读.可写状态.如此可以实现单线程管理多个通道,也就是可以管理多个网络连接. 为什么使用Selector? 用单线程处理多个 Channel 的好处是我需要更少的线程来处理 Channel .实际上,你甚至可以用一个线程来处理所有的Channel.从操作系统的角度来看,切换线程的开销是比较昂贵的,并且每个线程都需要占用系统资源,因此暂用线程越少越好. 简而言之,通过 Selector

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

假设有线程1/线程2/线程3,线程3必须在线程1/线程2执行完成之后开始执行,有两种方式可实现 Thread类的join方法:使宿主线程阻塞指定时间或者直到寄生线程执行完毕 CountDownLatch类:指定计数器,当计数器清零即取消阻塞 import java.util.concurrent.CountDownLatch; import org.junit.Assert; import org.junit.Test; /** * @Description: 规定线程次序的方法 */ publ

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

2017-07-29 16:58:13 一.简介 1.Set概念 Set可以理解为集合,非常类似数据概念中的集合,集合三大特征:1.确定性:2.互异性:3.无序性,因此Set实现类也有类似的特征. 2.HashSet HashSet继承自AbstractSet,实现了Set接口,但是其源码非常少,也非常简单.内部使用HashMap来存储数据,数据存储在HashMap的key中,value都是同一个默认值: 二.HashSet几个重要的方法 1.add(E e) HashSet的确定性,也可以理解

java反射系列四之创建运行时类的对象

一.概念认知 二.代码示例 package reflect; public class TestConstructor { public static void main(String[] args) throws Exception { TestConstructor T = new TestConstructor(); T.test(); } public void test() throws Exception{ String className = "reflect.Person&quo

【动态语言和静态语言】动态语言和静态语言的认识,定义

源:http://www.cnblogs.com/spmxlBlog/archive/2010/06/28/1766832.html 定义: 强类型语言(静态类型语言)是指需要进行变量/对象类型声明的语言,一般情况下需要编译执行. 例如C/C++/Java/C# 弱类型语言(动态类型语言)是指不需要进行变量/对象类型声明的语言,一般情况下不需要编译(但也有编译型的). 例如PHP/ASP/Ruby/Python/Perl/ABAP/SQL/JavaScript/Unix Shell等等.

Java多线程系列

参考资料: http://www.jianshu.com/p/40d4c7aebd66 0.环境 Java: jdk1.8.0_91 CPU: Intel Core i5-6500 Memory: 8G 1.说明 本系列文章为Java多线程的学习记录 Java多线程系列一--Java实现线程方法 Java多线程系列二--Thread类的方法 Java多线程系列三--实现线程同步的方法 Java多线程系列四--控制线程执行顺序 Java多线程系列五--列表类 Java多线程系列六--Map实现类