欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

简说设计模式——代理模式(续)

程序员文章站 2022-04-18 09:45:23
之前说了代理模式,即为其他对象提供一种代理以控制对这个对象的访问,详情见《简说设计模式——代理模式》,而代理模式常见的实现方式为静态代理及动态代理。 一、静态代理 所谓静态代理类是指:由程序员创建或由特定工具自动生成源代码,再对其进行编译。在程序运行之前,代理类的.class文件就已经存在了。UML ......

  之前说了代理模式,即为其他对象提供一种代理以控制对这个对象的访问,详情见,而代理模式常见的实现方式为静态代理及动态代理。

一、静态代理

  所谓静态代理类是指:由程序员创建或由特定工具自动生成源代码,再对其进行编译。在程序运行之前,代理类的.class文件就已经存在了。uml结构图如下:

简说设计模式——代理模式(续)

  这里以持久化层的数据交互类为例,iuserdao是与数据库进行交互的接口,userdao为iuserdao接口的实现类,userdaoproxy为代理类,二者皆实现了iuserdao接口。说具体点就是,userdao类是iuserdao接口的具体实现者,而userdaoproxy类是通过调用userdao类的相关方法来提供特定服务的。

  1. iuserdao接口

  这里为抽象目标类/接口。

1 public interface iuserdao {
2     
3     public void save();
4 
5 }

   2. userdao接口

  这里为具体目标类或被代理的对象。实现了iuserdao中的接口方法。

1 public class userdao implements iuserdao {
2 
3     @override
4     public void save() {
5         system.out.println("数据已保存!!!");
6     }
7     
8 }

   3. 静态代理类

  首先在静态代理类中引入iuserdao接口,通过调用userdao类的相关方法来提供特定服务。

 1 public class userdaoproxy implements iuserdao {
 2     
 3     private iuserdao iuserdao;
 4 
 5     public userdaoproxy(iuserdao iuserdao) {
 6         this.iuserdao = iuserdao;
 7     }
 8     
 9     @override
10     public void save() {
11         system.out.println("开始事务...");
12         iuserdao.save();    //执行目标对象
13         system.out.println("提交事务...");
14     }
15 
16 }

  4. client客户端

1 public class client {
2     
3     public static void main(string[] args) {
4         userdao userdao = new userdao();
5         userdaoproxy proxy = new userdaoproxy(userdao);
6         proxy.save();
7     }
8 
9 }

  运行结果如下:

  简说设计模式——代理模式(续)

  总结一下就是,在代理类中注入依赖,即引入需要代理的实体类,通过代理类来调用实体类中的方法来实现静态代理。

  虽然静态代理可以很好的对目标对象进行功能扩展,但对每一个服务都需要建立代理类,工作量较大且不易管理,而且如果接口发生改变的话,代理类也得进行相应的修改,这时动态代理的作用就显现出来了。

二、动态代理

  动态代理与静态代理的区别在于:在程序运行时,动态代理类是运用创建而成的。在的最后有提到用反射来代理switch语句进行选择,这里就运用到了类似的思想。

  通过动态代理,我们不再需要手动创建代理类,只需编写一个动态处理器即可,而真正的代理对象由jdk在运行时帮我们创建。所以我们也将之称为jdk动态代理。

  下面看一个例子,uml结构图如下:

  简说设计模式——代理模式(续)

  1. ibusiness接口

  被代理的接口。

1 public interface ibusiness {
2 
3     public void dowork();
4     
5 }

   2. business类

  具体实现类/被代理的对象。

1 public class business implements ibusiness {
2 
3     @override
4     public void dowork() {
5         system.out.println("进行业务逻辑处理");
6     }
7 
8 }

   3. businesshandler类

  businesshandler类实现类invocation接口,它是方法调用接口,声明了负责调用任意一个方法的invoke()方法,参数proxy指定动态代理类实例,参数method指定被调用的方法,参数args指定向被调用方法传递的参数,而invoke()方法的返回值表示被调用方法的返回值。其中 method.invoke(ibusiness, args) 相当于 ibusiness.method(args) 。

 1 public class businesshandler implements invocationhandler {
 2     
 3     private ibusiness ibusiness;
 4     
 5     public businesshandler(ibusiness ibusiness) {
 6         this.ibusiness = ibusiness;
 7     }
 8 
 9     @override
10     public object invoke(object proxy, method method, object[] args) throws throwable {
11         system.out.println("before");
12         method.invoke(ibusiness, args);
13         system.out.println("after");
14         return null;
15     }
16 
17 }

   4. client客户端

 1 public class client {
 2     
 3     public static void main(string[] args) {
 4         business business = new business();
 5         
 6         //生成代理类对象
 7         ibusiness proxy = (ibusiness) proxy.newproxyinstance(
 8                 business.getclass().getclassloader(), 
 9                 business.getclass().getinterfaces(), 
10                 new businesshandler(business));
11         
12         proxy.dowork();
13     }
14 
15 }

   此处通过java.lang.reflect.proxy类的newproxyinstance()方法来生成代理类对象,它的完整定义如下:

   public static object newproxyinstance(classloader loader, class<?>[] interfaces, invocationhandler handler) throws illegalargumentexception 

  参数loader指定动态代理类的类加载器,参数interfaces指定动态代理类需要实现的所有接口,参数handler指定与动态代理类相关联的invocationhandler对象。所以我们只需调用newproxyinstance()方法就可以某一个对象的代理对象了。(有关classloader类加载器的内容这里就不再赘述了,它的作用是将class文件加载到jvm虚拟机中去)。

  运行结果如下:

  简说设计模式——代理模式(续)

  相比于静态代理,动态代理的优势还是很明显的,不仅减少了对业务接口的依赖,还降低了耦合度,但它还是无法摆脱对接口的依赖。那么对于没有接口的类应该如何实现动态代理呢?

三、cglib代理

  cglib是一个强大的高性能代码生成包,底层是通过使用一个小而快的字节码处理框架asm来转换并生成新的类,所以我们一般也称之为cglib字节码生成。

  与jdk动态代理不同,cglib是针对类来实现代理的,所以对于没有接口的类我们可以通过cglib字节码生成来实现代理。原理是对指定的业务类生成一个子类,并覆盖其中的业务方法实现代理。但因为采用的是继承,所以不能对final修饰的类进行代理。

  下面看一个使用cglib进行代理的实例,需先导入相应的jar包(asm及cglib包)。

  1. 被代理类

  首先定义业务类,无需实现接口。

1 public class hello {
2     
3     public void sayhello() {
4         system.out.println("hello world!");
5     }
6 
7 }

  2. 拦截器

  定义一个拦截器,通过实现methodinterceptor接口的intercept()方法来实现回调方法,通过invokesuper()执行目标对象的方法。

 1 public class hellomethodinterceptor implements methodinterceptor {
 2 
 3     @override
 4     public object intercept(object object, method method , object[] objects , methodproxy methodproxy ) throws throwable {
 5         system.out.println("before " + method.getname());
 6         methodproxy.invokesuper(object, objects);
 7         system.out.println("after " + method.getname());
 8         return null;
 9     }
10 
11 }

   3. client客户端

  通过enhancer加强类来创建动态代理类,通过它的setsuperclass()方法来指定要代理的业务类(即为下方生成的代理类指定父类),然后通过create()方法生成代理类对象。

 1 public class client {
 2     
 3     public static void main(string[] args) {
 4         enhancer enhancer = new enhancer();        //工具类
 5         enhancer.setsuperclass(hello.class);    //继承被代理类
 6         enhancer.setcallback(new hellomethodinterceptor());        //设置回调
 7         
 8         hello hello = (hello) enhancer.create();    //生成代理类对象
 9         hello.sayhello();
10     }
11 
12 }

   运行结果如下:

  简说设计模式——代理模式(续)

  综上所述,cglib采用的是动态创建子类的方法,所以对final修饰的类不能进行代理。以spring aop编程为例,jdk动态代理及cglib代理的区别在于,有接口的目标对象采用jdk代理,无接口的目标对象采用cglib代理。

 

  源码地址:https://gitee.com/adamjiangwh/gof