设计模式之代理模式and装饰者模式

本文探讨了代理模式的静态代理和动态代理(包括JDK动态代理和CGLib动态代理)以及它们的实现原理。静态代理通过创建接口并实现相同方法,动态代理则依赖反射,在运行时动态生成代理类。文章还提到了静态代理与装饰者模式的相似之处,但指出它们的目的不同:静态代理用于调用被代理对象的方法,而装饰者模式是为了增强方法的能力。最后,通过开发游戏的例子说明了两者在实践中的差异。
摘要由CSDN通过智能技术生成

注意:本文不讲具体的概念,只是谈谈自己的一些想法。


代理模式分为:静态代理,动态代理(jdk动态代理,cglib动态代理)

静态代理:静态代理就是在开发的时候直接编码到程序中,也就是在编码的时候,我们就已经确定了被代理的对象等信息。

静态代理思路:创建一个接口,代理类和被代理类都实现该接口,然后代理类还要持有本代理类的一个实例。

                            持有被代理类实例的目的:是为了能够在代理类中进行本代理类方法的调用,因为代理模式最终的目的还是调用被代理类的方法。

                            实现同一接口的目的:是为了代理类和被代理类都具有相同的方法,然后在代理类的方法中调用被代理类相同的方法,而且我们可以在

                                                                      代理类中调用被代理类该方法的前后实现一些逻辑,例如:权限控制等。因此调用代理类方法的时候,也会

                                                                      调用 被代理类相同的方法。

动态代理

jdk动态代理:jdk自带的动态代理,主要是利用反射实现,可以运行时动态进行生成。但是该实现必须要依赖于接口。

                       注意上面所说的动态生成,这里的动态生成指的是什么呢?

                       指的就是我们在使用动态代理的时候,会自动在代理类中给我们生成与被代理类相同的方法,其中包括该接口中定义的所有方法,以及属于

                      object类的equals,hashcode,toString方法。

下面我们来看一个例子:

首先创建一个StudentFacade接口和studdentFacadeImpl实现类。

调用方法:                         

public static void main(String[] args) {  
        StudentFacadeProxy proxy = new StudentFacadeProxy();    // 代理类
        StudentFacade studentProxy = (StudentFacade) proxy.bind(new StudentFacadeImpl()/**被代理类*/);  
        studentProxy.study();    // 通过代理类调用study方法
    }  

注意:代码中的代理类之所以可以转为StudentFacade,是因为自动生成的代理类会根据我们传递的接口自动生成该接口的所有方法,也就是说自动生成的代理类
      中其实把接口中的所有方法都实现了一遍(下面会具体说明)

StudentFacadeProxy实现:

public class StudentFacadeProxy implements InvocationHandler {      // jdk动态代理要实现InvocationHandler接口
    private Object target;  
  
    // 创建代理时需要传递的参数:类加载器(传递被代理类的类加载器),被代理类实现的所有接口(用于动态生成代理类中的方法),实现了
       InvocationHandler接口的类(因为这个类本身就是,所有这里将this自身当做参数)
   public Object bind(Object target) {  
        this.target = target;  
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), this);
    }  
  
    @Override  
    public Object invoke(Object proxy, Method method, Object[] args)   // invoke方法,自动调用,无需我们手动调用(下面会说明在哪调用的)
            throws Throwable {  
        Object result=null;  
        System.out.println("方式执行之前");
        result=method.invoke(target, args);   // 执行方法(不了解的可以看看java反射)
        System.out.println("方法执行之后");  
        return result;  
    }  
  
}  
newProxyInstance源码:

    public static Object newProxyInstance(ClassLoader loader,    
            Class<?>[] interfaces,    
            InvocationHandler h)    
    throws IllegalArgumentException    
    {    
        if (h == null) {    
            throw new NullPointerException();    
        }    
        
        /*  
         * Look up or generate the designated proxy class.  
         */    
        Class cl = getProxyClass(loader, interfaces);    // 通过类加载器和接口得到代理类的字节码文件
        
        /*  
         * Invoke its constructor with the designated invocation handler.  
         */    
        try {    
               /*  
                * Proxy源码开始有这样的定义:  
                * private final static Class[] constructorParams = { InvocationHandler.class };  
                * cons即是形参为InvocationHandler类型的构造方法  
               */    
            Constructor cons = cl.getConstructor(constructorParams);     // 通过反射得到形参为InvocationHandler的构造器
            return (Object) cons.newInstance(new Object[] { h });        // 通过上面的构造器创建出代理对象的实例
        } catch (NoSuchMethodException e) {    
            throw new InternalError(e.toString());    
        } catch (IllegalAccessException e) {    
            throw new InternalError(e.toString());    
        } catch (InstantiationException e) {    
            throw new InternalError(e.toString());    
        } catch (InvocationTargetException e) {    
            throw new InternalError(e.toString());    
        }    
    }    

自动生成的代理类的源码:

    public final class $Proxy0 extends Proxy implements StudentFacade{    
        private static Method m1;    
        private static Method m0;    
        private static Method m3;    
        private static Method m2;    
        
        static {    
            try {    
              // 这里就是获取接口中的方法以及object的方法,因为invoke方法中需要传递该method
                m1 = Class.forName("java.lang.Object").getMethod("equals",    
                        new Class[] { Class.forName("java.lang.Object") });    
        
                m0 = Class.forName("java.lang.Object").getMethod("hashCode",    
                        new Class[0]);    
        
                m3 = Class.forName("***.StudentFacade").getMethod("study",    
                        new Class[0]);    
        
                m2 = Class.forName("java.lang.Object").getMethod("toString",    
                        new Class[0]);    
        
            } catch (NoSuchMethodException nosuchmethodexception) {    
                throw new NoSuchMethodError(nosuchmethodexception.getMessage());    
            } catch (ClassNotFoundException classnotfoundexception) {    
                throw new NoClassDefFoundError(classnotfoundexception.getMessage());    
            }    
        }   
        
        public $Proxy0(InvocationHandler invocationhandler) {    
            super(invocationhandler);    
        }    
        
        // 根据接口就可以知道有哪些方法需要被代理
        @Override    
        public final boolean equals(Object obj) {    
            try {    
                return ((Boolean) super.h.invoke(this, m1, new Object[] { obj })) .booleanValue();    
            } catch (Throwable throwable) {    
                throw new UndeclaredThrowableException(throwable);    
            }    
        }    
        
        @Override    
        public final int hashCode() {    
            try {    
                return ((Integer) super.h.invoke(this, m0, null)).intValue();    
            } catch (Throwable throwable) {    
                throw new UndeclaredThrowableException(throwable);    
            }    
        }    
        
        public final void study() {                  // 第一段代码中调用的study()方法其实就是调用的这个方法
            try {    
                super.h.invoke(this, m3, null);       // 这里自动调用的上面的invoke方法,m3是上面通过反射获取到的study的method
                return;    
            } catch (Error e) {    
            } catch (Throwable throwable) {    
                throw new UndeclaredThrowableException(throwable);    
            }    
        }    
        
        @Override    
        public final String toString() {    
            try {    
                return (String) super.h.invoke(this, m2, null);    
            } catch (Throwable throwable) {    
                throw new UndeclaredThrowableException(throwable);    
            }    
        }    
    }    

执行逻辑就是:通过代理类调用方法(调用的方法是自动生成的代理类中根据接口生成的方法) --> 在方法中调用invoke()方法,即我们实现了

InvocationHandler的类中我们覆写的invoke()方法 -=> 执行invoke方法中我们实现的逻辑以及通过反射调用被代理类中我们调用的方法


cglib动态代理:原理就是生成一个被代理目标的子类,然后覆盖目标中的方法(需要注意的是,对于final类型的类不能使用cglib代理,final类型

                             的方法也不能被覆写,但是这种方式可以不用实现接口)

实例代码:

public class ProxyCglib implements MethodInterceptor {     // 实现MethodInterceptor接口 
    private Object target;  
  
    public Object getInstance(Object target) {  
        this.target = target;  
        Enhancer enhancer = new Enhancer();  
        enhancer.setSuperclass(this.target.getClass());  
        enhancer.setCallback(this);   // 设置回调(就是实现MethodInterceptor接口的类)
        return enhancer.create();     // 创建代理对象
    }  
      
    @Override   
    public Object intercept(Object obj, Method method, Object[] args,  
            MethodProxy proxy) throws Throwable {  
        System.out.println("方法调用之前");  
        proxy.invokeSuper(obj, args);       // 调用实现类中的该方法
        System.out.println("方法调用之后");  
        return null;  
  
  
    }  
  
}  


最后说一下:静态代理模式和装饰者模式的区别

相信很多人和我一样,刚开时的时候感觉这两种设计模式实际写法没太大区别啊,为什么还要搞成两种设计模式呢?

慢慢我了解到,这两种设计模式虽然思路相同,但是目的却是不同的。

静态代理模式目的是为了调用被代理对象中的方法。而装饰者模式是为了增强这个方法的能力,最终目的是调用被装饰之后的对象

也就是静态代理是为了通过别人去代理完成这件事,而装饰者模式是通过增强自身去完成这件事。

举例说明一下:

就好比一个程序员要开发一个游戏,可以编写这个游戏的语言没有学过。那么他将有两个不同的做法:

1、自己学习这种语言,然后开发这款游戏。

2、找一个外包公司去帮助自己开发这款游戏。

方法一就属于是装饰者模式,方法二就属于是静态代理模式。


以上纯属学习中自己的见解,如有不对的地方欢迎留言指正。



 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值