代理模式怎么设计 (一些比较好的代理模式)

1.代理模式是什么:

代理模式是Java常见的设计模式之一。所谓代理模式是指客户端并不直接调用实际的对象,而是通过调用代理,来间接地调用实际的对象。

2.为什么要使用代理模式:

一般是因为客户端不想直接访问实际的对象,或者访问实际的对象存在困难,因此通过一个代理对象来完成间接的访问。

3.代理模式种类:

代理模式主要分为两类,静态代理和动态代理。静态代理比较简单,是由程序员编写的代理类,并在程序运行前就编译好的,而不是由程序动态产生代理类,这就是所谓的静态。

一、静态代理又分为聚合式静态代理继承式静态代理

举个管理员网站记录操作日志的例子:

1.1、聚合式静态代理:

1.1.1、创建一个管理员接口

/**
 * 场景:管理员网站记录操作日志
 * 方式一:聚合式静态代理
 * 创建一个管理员接口
 */
public interface Manager {
    void recordLog();
}

1.1.2、创建一个管理员类实现管理员接口

/**
 * 真实管理员类
 */
public class Admin implements Manager {
    @Override
    public void recordLog() {
        System.out.println("处理记录日志开始结束之间的业务逻辑。。。");
    }
}

1.1.3、创建一个聚合方式的代理类

/**
 * 以聚合方式实现的静态代理
 */
public class PolyAdmin implements Manager {
 
    private Admin admin;
 
    public PolyAdmin(Admin admin) {
        this.admin = admin;
    }
 
    @Override
    public void recordLog() {
        System.out.println("Admin 聚合式记录日志开始。。");
        admin.recordLog();
        System.out.println("Admin 聚合式记录日志结束。。");
    }
}

1.1.4、测试代码:

/**
 * 测试聚合式静态代理
 */
public class TestPolyAdmin {
    public static void main(String[] args) {
        Manager proxyManager = new PolyAdmin(new Admin());
        proxyManager.recordLog();
    }
}
输出结果:
Admin 聚合式记录日志开始。。
处理记录日志开始结束之间的业务逻辑。。。
Admin 聚合式记录日志结束。。

1.2、继承式静态代理

1.2.1、创建一个继承式静态代理类:

/**
 * 继承式静态代理
 */
public class ProxyAdmin extends Admin{
 
    @Override
    public void recordLog() {
        System.out.println("Admin 继承式记录日志开始。。");
        super.recordLog();
        System.out.println("Admin 继承式记录日志开始。。");
    }
}

1.2.2、测试代码:

/**
 * 测试继承式静态代理
 */
public class TestProxyAdmin {
    public static void main(String[] args) {
        ProxyAdmin proxyAdmin = new ProxyAdmin();
        proxyAdmin.recordLog();
    }
}

静态代理模式总结:

优缺点:通过上面的代理代码,我们可以看出代理模式的特点,代理类接受一个Manager接口的对象,任何实现该接口的对象,都可以通过代理类进行代理,增加了通用性。但是也有缺点,每一个代理类都必须实现一遍委托类(也就是Admin)的接口,如果接口增加方法,则代理类也必须跟着修改。其次,代理类每一个接口对象对应一个委托对象,如果委托对象非常多,则静态代理类就非常臃肿,难以胜任。

二、动态代理:动态代理又分为jdk动态代理cglib动态代理

2.1、jdk动态代理步骤:

  1. 创建一个实现InvocationHandler接口的类,它必须实现invoke()方法
  2. 创建被代理的类及接口
  3. 调用Proxy的静态方法,创建一个代理类
  4. 通过代理调用方法
import com.test.Manager;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
 
public class DynamicProxy implements InvocationHandler {
 
    private Object target;
 
    public DynamicProxy(Object target) {
        this.target = target;
    }
 
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Admin 动态代理打印日志开始。。。");
        method.invoke(target,args);
        System.out.println("Admin 动态代理打印日志结束。。。");
        return null;
    }
}

2.1.1、测试代码:

import com.test.Admin;
import com.test.Manager;
import java.lang.reflect.Proxy;
 
public class TestDynamicProxy {
    public static void main(String[] args) {
        //需要代理的真实对象
        Manager manager = new Admin();
        //创建中介类
        DynamicProxy dynamicProxy = new DynamicProxy(manager);
        //获取类加载器
        Class clazz=manager.getClass();
        /**
         *loader 类加载器
         *interfaces 实现接口
         *h InvocationHandler
         */
        Manager proxy = (Manager) Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),dynamicProxy);
        proxy.recordLog();
    }
}

2.1.2、jdk动态代理总结:

JDK动态代理有一个强制性要求,就是被代理的类必须实现了某一个接口,或者本身就是接口。

在测试代码中,Proxy.newProxyInstance()方法需要3个参数:类加载器(要进行代理的类)、被代理类实现的接口,事务处理器。所以先实例化Admin,实例化InvocationHandler的子类DynamicProxy,将各参数传入Proxy的静态方法newProxyInstance()即可获得manager的代理类,前面的静态代理,代理类是我们编写好的,而动态代理则不需要我们去编写代理类,是在程序中动态生成的。

2.2、cglib动态代理

举个例子:

2.2.1、创建一个老师即将要来了的类:

public class Teacher {
    public void move() {
        System.out.println("Teacher is coming 。。。。。");
    }
}

2.2.2、创建cglib代理的实现类:

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
 
/**
 * 基于spring的cglib的继承式 动态代理,
 */
public class CglibDynamicProxy implements MethodInterceptor {
    private Enhancer enhancer = new Enhancer();

    public Object getProxy(Class<?> clazz) {
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        return enhancer.create();
    }
 
    /**
     * 拦截所有目标类方法的调用
     * 参数:
     * obj目标实例对象
     * method 目标方法的反射对象
     * args方法的参数
     * proxy代理类的实例
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
 
        System.out.println("cglib 动态代理之老师还没来。。。。");
        methodProxy.invokeSuper(o,objects);
        System.out.println("cglib 动态代理之老师已经来了。。。。");
        return null;
    }
}

2.2.3、创建测试类:

public class TestCglibDynamicProxy {
    public static void main(String[] args) {
        CglibDynamicProxy cglibDynamicProxy = new CglibDynamicProxy();
        Teacher train = (Teacher) cglibDynamicProxy.getProxy(Teacher.class);
        train.move();
    }
}

总结:

  • 动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理。
  • 在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样对每一个方法或方法组合进行处理。
  • 不过JDK的Proxy仅支持 interface 代理。Java 的单继承机制注定了这些动态代理类们无法实现对 class 的动态代理。
  • 好在Spring有cglib为实现了class类的动态代理。典型应用:AOP编程

代理模式的例子分析,一些比较好的代理模式

三种代理方式对比