设计模式之代理模式
代理模式是设计模式的一种,简单解释就是不直接访问目标对象,通过访问代理对象就可以实现对目标对象的访问。就像现在买火车票,不用直接去火车站买,可以直接去各个代售点或者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 {
    //需要代理的目标对象
    private Object target;
    public DynamicProxy(Object target) {
        this.target = target;
    }
    public Object getProxyInstance() {
        /**
         * 执行动态代理对象的方法时,将会执行InvocationHandler的invoke方法
         * 其中三个参数为
         * proxy:动态代理对象
         * method:代表正在执行的方法
         * args:调用目标对象方法时传入的参数
         */
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), (proxy, method, args) -> {
            System.out.println("start transaction");
            //调用目标对象的方法
            Object result = method.invoke(target, args);
            System.out.println("commit transaction");
            return result;
        });
    }
}
测试JDK动态代理:
public class ProxyTest {
    public static void main(String[] args) {
        //动态代理测试
        IBaseDao baseDao = new DynamicProxyTarget();
        IBaseDao dynamicProxy = (IBaseDao) new DynamicProxy(baseDao).getProxyInstance();
        dynamicProxy.save();
    }
}
结果输出:
start transaction
save data
commit transaction
cglib代理
cglib (Code Generation Library )是一个第三方代码生成类库,可以在运行时在内存中动态生成一个子类对象,从而实现对目标对象功能的扩展。Spring框架中的AOP就使用了cglib。
cglib和上面两种代理最大的不同就是,被代理类不需要实现接口,就可以实现对目标对象的代理,代码侵入性更小。
需要代理的目标类:
public class CglibProxyTarget {
    public void save() {
        System.out.println("save data");
    }
}
cglib代理类,需要实现MethodInterceptor接口:
public class CglibProxy implements MethodInterceptor {
    private CglibProxyTarget target;
    public CglibProxy(CglibProxyTarget target) {
        this.target = target;
    }
    //生成代理对象
    public Object getProxyInstance() {
        Enhancer enhancer = new Enhancer();
        //指定父类
        enhancer.setSuperclass(target.getClass());
        //设置回调
        enhancer.setCallback(this);
        return enhancer.create();
    }
    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("start transaction");
        //调用目标对象的方法
        Object result = method.invoke(target, args);
        System.out.println("commit transaction");
        return result;
    }
}
测试结果:
public class ProxyTest {
    public static void main(String[] args) {
        //cglib代理测试
        CglibProxyTarget cglibProxyTarget = new CglibProxyTarget();
        cglibProxyTarget = (CglibProxyTarget) new CglibProxy(cglibProxyTarget).getProxyInstance();
        cglibProxyTarget.save();
    }
}
结果输出:
start transaction
save data
commit transaction
总结
- 静态代理代理对象和被代理对象都需要实现同一个接口,实现简单,但是耦合性太大,不便于维护。
 - JDK动态代理需要被代理对象实现业务接口,代理对象中实现InvocationHandler接口,通过Java反射生成代理,但是动态生成的代理更加灵活。
 - 静态代理编译后产生.class文件,比JDK反射性能好,cglib是通过字节码生成代理,性能高于反射,但是cglib会生成子类继承被代理对象,所以被代理对象不能为final。
 
代码很简单,需要理解的是思想,代理模式运用广泛,很多框架中都使用了代理模式,只是设计更复杂,个人笔记,如有不对请指出。