Java中代理模式(三种)

代理模式概念

代理模式是一种结构性设计模式,代理对象做为一个中间层,对真实对象的操作进行拦截或者增添额外的功能,提高系统间接性和灵活性。

抽象概念分解如下:

  1. 抽象主题:一个接口或者抽象类,定义了真实对象和代理对象的方法。
  2. 真实主题:实现抽象主题接口的具体类
  3. 代理:在真实主题基础上增添功能

JAVA代理模式代码

1.静态代理
public interface StaticDao {
    /**
     * 静态类初始化方法
     */
    default void staticMethod(){
        System.out.println("静态类初始化方法");
    }
}
public class StaticDaoProxy implements StaticDao{

    private final StaticDao staticDao;

    public StaticDaoProxy(StaticDao staticDao) {
        this.staticDao = staticDao;
    }

    @Override
    public void staticMethod(){
        System.out.println("静态方法拓展");
        staticDao.staticMethod();
    }

}
public class StaticProxyTest {
    public static void main(String[] args) {

        // 创建动态代理对象.  匿名内部类
        StaticDaoProxy staticDaoProxy = new StaticDaoProxy(new StaticDao() {
            @Override
            public void staticMethod() {
                StaticDao.super.staticMethod();
            }
        });

        // 调用方法
        staticDaoProxy.staticMethod();
    }
}

2.jdk动态代理
public interface JdkDao {
    default void jdkMethod(){
        System.out.println("jdk动态代理初始化方法");
    }
}
public class JdkProxy implements InvocationHandler {

    /**
     * 正在被此实例代理的目标对象。
     */
    private Object target;

    /**
     * 构造一个新的{@code JdkProxy}实例,用于给定的目标对象。
     *
     * @param target 其方法将被拦截并增强的对象。
     */
    public JdkProxy(Object target) {
        this.target = target;
    }

    /**
     * 返回一个代理实例,该实例将方法调用委托给底层目标对象,
     * 同时允许通过{@link #invoke(Object, Method, Object[])}方法拦截并
     * 修改这些方法的行为。
     *
     * @return 为目标对象生成的代理实例。
     */
    public Object getProxyInstance() {
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), this);
    }

    /**
     * 当代理实例进行方法调用时实际执行的方法。
     * 在此方法中,可在调用原方法前后添加额外操作(如:打印日志、执行验证等)。
     * 最终通过`method.invoke(target, args)`调用被代理对象的原方法并返回结果。
     *
     * @param proxy 代理类
     * @param method 被代理方法
     * @param args 方法参数
     * @return 原方法调用后的返回结果
     * @throws Throwable 如果原方法调用过程中抛出异常,此方法将抛出该异常
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("JDK动态代理拓展");
        return method.invoke(target, args);
    }
}
public class JdkProxyTest {
    public static void main(String[] args) {

        // 创建一个JdkProxy实例,传入待代理的JdkDao对象。这里使用匿名内部类继承JdkDao并重写其jdkMethod方法。
        JdkProxy jdkProxy = new JdkProxy(new JdkDao() {
            @Override
            public void jdkMethod() {
                // 调用父类(即JdkDao接口默认实现)的jdkMethod方法
                JdkDao.super.jdkMethod();
            }
        });

        // 通过JdkProxy获取代理实例,并将其强制转换为JdkDao类型
        JdkDao jdkDao = (JdkDao) jdkProxy.getProxyInstance();

        // 调用代理对象的jdkMethod方法,此时会触发JdkProxy中的invoke方法进行动态代理逻辑
        jdkDao.jdkMethod();
    }
}
3.cglib动态代理
public class CglibDao {
    public void cglibMethod(){
        System.out.println("cglib动态代理初始化");
    }
}
public class CglibProxy implements MethodInterceptor {
    private Object target;

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

    public Object getProxyInstance() {
        // 1. 创建Enhancer工具类实例
        Enhancer enhancer = new Enhancer();
        // 2. 设置需要创建子类(代理对象)的父类(被代理对象的类)
        enhancer.setSuperclass(target.getClass());
        // 3. 设置回调方法(代理逻辑),即将CglibProxy自身作为回调处理器
        enhancer.setCallback(this);
        // 4. 生成并返回代理对象
        return enhancer.create();
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("cglib动态代理拓展方法");
        // 调用原始方法并返回结果
        return method.invoke(target, args);
    }
}
public class CglibProxyTest {
    public static void main(String[] args) {
        // 创建一个CglibProxy实例,传入待代理的CglibDao对象
        CglibProxy cglibProxy = new CglibProxy(new CglibDao());

        // 通过CglibProxy获取代理实例,并将其强制转换为CglibDao类型
        CglibDao proxyInstance = (CglibDao) cglibProxy.getProxyInstance();

        // 调用代理对象的cglibMethod方法,此时会触发CglibProxy中的intercept方法进行动态代理逻辑
        proxyInstance.cglibMethod();
    }
}

动态代理对比

JDK动态代理

  • 实现原理:基于Java反射和InvocationHandler接口,当目标类实现了至少一个接口时,JDK动态代理可以通过创建一个实现了所有目标接口的新代理类来工作。
  • 特点:
    • 只能针对实现了接口的类创建代理。
    • 不需要引入第三方库,直接由JDK的标准库提供支持。
    • 创建代理的速度相对较慢,因为涉及到接口方法的反射调用。
    • 由于是基于接口,因此代理对象不持有目标类的引用,而是持有目标接口的引用,故不能访问到目标类的非接口方法或者私有方法。

CGLIB动态代理

  • 实现原理:基于ASM库来操作字节码,能够通过继承目标类并生成其子类的方式来实现代理,即使目标类没有实现任何接口也能适用。
  • 特点:
    • 不仅能代理接口,还能代理未实现接口的普通类,应用范围更广。
    • 需要引入CGLIB库,不是Java标准库的一部分。
    • 生成代理类的速度理论上可能比JDK代理稍快,因为它是在编译期生成的字节码。
    • 由于是继承方式,可以访问并覆盖目标类的所有方法(包括非公开方法),但如果目标类是final类,则无法对其进行代理。

Spring框架默认优先使用JDK动态代理,若目标类不满足接口条件则退而使用CGLIB。

04-12 04:26