Java设计模式-代理模式

设计模式之代理模式

代理模式是设计模式的一种,简单解释就是不直接访问目标对象,通过访问代理对象就可以实现对目标对象的访问。就像现在买火车票,不用直接去火车站买,可以直接去各个代售点或者APP上购买,这里的代售点或者APP就是火车站的代理,这样做的好处是,不用修改目标对象,可以在代理对象中增加额外的操作,达到扩展目标对象的目的

Java中主要有三种方式:静态代理,JDK动态代理,cglib代理。前两种代理方式都是通过接口代理,cglib可以实现代理类。

静态代理

静态代理中代理对象和被代理对象都需要实现同一个接口,才能达到代理的目的。

这样做就会存在不好的地方,当修改接口时,代理对象和被代理对象都需要修改,耦合性太大,不容易维护,同时可能会产生过多的代理类。

静态代理代码实现

定义一个接口:

public interface IBaseDao {
    //存储数据方法
    void save();
}

需要代理的目标类,需要实现IBaseDao接口:

public class StaticProxyTarget implements IBaseDao {
    @Override
    public void save() {
        System.out.println("save data");
    }
}

代理类,需要实现IBaseDao接口:

public class StaticProxy implements IBaseDao {
    //代理目标对象
    private StaticProxyTarget target;
    //通过构造函数传入代理对象
    public StaticProxy(StaticProxyTarget target) {
        this.target = target;
    }

    @Override
    public void save() {
        System.out.println("start transaction");
        target.save();
        System.out.println("commit transaction");
    }
}

测试静态代理:

public class ProxyTest {
    public static void main(String[] args) {
        //目标对象
        StaticProxyTarget staticProxyTarget = new StaticProxyTarget();
        //代理对象
        StaticProxy staticProxy = new StaticProxy(staticProxyTarget);
        staticProxy.save();
    }
}

输出结果:

start transaction
save data
commit transaction

JDK动态代理

JDK动态代理是利用Java API,动态的生成代理对象,达到代理目标对象的作用。

与静态代理不同的是,动态代理是在Java运行时动态生成字节码,并加载到jvm中运行,没有.class文件,静态代理编译后会产生.class文件。

定义一个接口:

public interface IBaseDao {
    void save();
}

需要代理的目标类,需要实现IBaseDao接口:

public class DynamicProxyTarget implements IBaseDao{

    @Override
    public void save() {
        System.out.println("save data");
    }
}

动态代理类:
其中Proxy提供了用于创建动态代理对象的static方法。
主要用到了下面这个方法,可以直接创建一个动态代理对象,该代理对象的实现类实现了interfaces指定的系列接口,执行代理对象的每个方法时都会被替换成InvocationHandler的invoke方法。

static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
public class DynamicProxy &#123;
    //需要代理的目标对象
    private Object target;

    public DynamicProxy(Object target) &#123;
        this.target = target;
    &#125;

    public Object getProxyInstance() &#123;
        /**
         * 执行动态代理对象的方法时,将会执行InvocationHandler的invoke方法
         * 其中三个参数为
         * proxy:动态代理对象
         * method:代表正在执行的方法
         * args:调用目标对象方法时传入的参数
         */
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), (proxy, method, args) -> &#123;
            System.out.println("start transaction");
            //调用目标对象的方法
            Object result = method.invoke(target, args);
            System.out.println("commit transaction");
            return result;
        &#125;);
    &#125;
&#125;

测试JDK动态代理:

public class ProxyTest &#123;
    public static void main(String[] args) &#123;
        //动态代理测试
        IBaseDao baseDao = new DynamicProxyTarget();
        IBaseDao dynamicProxy = (IBaseDao) new DynamicProxy(baseDao).getProxyInstance();
        dynamicProxy.save();
    &#125;
&#125;

结果输出:

start transaction
save data
commit transaction

cglib代理

cglib (Code Generation Library )是一个第三方代码生成类库,可以在运行时在内存中动态生成一个子类对象,从而实现对目标对象功能的扩展。Spring框架中的AOP就使用了cglib。

cglib和上面两种代理最大的不同就是,被代理类不需要实现接口,就可以实现对目标对象的代理,代码侵入性更小。

需要代理的目标类:

public class CglibProxyTarget &#123;
    public void save() &#123;
        System.out.println("save data");
    &#125;
&#125;

cglib代理类,需要实现MethodInterceptor接口:

public class CglibProxy implements MethodInterceptor &#123;
    private CglibProxyTarget target;

    public CglibProxy(CglibProxyTarget target) &#123;
        this.target = target;
    &#125;

    //生成代理对象
    public Object getProxyInstance() &#123;
        Enhancer enhancer = new Enhancer();
        //指定父类
        enhancer.setSuperclass(target.getClass());
        //设置回调
        enhancer.setCallback(this);

        return enhancer.create();
    &#125;

    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable &#123;
        System.out.println("start transaction");
        //调用目标对象的方法
        Object result = method.invoke(target, args);
        System.out.println("commit transaction");
        return result;
    &#125;
&#125;

测试结果:

public class ProxyTest &#123;
    public static void main(String[] args) &#123;
        //cglib代理测试
        CglibProxyTarget cglibProxyTarget = new CglibProxyTarget();
        cglibProxyTarget = (CglibProxyTarget) new CglibProxy(cglibProxyTarget).getProxyInstance();
        cglibProxyTarget.save();
    &#125;
&#125;

结果输出:

start transaction
save data
commit transaction

总结

  • 静态代理代理对象和被代理对象都需要实现同一个接口,实现简单,但是耦合性太大,不便于维护。
  • JDK动态代理需要被代理对象实现业务接口,代理对象中实现InvocationHandler接口,通过Java反射生成代理,但是动态生成的代理更加灵活。
  • 静态代理编译后产生.class文件,比JDK反射性能好,cglib是通过字节码生成代理,性能高于反射,但是cglib会生成子类继承被代理对象,所以被代理对象不能为final。

代码很简单,需要理解的是思想,代理模式运用广泛,很多框架中都使用了代理模式,只是设计更复杂,个人笔记,如有不对请指出。


   转载规则

本文不允许转载。
 上一篇
Spring之IOC的注入方式 Spring之IOC的注入方式
Spring之IOC的注入方式 在java中,要使用一个对象,必须先创建一个实例,但是有了IOC之后,对象的创建与销毁都交给了IOC容器,不用我们手动创建,而是直接从IOC容器中获取,达到了解耦的效果。IOC是一种思想,在Spring中,实
2018-05-09
下一篇 
Redis3集群搭建(下)- 实操 Redis3集群搭建(下)- 实操
Redis3集群搭建(下)- 实操上一篇博客学习了一些Redis集群的基础知识,这篇文章将会开始学习搭建一个3主3从的小型的Redis集群。 准备 Redis 3.2.9 一台机器,Linux或者macOS 我本机是macOS,所以这
2018-05-07
  目录