基础:需要具备面向对象设计思想,多态的思想,反射的思想;
java动态代理机制的出现,使得java开发人员不用手工编写代理类,只要简单地指定一组接口及委托类对象,便能动态地获得代理类。代理类会负责将所有的方法调用分派到委托对象上反射执行,在分派执行的过程中,开发人员还可以按需调整委托类对象及其功能,这是一套非常灵活有弹性的代理框架。通过阅读本文,读者将会对java动态代理机制有更加深入的理解。本文首先从java动态代理的运行机制和特点出发,对其代码进行了分析,推演了动态生成类的内部实现。
代理模式的基本概念和分类
代理模式:为其他对象提供一个代理,来控制对这个对象的访问。代理对象起到中介作用,可以去掉服务或者增加额外的服务,或者引用别人的话:“代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。”
代理模式在开发中的应用场景
远程代理:为不同地理的对象提供局域网代表对象。
虚拟代理:根据需要将资源消耗很大的对象进行延迟,真正需要的时候进行创建。比如网页中的先显示文字再显示图片。
保护代理:控制不同用户的访问权限。比如:只有当客户注册成功之后,才可以进行增删改查等操作。
智能引用代理:提供对目标代理额外的服务。
代理模式的实现方式
使用继承和聚合实现动态代理,哪种更好呢!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
public interface moveable { public void move(); } public class car implements moveable{ @override public void move() { try { thread.sleep( new random().nextint( 1000 )); system.out.println( "……行驶中……" ); } catch (interruptedexception e) { // todo auto-generated catch block e.printstacktrace(); } } } public class car2 extends car{ @override public void move() { //分离代码,增加业务逻辑 long starttime=system.currenttimemillis(); system.out.println( "汽车开始行驶……" ); super .move(); long endtime=system.currenttimemillis(); system.out.println( "汽车结束行驶……时间:" +(endtime-starttime)+ "ms" ); } } |
继承方式实现代理
moveablecar2=newcar2();
car2.move();
聚合方式实现代理
carcar=newcar();
moveablem=newcar3(car);
m.move();
总结
使用继承方式不够灵活,当功能叠加的时候,只能臃肿的扩展代理类;
使用聚合的方式,代理之间可以相互传递,灵活的组合代理;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
public class carlogproxy extends car{ @override public void move() { //分离代码,增加业务逻辑 long starttime=system.currenttimemillis(); system.out.println( "日志开始……" ); super .move(); long endtime=system.currenttimemillis(); system.out.println( "日志结束……" ); } } public class cartimeproxy implements moveable { public cartimeproxy(car car) { super (); this .car=car; } private carcar; @override public void move() { //分离代码,增加业务逻辑 long starttime=system.currenttimemillis(); system.out.println( "汽车开始行驶……" ); car.move(); long endtime=system.currenttimemillis(); system.out.println( "汽车结束行驶……时间:" +(endtime-starttime)+ "ms" ); } } @test : car car = new car(); cartimeproxy ctp= new cartimeproxy(car); carlogproxy clp= new carlogproxy(ctp); clp.move(); //还可以通过接口相互传递代理实例 carlogproxy clp1= new carlogproxy(car); cartimeproxy ctp1= new cartimeproxy(clp1); ctp1.move(); |
jdk动态代理和cglib动态代理
jdk动态代理
代理实现
如果不同的对象要实现相同功能的代理类,应该如何处置?
此时可以试着将其集成在同一个代理类中-----动态代理:实现对不同类/不同方法的代理;
大致过程如下:
java动态代理类位于java.lang.reflect包下,一般主要涉及到一下两个类:
(1)interfaceinvocationhandler:该接口中仅定义了一个方法publicobjectinvoke(objectobj,methodmethod,object[]args)
obj:一般是指代理类
method:是被代理的方法
args为该方法的参数数组。
这个抽象的方法在代理类中动态实现。
(2)proxy:该类即为动态代理类
statixobjectnewproxyinstance(classloaderloader,class[]interfaces,invocationhandlerh)
返回甙类类的一个实例,返回后的代理类可以当做被代理类使用(可以使用被代理类在接口中声明过的方法);
实现实例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
@ timehandler public class timehandler implements invocationhandler { public timehandler(object target) { super (); this .target = target; } private objecttarget; /* * 参数: * proxy 被代理对象 * method 被代理对象的方法 * args 方法的参数 * * 返回值: * object 方法返回值 */ @override public object invoke(object proxy, method method,object[] args) throws throwable { long starttime=system.currenttimemillis(); system.out.println( "汽车开始行驶……" ); method.invoke(target); long endtime=system.currenttimemillis(); system.out.println( "汽车结束行驶……时间:" +(endtime-starttime)+ "ms" ); return null ; } } |
1
2
3
4
5
|
@被代理类的接口 public interface moveable { public void move(); } @被代理的类 |
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public class car implements moveable{ @override public void move() { try { thread.sleep( new random().nextint( 1000 )); system.out.println( "……行驶中……" ); } catch (interruptedexception e) { // todo auto-generated catch block e.printstacktrace(); } } } |
@测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
public class test { /** * jdk动态代理的测试类 */ public static void main(string[] args) { car car= new car(); invocationhandler h= new timehandler(car); class <?>cls=car.getclass(); /* * loader 类加载器 * interfaces 实现接口 * h invocationhandler */ moveable m=(moveable)proxy.newproxyinstance(cls.getclassloader(),cls.getinterfaces(),h); m.move(); } } |
&&测试结果
梳理总结
所为的dynamicproxy是这样一种class:
它是在运行时生成的class,该class需要实现一组interface,使用动态代理类的时候,必须实现invocationhandler接口。
jdk动态代理的一般步骤
1.创建一个实现接口invocationhandler的类,它必须实现invoke()
2.创建被代理的类以及接口
3.调用proxy的静态方法,创建一个代理类
newproxyinstance(classloaderloader,class[]interfaces,invocationhandlerh)
4.通过代理调用方法
cglib动态代理的实现
代理实现
@引入cglib-node-2.2.jar包
@cglibproxy拦截类实现接口methodinterceptor:重写intercept拦截方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
public class cglibproxy implements methodinterceptor { private enhancerenhancer= new enhancer(); public object getproxy( class cl) { //设置创建子类的类 enhancer.setsuperclass(cl); enhancer.setcallback( this ); return enhancer.create(); } /* * 拦截所有目标类方法的调用 * object 目标类的实例 * m 目标方法的反射对象 * args 方法的参数 * proxy 代理类的实例 * */ @override public object intercept(object obj, method m,object[] args, methodproxy proxy) throws throwable { system.out.println( "日志开始……" ); //代理类调用父类的方法 proxy.invokesuper(obj, args); system.out.println( "日志结束……" ); return null ; } } |
@被代理类train
1
2
3
4
5
6
|
public class train { public void move() { system.out.println( "火车行驶中……" ); } } |
@测试类
1
2
3
4
5
6
7
8
9
10
|
public class test { /** * cglibproxy动态代理测试类 */ public static void main(string[] args) { cglibproxy proxy= new cglibproxy(); train t=(train)proxy.getproxy(train. class ); t.move(); } } |
##测试结果:
梳理总结
使用cglibproxy实现动态代理的一般步骤
1、创建类实现接口methodinterceptor,并重写intercept方法
2、创建被代理类
3、调用代理类自定义的方法,得到一个代理实例
4、通过代理实例调用被代理类的需要执行的方法
比较总结
jdk动态代理
1、只能代理实现了接口的类
2、没有实现接口的类不能实现jdk的动态代理
cglib动态代理
1、针对类来实现代理
2、对执行目标类产生一个子类,通过方法拦截技术拦截所有父类方法的调用。
模拟代理产生步骤
思路:
实现功能:通过proxy的newproxyinstance返回代理对象
1、声明一段源码(动态产生代理)
2、编译源码(jdkcompilerapi)产生新的类(代理类)
3、将这个类load到内存当中,产生一个新的对象(代理对象)
4、返回代理对象
完善动态代理实现
首先得到系统编译器,通过编译器得到文件管理者,然后获取文件,然后编译器执行编译任务,完成编译之后,将class文件加载到类加载器中,通过构造方法得到实例,然后调用newinstance()接收一个对象的实例。
(1)拿到编译器javacompilercompiler=toolprovider.getsystemjavacompiler();
(2)文件管理者standardjavafilemanagerfilemgr=compiler.getstandardfilemanager(null,null,null);
(3)获取文件iterableunits=filemgr.getjavafileobjects(filename);
(4)编译任务compilationtaskt=compiler.gettask(null,filemgr,null,null,null,units);
(5)load到内存
classloadercl=classloader.getsystemclassloader();
classc=cl.loadclass(”com.imooc.proxy.$proxy0”);
(6)通过代理对象的构造器构造实例
constructorctr=c.getconstructor(infce);
ctr.newinstance(newcar());
-------
上说所说,内部的业务逻辑是硬编码的,如何实现真正的动态代理,动态的指定业务逻辑呢?
1、需要创建一个事务处理器,首先创建一个接口也就是invocationhandler,为了模拟jdk,这里把接口的名字和jdk事务处理器名称一样,同样写一个方法叫做invoke(),用来表示对某个对象的某个方法进行业务处理,所以需要把某个对象以及对象的方法作为invoke()方法的参数传递进来,invoke(objectobj,methodmethod),方法作为参数使用到了java反射,需要把此包引入。这样invocationhandler接口就完成了。
2、创建事务处理实现类比如说时间代理timerproxy,实现了invocationhandler接口,这样结构就成了
1
2
3
4
5
6
7
8
|
——————timerproxyimplementsinvocationhandler{ ————————- @override ————————-voidinvoke(objectobj,methodmethod){ ——————————— //业务逻辑<br> —————————————method.invoke(目标对象,参数); ———————————— //业务逻辑<br> ——————————} —————————} |
需要将目标对象传入,没有参数可以不写参数,创建代理对象的构造方法,初始化目标对象
3、在proxy类的newproxyinstance()方法中,除了要把目标class接口作为参数外,还需要把事务处理器invocationhandler传进去,然后更改创建实例对象中硬编码的部分用事务处理器方法替代即可。难点在于字符串的拼接。
总结
在我们项目中代理模式有自己的实际意义,比如说我们想要调用某个jar包下的某个类,可以在调用这个类之前之后添加一些特殊的业务逻辑,这种方式也叫作aop面向切面编程。(在不改变原有功能的基础上,添加额外的功能。)
以上就是本文关于java动态代理(设计模式)代码详解的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站其他相关专题,如有不足之处,欢迎留言指出。感谢朋友们对本站的支持!
原文链接:http://blog.csdn.net/csdn_terence/article/details/52860221