深入剖析动态代理--从静态代理到动态代理的演化

静态代理

如上图,在程序运行之前,程序员就要编写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动态代理。

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

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

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

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

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

那么这两种方式分别是:第一种是JDK动态代理的实现方式,第二种是CGLIB的实现方式。

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

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-03 16:47:31

深入剖析动态代理--从静态代理到动态代理的演化的相关文章

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

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

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

源: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等等.

静态数组和动态数组

概念 数组在程序设计中应用十分广泛,可以用不同类型的数组来存储大量相同类型的数据.创建数组的方式一般有三种: 全局/静态范围的数组.局部变量数组.申请堆空间创建的数组.其中,全局/静态范围的数组以及局部变量数组都属于静态数组,从堆中申请空间建立的数组为动态数组. 静态数组和动态数组的区别 1.静态数组的大小是在编译期间就确定,并且分配的,其内存在使用结束后由计算机自动释放,效率高:动态数组是在程序运行时,由程序员根据实际需要从堆内存中动态申请的,使用结束后由程序员进行释放,效率低. 2.对静态数

静态库和动态库系列(2)

这个篇幅主要学习一下如何在linux下去生成.so和.a    学习来源http://blog.chinaunix.net/uid-23592843-id-223539.html 先说明一点: 尽管在windows下和在linux下都有静态库和动态库的概念,但是由于编译器,汇编器,连接器的不同.windows的动态(lib)静态(dll)库和linux下的动态(.so)静态(.a)库是不兼容的. 库的产生步骤: 静态库 .a step1:由源文件(.h .cpp) 编译生成一堆.o, 每个.o都

IOS 静态库 和 动态库

库从本质上市一中可执行的二进制格式,可以被载入内存中执行 iOS 中的静态库有 .a 和 .framework 两种形式;  动态库有  .dylib   和  .framework 两种, 后来 .dylib 被苹果换成 .tbd 的形式 静态库 和 动态库 区别 静态库和动态库是相对于编译期和运行期的,静态库在编译期会被连接到代码中,程序运行期将不再需要改静态库, 而动态库在编译期不会被连接到代码中,只有在程序运行期间才会被载入,所以在程序运行期间还需要动态库的存在. 总结 同一个静态库在不

静态资源和动态资源

手动开发动态资源 1 静态资源和动态资源的区别 静态资源: 当用户多次访问这个资源,资源的源代码永远不会改变的资源. 动态资源:当用户多次访问这个资源,资源的源代码可能会发送改变. 动态资源的开发技术 Servlet : 用java语言来编写动态资源的开发技术. Servlet特点: 1)普通的java类,继承HttpServlet类,覆盖doGet方法 2)Servlet类只能交给tomcat服务器运行!!!!(开发者自己不能运行!!!) Servlet手动编写步骤: 1)编写一个servle

转载《Xcode 创建静态库和动态库》

Xcode 创建静态库和动态库 地址链接   http://www.cocoachina.com/ios/20150921/13385.html 1.linux中静态库和动态库区别: 库从本质上来说是一种可执行代码的二进制格式,可以被载入内存中执行.库分静态库和动态库两种. 静态库:这类库的名字一般是libxxx.a:利用静态函数库编译成的文件比较大,因为整个函数库的所有数据都会被整合进目标代码中,他的优点就显而易见了,即编译后的执行程序不需要外部的函数库支持,因为所有使用的函数都已经被编译进去

什么是动态语言和静态语言?静态语言动态语言的区别

定义 通常我们所说的动态语言.静态语言指 动态类型语言(Dynamically Typed Language)和 静态类型语言Statically Typed Language).还有一个 Dynamic Programming Language (动态编程语言),静态编程语言. 动态类型语言:在运行期间检查数据的类型的语言.用这类语言编程,不会给变量指定类型,而是在附值时得到数据类型.如:Python和ruby就是典型动 态类型语言.很多脚本语言vbscrīpt,javascrīpt也是这类语

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

静态代理 如上图,在程序执行之前.程序猿就要编写Proxy.然后进行编译,即在程序执行之前,代理类的字节码文件就已经生成了(Proxy类的class文件已经存在了). 静态代理尽管在增强现有的接口业务功能方面有非常大长处,可是大量使用这样的静态代理,会使系统内的类的规模大量增大,不易维护.而且Proxy类和RealSubject类功能本质上是一样的.仅仅只是Proxy起到了一个中介的作用,这样的代理在系统中的存在导致了系统结构的臃肿和松散. 为了解决问题.产生了动态代理.动态代理是在系统执行中,