设计模式 - 代理模式

101 阅读4分钟

本文已参加【新人创作礼】活动,一起开启掘金创作之路。

概念

代理模式(Proxy Pattern):给某一个对象提供一个代理,并由代理对象控制对原对象的引用。代理模式是一种对象结构型模式。

角色

  • Subject(抽象主题角色):它声明了真实主题和代理主题的共同接口。
  • Proxy(代理主题角色):代理主题角色内部包含了对真实主题的引用。通常,在代理主题角色中,客户端在调用所引用的真实主题操作之前或之后还需要执行其他操作,而不仅仅是单纯调用真实主题对象中的操作。
  • RealSubject(真实主题角色):它定义了代理角色所代表的真实对象,中实现了真实的业务操作。

image.png

代码

class Proxy extends Subject {
    // 坚持一个真实主题对象的引用
    private RealSubject realSubject = new RealSubject();
    
    public void preRequest() {
        // 添加逻辑
    }
    public void request() {
        preRequest();
        realSubject.request(); // 调用真实对象的方法
        preRequest();
    }
    public void preRequest() {
        // 添加逻辑
    }
}

动态代理

通常情况下,每个代理类编译之后都会生成一个class文件,代理类所实现的接口和所代理的方法都被固定,这种代理被称为静态代理(Static Proxy)。 静态代理中,客户端通过Proxy类调用RealSubject类的request()方法,同时还可以在代理类中封装其他方法(例如preRequest()和postRequest()等)。如果按照这种方法使用代理模式,那么代理类和真实主题类都应该是事先已经存在的,代理类的接口和所代理方法都已明确指定。如果需要为不同的真实主题类提供代理类或者代理一个真实主题类中的不同方法,都需要增加新的代理类,这将导致系统中的类个数急剧增加,因此需要想办法减少系统中类的个数。动态代理可以让系统能够根据实际需要来动态创建代理类,让同一个代理类能够代理多个不同的真实主题类,而且可以代理不同的方法。 Java中动态代理分为JDK和CGLIB两个方式。其中JDK需要代理类实现InvocationHandler接口。

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

public class JDKProxy {

    public static void main(String[] args) {
        // 代理UserDAO
        InvocationHandler handler = null;
        AbstractUserDAO userDAO = new UserDAO();
        handler = new DAOLogHandle(userDAO);
        AbstractUserDAO proxy = null;
        proxy = (AbstractUserDAO)Proxy.newProxyInstance(AbstractUserDAO.class.getClassLoader(), new Class[]{AbstractUserDAO.class}, handler);
        proxy.findUserById("123");

        System.out.println("-------------------------------------------");
        // 代理DeptDAO
        AbstractDeptDAO deptDAO = new DeptDAO();
        handler = new DAOLogHandle(deptDAO);
        AbstractDeptDAO proxy2 = null;
        proxy2 = (AbstractDeptDAO) Proxy.newProxyInstance(AbstractDeptDAO.class.getClassLoader(), new Class[]{AbstractDeptDAO.class}, handler);
        proxy2.deleteDeptDAOById("123");
    }
}

interface AbstractUserDAO {
    public Boolean findUserById(String userId);
}

// 部门数据DAO操作
interface AbstractDeptDAO {
    public Boolean deleteDeptDAOById (String deptId);
}

class UserDAO implements AbstractUserDAO {

    public Boolean findUserById(String userId) {
        if (userId != null && userId.length() > 0) {
            System.out.println("查询" + userId + "的信息");
            return true;
        }
        return false;
    }
}

class DeptDAO implements AbstractDeptDAO {
    public Boolean deleteDeptDAOById (String deptId) {
        if (deptId != null && deptId.length() > 0) {
            System.out.println("删除" + deptId + "记录");
            return true;
        }
        return false;
    }
}

class DAOLogHandle implements InvocationHandler {
    // 真实对象
    private Object realObj;
    public DAOLogHandle() {}

    public DAOLogHandle(Object object) {
        this.realObj = object;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
        preInvoke();
        Object res = method.invoke(realObj, args);
        postInvoke();
        return res;
    }

    private void preInvoke() {
        System.out.println("方法调用前");
    }

    public void postInvoke() {
        System.out.println("方法调用后");
    }
}

CGLIB方式,需要代理类实现MethodInterceptor接口。

package com.example.rabbitmqdemo;
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 ProxyDemo {

    public static void main(String[] args) {
        // cglib 代理UserDAO
        DAOLogInterceptor interceptor = new DAOLogInterceptor(userDAO);
        AbstractUserDAO cglibProxy = (AbstractUserDAO)interceptor.getCglibProxy();
        cglibProxy.findUserById("456");
        System.out.println("-------------------------------------------");
        interceptor = new DAOLogInterceptor(deptDAO);
        AbstractDeptDAO cglibProxy2 = (AbstractDeptDAO)interceptor.getCglibProxy();
        cglibProxy2.deleteDeptDAOById("456");
    }
}

class DAOLogInterceptor implements MethodInterceptor {
    // 真实对象
    private Object realObj;

    public DAOLogInterceptor() {}
    public DAOLogInterceptor(Object realObj) {
        this.realObj = realObj;
    }
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        preInvoke();
        Object res = method.invoke(realObj, objects);
        postInvoke();
        return res;
    }

    //定义获取代理对象方法
    public Object getCglibProxy(){
        //为目标对象target赋值
        Enhancer enhancer = new Enhancer();
        //设置父类,因为Cglib是针对指定的类生成一个子类,所以需要指定父类
        enhancer.setSuperclass(realObj.getClass());
        enhancer.setCallback(this);// 设置回调
        Object result = enhancer.create();//创建并返回代理对象
        return result;
    }

    private void preInvoke() {
        System.out.println("方法调用前");
    }

    public void postInvoke() {
        System.out.println("方法调用后");
    }
}

问题记录

  • 代理模式和装饰模式的区别 代理模式中代理类和真实主题角色关联关系(上面类图);装饰模式中抽象装饰类和抽象构件类是聚合关系。在代理模式中我们增强方法是通过直接使用代理类,实际上我们代理的类对外是隐藏的,只能看到代理类。而装饰模式是通过自己传入需要被装饰的对象,然后再使用装饰过后的对象的增强方法。两者都是对类的方法进行增强,但装饰器模式强调的是增强自身,在被装饰之后你能够够在被增强的类上使用增强后的方法。增强过后还是你,只不过能力变强了。而代理模式则强调要别人帮你去做一些本身与你业务没有太多关系的职责。代理模式是为了实现对象的控制,因为被代理的对象往往难以直接获得或者是其内部不想暴露出来。[2]

参考资料

[1] 刘伟. 设计模式的艺术[M]. 清华大学出版社, 2020.

[2] 代理模式和装饰模式的区别_装饰模式和代理模式