设计模式之——代理模式

       代理模式:为其它对象提供一种代理,以控制对整个对象的访问。代理对象起到中介作用,可去掉某些功能或增加额外的功能。

       例如:生活中的住酒店,一般我们在途牛、携程订酒店的时候,往往会看到什么什么旅行社,其实并不是我们自己去订,而是我们下单之后,旅行社为我们去订酒店,此处的旅行社就是我们的代理。

       代理模式分类:静态代理、动态代理。

     一、静态代理

       静态代理:代理和被代理对象在代理之前是确定的,它们都实现相同的接口或者继承相同的类。也就是说代理类和被代理类都是已经写好的。

        

        Subject表示其父类或父接口,它定义了被代理对象需要做的事,也就是抽象行为。RealSubject表示被代理的对象,也就是我自己,需要去订酒店的。ProxySubject表示代理对象,也就是旅行社,真正帮我订票的人。

        下面来看看静态代理的代码。

         接口Subject:

//抽象出订房的方法,也是需要代理对象帮忙做的事(订房)
public interface Subject {
    public void reservation();
}

       代理类(旅行社):

//代理对象,也就是帮我买票的旅行社
public class ProxySubject implements Subject {
    //接受一个代理对象,也就是为谁订票。
    RealSubject realSubject;
    ProxySubject(RealSubject realSubject) {
        this.realSubject = realSubject;
    }

    public void reservation() {
        System.out.println("收到订单!");
        //注意此处,调用的是代理对象的实例去进行订票。
        realSubject.reservation();
        System.out.println("订票完成!");
    }
}

         被代理对象(订房的人):

//实现接口并重写方法
public class RealSubject implements Subject{
    public void reservation() {
        System.out.println("订票!");
    }
}

      测试:

public class Test {
    public static void  main(String[] args) {
        RealSubject realSubject = new RealSubject();
        ProxySubject proxySubject = new ProxySubject(realSubject);
        //proxySubject代理人替代理对象realSubject订房!
        proxySubject.reservation();
    }
}



结果:
收到订单!
订票!
订票完成!

        因为代理类和被代理类在编译的时候就已经确定了,所以为静态代理,静态代理的缺点就是要扩展很麻烦,需要创建很多的类和接口。

       二、JDK动态代理

        JDK动态代理:在动态代理中我们不再需要再手动的创建代理类,我们只需要编写一个事务处理器就可以了。真正的代理对象由JDK再运行时为我们动态的来创建。

      JDK动态代理是通过反射来完成的,毕竟只有反射才能在运行时获取到对象或类的信息。动态代理要求代理类必须实现一个接口,并且要实现InvocationHandler接口的invoke方法。(并不是实现这个接口,而是代理类实现的接口,是代理类实现的Subject这个接口)

       此处,为了让结构明了,我们先用一个类实现InvocationHandler接口的invoke方法,一般情况下都是使用匿名内部类实现InvocationHandler接口的invoke方法。

        接口和被代理类都没有变化,代理类ProxySubject可以删除。

        实现InvocationHandler接口: 

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
//实现事务处理器接口
public class DynamicProxyHandler implements InvocationHandler {
    //用object接收一个代理对象的实例,因为Method.invoke的方法接收的参数也是Object,所以用Object即可。
    private Object targe;
    private int day;
    //构造方法,传入一个代理对象的实例
    public DynamicProxyHandler(Object targe) {
        this.targe = targe;
    }

    /**第一个参数暂时不知道有什么用,可以忽略
     * 第三个参数表示被代理对象的方法的参数,
     * 该事务处理器一般用匿名内部类的方式实现,待会再改造,会隐式的调用该方法。
    **/
     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
         /**调用Method的invoke方法,第一个参数表示要调用该方法的对象,
          * 第二个参数表示传入该方法的形参数组。
          * 返回值为该方法的返回值,Object对象
          * 在此处,第一个参数表示被代理对象的实例,第二个参数直接写args即可。
         **/
         method.invoke(this.targe,args);
        return null;
    }
}

         在测试类中具体操作:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class Test {
    public static void  main(String[] args) {
        //创建一个被代理类的实例。
        Subject realSubject = new RealSubject();
        //创建一个事务处理器的实例,并传入被代理类的实例。
        InvocationHandler dynamicProxyHandler = new DynamicProxyHandler(realSubject);
        /**该方法会隐式的调用事务处理器的invoke方法,返回一个代理类对象
         * 该方法的第一个参数表示:传入被代理类的类加载器
         * 第二个参数表示:被代理类实现的接口,注意看,Subject.class这个类表示接口。
         * 第三个参数表示:事务处理器。
         */
         Subject proxy = (Subject) Proxy.newProxyInstance(Subject.class.getClassLoader(),
                new Class[]{ Subject.class},dynamicProxyHandler);
         //通过代理类调用代理的行为
        proxy.reservation();
        //可能有些读者会认为proxy不就是被代理类吗?其实并不是,它是一个代理类,看下面输出的结果,证明它是一个proxy的子类
        System.out.println("proxy的类型"+proxy.getClass());
    }
}


输出结果:

订房成功!
proxy的类型class com.sun.proxy.$Proxy0

          jdk的动态代理,此刻应该也明白得差不多,也就是我们并不需要去创建代理类,需要去写一个接口和一个被代理类即可。

         下面在看看匿名内部类实现jdk动态代理的操作,上面的代码,把事务处理器的实现类删除,全部在测试类中进行。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class Test {
    public static void  main(String[] args) {
        //创建一个被代理类的实例。
        final Subject realSubject = new RealSubject();
        Subject proxy = (Subject) Proxy.newProxyInstance(Subject.class.getClassLoader(),
                new Class[]{Subject.class}, new InvocationHandler() {
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        method.invoke(realSubject,args);
                        return null;
                    }
                }
        );
        proxy.reservation();
        System.out.println("proxy的类型是"+proxy.getClass());
    }
}


输出结果:

订房成功!
proxy的类型是class com.sun.proxy.$Proxy0

         使用匿名内部类是不是感觉到精简了很多。

         写了这么多,不知道你明白了没有,这就是jdk动态代理。下面继续说,动态代理的另一种形式,cglib动态代理。

      三、Cglib动态代理

          cglib是一个java的开源项目,它能够在运行期间动态的生产字节码,它是通过字节码技术生成一个被代理类的子类作为代理类,再通过方法拦截技术拦截所有父类方法的调用,原理的实现也就是通过继承,也就要求被代理类不能是被final修饰的。

           cglib要求我们实现MethodInterceptor接口并且重写intercept方法,类似jdk代理的事务处理器,在cglib中为父类拦截器,可以通过匿名内部类去实现。我们先通过一个类去实现该接口,之后再改造成匿名内部类。

           RealSubject被代理类:

//cglib不需要实现接口
public class RealSubject {
    public void reservation() {
        System.out.println("订房成功!");
    }
}

           删除掉Subject接口,因为不需要了。

           Maven导包,不会的直接去网上下载即可。

<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>2.2.2</version>
</dependency>

           创建一个类实现MethodInterceptor接口

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class Cglib implements MethodInterceptor{
    //被代理类
    private Object target;
    //传入被代理类的实例,返回值为返回被代理的对象。
    public Object getInstance(Object target) {
        this.target = target;
        //创建一个Enhancer类,该类用于创建代理类。
        Enhancer enhancer = new Enhancer();
        //为Enhancer指定要代理的类。
        enhancer.setSuperclass(target.getClass());
        //设置回调拦截,此处会调用MethodInterceptor的intercept方法,是显示调用。
        enhancer.setCallback(this);
        //创建并返回代理对象,通过默认无参的构造方法创建
        return enhancer.create();
    }
    /**
     *
     * @param object  目标类(被代理类)的实例
     * @param method  目标类(被代理类)反射获得到的方法名
     * @param args    目标类方法的参数
     * @param proxy   代理类的实例
     * @return
     * @throws Throwable
     */
    public Object intercept(Object object, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        //代理类去调用父类的方法实现动态代理
        proxy.invokeSuper(object,args);
        return null;
    }
}

             测试类

public class Test {
    public static void  main(String[] args) {
        //创建一个被代理类的实例。
        RealSubject realSubject = new RealSubject();
        //创建一个cglib实例
        Cglib cglib = new Cglib();
        //调用getInstance方法创建代理对象。
        RealSubject proxy = (RealSubject) cglib.getInstance(realSubject);
        proxy.reservation();
        //判断代理类的类型
        System.out.println(proxy instanceof RealSubject);

    }
}


输出结果:

订房成功!
true

            这也就证明了cglib是通过继承来实现动态代理的。

            下面再来看看匿名内部类的做法。

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class Test {
    public static void  main(String[] args) {
        //创建一个被代理类的实例。
        RealSubject realSubject = new RealSubject();
        //创建一个Enhancer实例
        Enhancer enhancer = new Enhancer();
        //为哪个目标类(被代理类)创建代理
        enhancer.setSuperclass(realSubject.getClass());
        //设置回调,调用MethodInterceptor的intercept方法拦截父类
        enhancer.setCallback(new MethodInterceptor() {
            public Object intercept(Object object, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                //代理类去调用父类的方法实现动态代理
                proxy.invokeSuper(object,args);
                return null;
            }
        });
        //创建并返回代理对象,通过默认无参的构造方法创建
        RealSubject proxy = (RealSubject) enhancer.create();
        proxy.reservation();
        System.out.println(proxy instanceof RealSubject);

    }
}

              好了,cglib动态代理就写这么多,当然不仅仅是这些东西,里面还有很多细节值得思考,这就需要你自己去深入研究。老铁们觉得不错的话,可以关注一波,谢谢!

猜你喜欢

转载自blog.csdn.net/m0_37914588/article/details/81812483