设计模式之代理模式:三种代理模式的实现方式

设计模式之代理模式:三种代理模式的实现方式

    前言:代理模式和另外一种设计模式--装饰者模式十分相像,他们都是在不修改目标对象源代码的基础上,对源代码的一次重构。只是,代理模式,关注的是目标对象的使用,他有目标的控制权,而装饰者模式,只是对目标对象功能的增强,仅此而已,不拥有目标对象的控制权。

    代理模式有三种实现方式:

  1. 继承目标对象的同一个接口,引入目标对象,重写方法;
  2. 使用JDK动态代理的API,动态生成一个动态代理对象的实例;
  3. 使用CGlib,在不需要传入接口的条件下,就可以重构代码;

    一、土味代理模式

    所谓土味代理模式,就是最原生的实现方式,不使用任何API。

    步骤:

  1. 继承目标对象的接口;
  2. 引入目标对象的成员变量,并重写目标对象的方法;

    代码:

    1.目标对象源码:

/**
 * 接口实现
 * 目标对象
 */
public class UserDao implements IUserDao {
    public void save() {
        System.out.println("----已经保存数据!----");
    }
}

    2.代理对象源码:

public class UserDaoProxy implements IUserDao{
    //接收保存目标对象
    private IUserDao target;
    public UserDaoProxy(IUserDao target){
        this.target=target;
    }

    public void save() {
        System.out.println("开始事务...");
        target.save();//执行目标对象的方法
        System.out.println("提交事务...");
    }
}

    此方法实现简单,也确实完成了不修改目标对象的代码的情况下对代码重构的目的,但是缺点也十分明显,有几个目标对象要生成代理对象,就得维护几个代理对象,如果接口增加方法,那么代理对象相应的也有进行修改。

    二、JDK实现动态代理

    上述方法是静态代理的实现,接下来介绍两种动态代理的实现方式,所谓动态代理,就是动态的生成一个代理对象,但实际上并没有写一个代理对象的类。

    JDK实现动态代理就是用JDK原有的API传入目标对象的类加载器和接口,再重写代理方法,返回一个代理对象。

    代码:

    代理对象的工厂类:

/**
 * 创建动态代理对象
 * 动态代理不需要实现接口,但是需要指定接口类型
 */
public class ProxyFactory{

    //维护一个目标对象
    private Object target;
    public ProxyFactory(Object target){
        this.target=target;
    }

   //给目标对象生成代理对象
    public Object getProxyInstance(){
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("开始事务2");
                        //执行目标对象方法
                        Object returnValue = method.invoke(target, args);
                        System.out.println("提交事务2");
                        return returnValue;
                    }
                }
        );
    }

}

    JDK动态代理的确实现了动态代理,无需重写接口,多维护几个代理类,但是,需要传入接口,一样成为了限制他的缺点。

    三、CGlib实现动态代理

    CGlib实现需要先导入相应的jar包,相关的代码如下:

    动态代理工厂类:

/**
 * Cglib子类代理工厂
 * 对UserDao在内存中动态构建一个子类对象
 */
public class ProxyFactory implements MethodInterceptor{
    //维护目标对象
    private Object target;

    public ProxyFactory(Object target) {
        this.target = target;
    }

    //给目标对象创建一个代理对象
    public Object getProxyInstance(){
        //1.工具类
        Enhancer en = new Enhancer();
        //2.设置父类
        en.setSuperclass(target.getClass());
        //3.设置回调函数
        en.setCallback(this);
        //4.创建子类(代理对象)
        return en.create();

    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("开始事务...");

        //执行目标对象的方法
        Object returnValue = method.invoke(target, args);

        System.out.println("提交事务...");

        return returnValue;
    }
}

    CGlib底层是动态的在内存中生成了目标对象的子类的字节码,并生成相应的对象,JDK动态代理实现则是使用的反射机制。

    CGlib实现动态代理能满足没有接口的目标对象的代理对象实现。

    所以,在Spring中,实现AOP的AspectJ框架底层,会对无接口的目标对象采用CGlib,有接口的目标对象则采用JDK动态代理。

你可能感兴趣的:(设计模式,Spring)