菜单

davidzhu
发布于 2025-06-02 / 42 阅读
1
3

代理模式

1、基本介绍

代理设计模式是一种结构型设计模式,它为其他对象提供一个代理,以控制对这个对象的访问。代理模式分为静态代理动态代理。静态代理是指代理类在编译时就已经确定,而动态代理的代理类在运行时动态生成

  • 代理模式的两大作用:

  1. 屏蔽被代理对象

  2. 对被代理对象的方法做增强

2、静态代理

2.1、案例

1、定义接口,供代理类和被代理类实现

public interface DataQueryService {
    String query(String param);
}

2、定义被代理类

public class DataQuery implements DataQueryService{
    @Override
    public String query(String param) {
        // 查询数据库并返回结果
        return "查询结果";
    }
}

3、定义代理类,并给被代理类做增强

/**
 * DataQuery代理类
 */
public class DataQueryProxy implements DataQueryService{
    // 代理谁就应该持有谁
    private DataQuery dataQuery;

    // 缓存
    private Map<String, String> queryCaChe = new HashMap<>();

    public DataQueryProxy(DataQuery dataQuery) {
        // 1、屏蔽被代理对象
        this.dataQuery = dataQuery;
    }

    @Override
    public String query(String param) {
        // 2、对原方法做增强
        String data = queryCaChe.get(param);
        if (StringUtils.isEmpty(data)) {
            return dataQuery.query(param);
        }
        return data;
    }
}

2.2、静态代理步骤总结

1、创建一个接口,定义代理类和被代理类共同实现的方法

2、创建被代理类,定义这个接口,并且在其中定义实现方法

3、创建代理类,也要实现这个接口,同时定义一个属性为被代理类对象

4、在代理类中实现接口中的方法,方法中调用被代理类中的对应方法

5、在使用代理类时,首先创建被代理类的对象和代理类的对象,然后通过代理类调用方法

这样就实现对被代理类方法的增强或修改。当然也可以通过继承来实现代理。步骤与接口一致,但是通过继承实现代理类和被代理类的耦合度较高,不够灵活

3、动态代理

3.1、介绍

动态代理是在程序运行时动态生成代理类,无需手动编写代理类,大大的降低了代码的复杂度。动态代理一般使用Java提供的反射机制实现,可以对任意实现了接口的类进行代理

3.2、基于JDK实现动态代理

1、基于代理类和被代理类基础上,使用Proxy.newProxyInstance()方法创建代理对象,执行query方法

public class ProxyApplication {
    public static void main(String[] args) {
        // jdk动态代理主要使用Proxy类来完成
        // 1、定义被代理类的类加载器
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        // 2、代理类需要实现的接口
        Class[] interfaces = new Class[] {DataQueryService.class};
        InvocationHandler invocationHandler = new CacheInvocationHandler();
        // 3、获取代理对象
        DataQueryService dataQuery = (DataQueryService) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
        // 4、调用代理对象增强的方法,其实就是调用invocationHandler的invoke方法
        dataQuery.query("key1");
    }
}

2、代理类会实现接口的所有方法并转发到InvocationHandler的invoke方法中对被代理类做增强

public class CacheInvocationHandler implements InvocationHandler {
    private static final Map<String, String> cacheMap = new HashMap<>();

    // 持有被代理类
    private DataQuery dataQuery;

    public CacheInvocationHandler() {
        this.dataQuery = new DataQuery();
    }

    /**
     * @param proxy 代理类
     * @param method 方法
     * @param args 方法参数
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 方法名称
        String name = method.getName();
        // 对代理类的query方法做增强
        if ("query".equals(name)) {
            String key = args[0].toString();
            String value = cacheMap.get(key);
            if (value == null) {
                String queryResult = dataQuery.query(key);
                if (queryResult != null) {
                    cacheMap.put(key, queryResult);
                    return queryResult;
                }
            } else {
                return value;
            }
        }
        // 如果是其他方法则不作增强,正常执行被代理类方法
        return method.invoke(dataQuery, args);
    }
}

3.3、基于CGLib实现动态代理

1、定义被代理类,注意:该被代理类并没有实现接口

public class DataQuery {
    public String query(String param) {
        // 查询数据库并返回结果
        return "查询结果";
    }
}

2、创建代理类并调用方法

public class ProxyApplication {
    public static void main(String[] args) {
        // CGlib通过Enhancer类来实现动态代理
        Enhancer enhancer = new Enhancer();
        // 设置代理类的父类(注意此处没有设置接口,CGLib动态代理会动态的创建被代理类的子类)
        enhancer.setSuperclass(DataQuery.class);
        // 设置一个方法拦截器来拦截方法,对方法进行增强
        enhancer.setCallback(new CacheMethodInterceptor());
        // 创建代理类
        DataQuery dataQuery = (DataQuery) enhancer.create();
        dataQuery.query("key");
    }
}

3、在MethodInterceptor(方法拦截器)中执行增强逻辑

public class CacheMethodInterceptor implements MethodInterceptor {
    private static final Map<String, String> cacheMap = new HashMap<>();

    // 持有被代理类
    private DataQuery dataQuery;

    public CacheMethodInterceptor() {
        this.dataQuery = new DataQuery();
    }

    /**
     *
     * @param o 代理类
     * @param method 方法
     * @param args 方法参数
     * @param methodProxy 用于调用未被拦截的方法
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        // 方法名称
        String name = method.getName();
        // 对代理类的query方法做增强
        if ("query".equals(name)) {
            String key = args[0].toString();
            String value = cacheMap.get(key);
            if (value == null) {
                String queryResult = dataQuery.query(key);
                if (queryResult != null) {
                    cacheMap.put(key, queryResult);
                    return queryResult;
                }
            } else {
                return value;
            }
        }
        // 如果是其他方法则不作增强,正常执行被代理类方法
        return method.invoke(dataQuery, args);
    }
}

3.4、动态代理总结

3.4.1、JDK动态代理

  1. JDK动态代理是基于Java反射机制实现的。

  2. 在使用JDK动态代理时,被代理的对象必须实现一个或多个接口,代理对象的类型由接口类型决定。

  3. 在运行时,JDK动态代理会动态地创建一个实现指定接口的代理类,并且重写该接口的所有方法,使得这些方法的调用都被转发到InvocationHandler对象中的invoke方法中进行处理。

3.4.2、CGLib动态代理

  1. CGLib动态代理则是通过继承被代理类来实现的。

  2. 在使用CGLib动态代理时,被代理的类不必实现任何接口,代理对象的类型由被代理类的类型决定。

  3. 在运行时,CGLib动态代理会动态地创建一个被代理类的子类,并且重写该类的所有非final方法

3.4.3、两者的区别

JDK动态代理只能代理实现了接口的类,而CGLib动态代理可以代理任何类

JDK动态代理在运行时会动态生成代理类;而CGLib动态代理在运行时会动态生成被代理类的子类

3.5、使用方式

如果被代理的类实现了接口,建议使用JDK动态代理;如果被代理的类没有实现接口,建议使用CGLib动态代理


评论