【spring】静态代理&&动态代理

    xiaoxiao2021-03-25  119

    在之前在设计模式中学习过代理设计模式,现在spring中又讲解了动态代理和静态代理,这里串一下。

    一、设计模式--代理模式

     1,简介

        代理模式是23中常用设计模式之一,代理模式的定义是:为其他对象提供一种代理以控制对这个对象的访问。使用代理模式创建代理对象,让代理对象控制目标对象的访问。(目标对象可以是远程的对象、开销大的对象或者安全控制的对象),并且可以在不改变目标对象的情况下添加一些额外的功能。

    (ps:以前总觉得概念有些抽象,但是现在回头看这个概念,恩,觉得一针见血。特别是“在不改变目标对象的情况下添加一些额外的功能”这句话,下面静态代理和动态代理的例子就很好地解释了这句话。)

    代理模式类图:

    2,解决的问题是

    代理模式解决的主要问题是:直接访问对象带来的问题,目的是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类过滤消息并且转发消息,以及进行消息被委托类执行之后的后续的操作。

    3,使用要求

      为了保持行为的一致性,所以代理类和委托类需要实现相同的接口,所以在访问者看来二者并无差别。通过代理类,能够有效控制对委托对象的直接访问,也可以很好地隐藏和保护委托对象,比如:我们需要出国旅游,但是出国旅游需要办理签证,办理住宿,预订机票等各种操作。如果每个人来办理,就会自觉地特别麻烦,我们统一交给旅游社区管理,我们每个人操心的事情就特别少了。只需要带好自己的行李即可。而办理签证这些繁琐的事情统一交给旅行社来办理。我们出国旅游也就变得简单了。

     简单来说,代理解决的问题是两个类需要通信时,引入第三方代理类,将两个类的关系解耦,让我们只了解代理类,通过接口调用将具体的实现隐藏在内部。

    注意:代理类和委托类要实现相同的接口,因为代理真正调用的还是委托类的方法。

    4,两种代理类型

    静态代理:其实代理的一般模式就是静态代理的实现模式,→程序员创建的生成源代码然后对其编译。在程序运行之前.class文件就已经存在了。

    动态代理:在程序运行时通过反射机制动态建立而成。

    接下来就分别用静态代理和动态代理演示例子说明下:业务要求,在增加、添加、修改用户之前,检测操作者的安全性。

    二、静态代理

    1,userManager接口

    package com.bjpowernode.spring; public interface UserManager { public void addUser(String name,String password); public void delUser(int userId); } 2,UserManager的具体实现类UserManagerImpl.java package com.bjpowernode.spring; public class UserManagerImpl implements UserManager { public void addUser(String name, String password) { System.out.println("---------------UserManagerImpl.addUser()-------------"); } public void delUser(int userId) { System.out.println("---------------UserManagerImpl.delUser()-------------"); } } 3,代理类---代理用户管理实现类 package com.bjpowernode.spring; public class UserManagerImplProxy implements UserManager { //持有目标接口 private UserManager userManager; //因为要代理UserManager,所以得知道他的内容 //通过构造方法传入目标对象 private UserManagerImplProxy(UserManager userManager){ this.userManager=userManager; } @Override public void addUser(String name, String password) { //添加用户之前,检查安全性。 checkSecurty(); userManager.addUser(name, password); } @Override public void delUser(int userId) { checkSecurty(); userManager.delUser(userId); } //一个检测安全的方法,别的方法在使用的时候,每个方法都得调用。 public void checkSecurty(){ System.out.println("---------------check-------------"); } } 4,客户端调用 public class Client { public static void main(String[] args){ //直接调用代理类 UserManager userManager=new UserManagerImplProxy(new UserManagerImpl()); userManager.addUser("1111", "张三"); } }

    5.总结

    通过上面例子,我们可以看出静态代理的优缺点。

     优点

     实现了代理类和实现类之间的解耦,客户端只知道代理类即可。

     缺点:

        因为代理类和实现类实现了相同的接口,代理类通过委托类实现了相同的方法,所以这样出现了大量的代码重复。如果接口中增加一个方法,那么实现类和接口类中都需要增加方法,所以出现了大量重复代码。

       只能够实现一种类型的对象,如果抽象为多种类型的对象提供服务。例如上面例子的代理,只能够为UserManager这个类提供代理,如果要为BookManager类增加一个代理,那么就需要为BookManager增加一个代理类。如果项目大的话,静态代理就无法胜任了。所以静态代理只能够为特定的接口服务,如果想为多个接口服务则需要建立了很多代理类。

    三、动态代理

    通过分析静态代理的缺点,我们知道静态代理不能够动态的为多种类型的对象创建实例,所以接下来就出现了动态代理来解决这个问题。

    和静态代理在编译器时就确定被代理的对象不同,动态代理是在运行时,通过反射进制实现动态代理,所以能够带来各种类型的对象。

    1,UserManager.java接口类和UserManagerImpl.java类,和静态代理中的代码一样,此处忽略。

    2,增加SecurityHandler.java类。引入java.lang.reflect包中的Proxy类和InvocationHandler,接口提供了生成动态代理的能力。

    package com.bjpowernode.spring; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; //要实现InvoicecationHandler接口 public class SecurityHandler implements InvocationHandler{ //定义为object类型,传过来是哪个类,就是哪个类。 //给我目标,我根据目标创建代理。 //建立一个目标的实例。目标是一个Java对象,更加通用性。而不仅仅是UserManager,只要是object都可以生成代理。 private Object targetObject; public Object createProxyInstance(Object targetOjbect) { //现在的行参==传输过来的实参 this.targetObject=targetOjbect; //根据目标生成代理。 //根据传入创建不同的实例,看一下源码参数 return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(),//代理其实是拿到目标的接口生成。 this); //this,拿到当前对象 } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //检查安全性控制,类似于Filter。 checkSecurty(); //调用目标方法 Object ret=method.invoke(targetObject, args);//第一个参数,目标对象。第二个是参数:多个还是一个。 return ret; } //一个检测安全的方法,别的方法在使用的时候,每个方法都得调用。 public void checkSecurty(){ System.out.println("---------------check-------------"); } }

    3,客户端代码Client.java

    package com.bjpowernode.spring; public class Client { public static void main(String[] args){ SecurityHandler hander= new SecurityHandler(); //传入一个要代理的实现,根据这个实现,找到相应的接口。现在UserManager是指向一个代理类 UserManager userManager=(UserManager)hander.createProxyInstance(new UserManagerImpl()); //调用添加方法 userManager.addUser("张三", "123"); } } 可以看出,我们可以通过SecurityHandler代理不同的类型的类,如果我们把对外的接口都通过动态代理来实现,那么所有函数的调用最终都会经过Invoke函数的转发。所以我们就可以在这里做些自己想做的操作。比如日志系统、权限控制等,这也就是AOP的基本原理实现。

    4,动态代理优点:

    可以通过一个代理类,完成所有的代理工作,不需要像静态代理一样需要一个个来代理。

    总结:

    静态代理和动态代理都是实现了客户端跟实现类的解耦,但是动态代理通过对类的进一步抽象和封装,使其复用性进一步得到了提高。通过对动态代理的学习,更加了解了AOP ,下一篇我们来聊聊AOP。

    转载请注明原文地址: https://ju.6miu.com/read-19081.html

    最新回复(0)