zoukankan      html  css  js  c++  java
  • java代理

    java的三种代理模式

    1、什么是代理

      代理模式是一种设计模式,简单说即是在不改变源码的情况下,实现对目标对象功能扩展

    2、为什么要使用代理

    我们为什么要引入java的代理,除了当前类能够提供的功能外,我们还需要补充一些其他功能。

    最容易想到的情况就是权限过滤,我有一个类做某项业务,但是由于安全原因只有某些用户才可以调用这个类,此时我们就可以做一个该类的代理类,要求所有请求必须通过该代理类,由该代理类做权限判断,如果安全则调用实际类的业务开始处理。

    可能有人说为什么我要多加个代理类?我只需要在原来类的方法里面加上权限过滤不就完了吗?

    在程序设计中有一个类的单一性原则问题,这个原则很简单,就是每个类的功能尽可能单一。为什么要单一,因为只有功能单一这个类被改动的可能性才会最小

    如果你将权限判断放在当前类里面,当前这个类就既要负责自己本身业务逻辑、又要负责权限判断,那么就有两个导致该类变化的原因,现在如果权限规则一旦变化,这个类就必需得改,显然这不是一个好的设计。

    3、静态代理

      比如有个歌手对象叫Singer,这个对象有一个唱歌方法叫sing()

      

    1 1 public class Singer{
    2 2     public void sing(){
    3 3         System.out.println("我是周杰伦");
    4 4     }  
    5 5 }

     假如你希望,通过你的某种方式生产出来的歌手对象,在唱歌前后还要想观众问好和答谢,也即对目标对象Singer的sing方法进行功能扩展。

    1 1 public void sing(){
    2 2     System.out.println("大家好");
    3 3     System.out.println("我是周杰伦");
    4 4     System.out.println("谢谢大家");
    5 5 }  

    但是往往你又不能直接对源代码进行修改,可能是你希望原来的对象还保持原来的样子,又或许你提供的只是一个可插拔的插件,甚至你有可能都不知道你要对哪个目标对象进行扩展。这时就需要用到java的代理模式了。

     1 public interface Singer {
     2     void sing();
     3 }
     4 
     5 /**
     6  *  目标对象实现了某一接口
     7  */
     8 public class Singer implements Singer{
     9     public void sing(){
    10         System.out.println("我是周杰伦");
    11     }  
    12 }
    13 
    14 /**
    15  *  代理对象和目标对象实现相同的接口
    16  */
    17 public class SingerProxy implements Singer{
    18     // 接收目标对象,以便调用sing方法
    19     private Singer target;
    20     public UserDaoProxy(Singer target){
    21         this.target=target;
    22     }
    23     // 对目标对象的sing方法进行功能扩展
    24     public void sing() {
    25         System.out.println("大家好");
    26         target.sing();
    27         System.out.println("谢谢大家");
    28     }
    29 }

    测试类:

     1 /**
     2  * 测试类
     3  */
     4 public class Test {
     5     public static void main(String[] args) {
     6         //目标对象
     7         Singer target = new Singer();
     8         //代理对象
     9         Singer proxy = new SingerProxy(target);
    10         //执行的是代理的方法
    11         proxy.sing();
    12     }
    13 }

      总结:其实这里做的事情无非就是,创建一个代理类SingerProxy,继承了Singer接口并实现了其中的方法。只不过这种实现特意包含了目标对象的方法,正是这种特征使得看起来像是扩展了目标对象的方法。假使代理对象中只是简单地对sing方法做了另一种实现而没有包含目标对象的方法,也就不能算作代理模式了。所以这里的包含是关键。

      缺点:1、这种实现方式很直观也很简单,但其缺点是代理对象必须提前写出(在编译期就已经知道了代理对象),如果接口层发生了变化,代理对象的代码也要进行维护。如果能在运行时动态地写出代理对象,不但减少了一大批代理类的代码,也少了不断维护的烦恼,不过运行时的效率必定受到影响。这种方式就是接下来的动态代理。

         2、因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护.

    4、动态代理(JDK代理)

    跟静态代理的前提一样,依然是对Singer对象进行扩展

     /**
      * 歌手接口
      */
    1
    public interface Singer { 2 void sing(); 3 } 4

    目标对象1

     1 /**
     2  * 接口实现
     3  * 目标对象
     4  */
     5 public class Singer01 implements Singer {
     6     @Override
     7     public void sing() {
     8         System.out.println("我是刘德华");
     9     }
    10 }

    目标对象2

     1 /**
     2  * 接口实现
     3  * 目标对象
     4  */
     5 public class Singer02 implements Singer {
     6 
     7     @Override
     8     public void sing() {
     9         System.out.println("我是周杰伦");
    10     }
    11 }

    代理工厂类

     1 /**
     2  * 代理工厂类
     3  */
     4 public class ProxyFactory{
     5 
     6    //维护一个目标对象
     7     private Object target;
     8 
     9     public ProxyFactory(Object target) {
    10         this.target = target;
    11     }
    12 
    13     //给目标对象生成代理对象
    14     public Object getProxyInstance(){
    15         return Proxy.newProxyInstance(
    16                 target.getClass().getClassLoader(),
    17                 target.getClass().getInterfaces(),
    18                 new InvocationHandler() {
    19                     @Override
    20                     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    21                         System.out.println("大家好");
    22                         //执行目标对象方法
    23                         Object returnValue = method.invoke(target,args);
    24                         System.out.println("谢谢大家");
    25                         return returnValue;
    26                     }
    27                 }
    28         );
    29     }
    30 }

    测试类

     1 /**
     2  * 测试类
     3  */
     4 public class App {
     5     public static void main(String[] args){
     6         Singer01 target = new Singer01();
     7         Singer02 target02 = new Singer02();
     8 
     9         //给目标对象创建代理对象
    10         Singer proxy = (Singer) new ProxyFactory(target).getProxyInstance();
    11         Singer proxy02 = (Singer) new ProxyFactory(target02).getProxyInstance();
    12         //执行方法
    13         proxy.sing();
    14         System.out.println("----------------------------------");
    15         proxy02.sing();
    16     }
    17 }

    测试结果:

    大家好
    我是刘德华
    谢谢大家
    ----------------------------------
    大家好
    我是周杰伦
    谢谢大家

    JDK中生成代理对象的API
    代理类所在包:java.lang.reflect.Proxy
    JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数,完整的写法是:

    static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )

    注意该方法是在Proxy类中是静态方法,且接收的三个参数依次为:

    • ClassLoader loader,:指定当前目标对象使用类加载器,获取加载器的方法是固定的
    • Class<?>[] interfaces,:目标对象实现的接口的类型,使用泛型方式确认类型
    • InvocationHandler h:事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入

    总结JDK动态代理解决了静态代理中需要创建多个代理类的问题

    缺点:可以看出静态代理和JDK代理有一个共同的缺点,就是目标对象必须实现一个或多个接口,假如没有,则可以使用Cglib代理

    5、CGLIB代理

    前提条件:

    • 需要引入cglib的jar文件,由于Spring的核心包中已经包括了Cglib功能,所以也可以直接引入spring-core-3.2.5.jar
    • 目标类不能为final
    • 目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法
    1 /**
    2  * 目标对象,没有实现任何接口
    3  */
    4 public class Singer{
    5 
    6     public void sing() {
    7         System.out.println("我是周杰伦");
    8     }
    9 }
     1 /**
     2  * Cglib子类代理工厂
     3  */
     4 public class ProxyFactory implements MethodInterceptor{
     5     // 维护目标对象
     6     private Object target;
     7 
     8     public ProxyFactory(Object target) {
     9         this.target = target;
    10     }
    11 
    12     // 给目标对象创建一个代理对象
    13     public Object getProxyInstance(){
    14         //1.工具类
    15         Enhancer en = new Enhancer();
    16         //2.设置父类
    17         en.setSuperclass(target.getClass());
    18         //3.设置回调函数
    19         en.setCallback(this);
    20         //4.创建子类(代理对象)
    21         return en.create();
    22     }
    23 
    24     @Override
    25     public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
    26         System.out.println("向观众问好");
    27         //执行目标对象的方法
    28         Object returnValue = method.invoke(target, args);
    29         System.out.println("谢谢大家");
    30         return returnValue;
    31     }
    32 }

     测试类:

    /**
     * 测试类
     */
    public class Test{
        public static void main(String[] args){
            //目标对象
            Singer target = new Singer();
            //代理对象
            Singer proxy = (Singer)new ProxyFactory(target).getProxyInstance();
            //执行代理对象的方法
            proxy.sing();
        }
    }

    总结:三种代理模式各有优缺点和相应的适用范围,主要看目标对象是否实现了接口。以Spring框架所选择的代理模式举例

    在Spring的AOP编程中: 如果加入容器的目标对象有实现接口,用JDK代理 如果目标对象没有实现接口,用Cglib代理

     
     
     
     
  • 相关阅读:
    同一WpfApplication下简单的页面转换
    触发器
    程序包
    函数
    存储过程
    游标
    我的第一个SolidWorks图
    张量系列(tensor02)
    张量系列-Tensor(01)
    Python与矩阵论——特征值与特征向量
  • 原文地址:https://www.cnblogs.com/xiaocao123/p/10686424.html
Copyright © 2011-2022 走看看