您的位置:首页 > 编程语言 > Java开发

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

2015-06-29 09:35 701 查看
静态代理



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

静态代理虽然在增强现有的接口业务功能方面有很大优点,但是大量使用这种静态代理,会使系统内的类的规模大量增大,不易维护;并且Proxy类和RealSubject类功能本质上是一样的,只不过Proxy起到了一个中介的作用,这种代理在系统中的存在导致了系统结构的臃肿和松散。

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

来看下面的例子:

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

[java] view
plaincopyprint?

/*

* 售票服务接口

*/

public interface TicketService {

//售票

public void sellTicket();

//问询

public void inquire();

//退票

public void withdraw();

}

[java] view
plaincopyprint?

/**

* 售票服务接口实现类,车站

* @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("-------------退票--------------");

}

}

[java] view
plaincopyprint?

/**

* 车票代售点

*

* @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");

}

}

[java] view
plaincopyprint?

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动态代理。都是通过一步步的抽象封装从而达到解决某类问题,产生具体应用方案的过程。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: