Skip to content

Java 中的代理

代理的使用非常广泛,Spring AOP、Mybatis、RPC、Mock 等都使用到了代理,其中主要是动态代理

代理设计模式

代理模式提供了对目标对象的额外访问途径,即通过代理对象访问目标对象,这样可以在不修改目标对象的前提下,对原有功能进行扩展、增强。

静态代理

示例

java
public class StaticProxyTest {

    interface IUserDao {
        void save();
    }

    static class UserDao implements IUserDao {
        @Override
        public void save() {
            System.out.println("保存数据");
        }
    }

    static class UserDaoProxy implements IUserDao {

        private IUserDao target;

        public UserDaoProxy(IUserDao target) {
            this.target = target;
        }

        @Override
        public void save() {
            System.out.println("开启事务");
            target.save();
            System.out.println("提交事务");
        }
    }

    @Test
    public void testStaticProxy() {
        //目标对象
        IUserDao target = new UserDao();
        //代理对象
        UserDaoProxy proxy = new UserDaoProxy(target);
        proxy.save();
    }
}

静态代理需要代理对象和目标对象实现一样的接口。

在代理对象中调用目标对象,达到在不修改目标对象的前提下扩展目标对象的功能。

缺点:

  • 冗余,由于代理对象要实现与目标对象一致的接口,会产生过多的代理类。
  • 不易维护,一旦接口增加方法,目标对象与代理对象都要进行修改。

JDK 动态代理

静态代理与动态代理的区别:

静态代理在编译期完成,运行时已生成最终 class 文件, 动态代理是运行时在 JVM 中动态生成 class。

java
public class JdkProxyTest {

    interface SmsService {
        String send(String message);
    }

    static class SmsServiceImpl implements SmsService {
        @Override
        public String send(String message) {
            System.out.println("send message:" + message);
            return message;
        }
    }

    static class JdkProxyFactory {
        public static <T> T getProxy(Object target) {
            return (T) Proxy.newProxyInstance(
                    // 目标类的类加载
                    target.getClass().getClassLoader(),
                    // 代理需要实现的接口,可指定多个
                    target.getClass().getInterfaces(),
                    // 代理对象对应的自定义 InvocationHandler
                    new ProxyInvocationHandler(target)
            );
        }
    }

    static class ProxyInvocationHandler implements InvocationHandler {
        /**
         * 目标对象
         */
        private final Object target;

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

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
            //调用方法之前,我们可以添加自己的操作
            System.out.println("before method " + method.getName());
            Object result = method.invoke(target, args);
            //调用方法之后,我们同样可以添加自己的操作
            System.out.println("after method " + method.getName());
            return result;
        }
    }

    @Test
    public void test() {
        SmsService smsService = JdkProxyFactory.getProxy(new SmsServiceImpl());
        smsService.send("java");
    }
}

CGLIB 动态代理

CGLIB (Code Generation Library )是使用 ASM 修改子类节码文件生成代理对象

与 JDK 动态代理区别:

JDK 动态代理要求目标对象实现对应接口, CGLIB 不要求实现接口,但是会生成目标对象的子类,所以目标对象不能被 final 修饰。

java
public class CglibProxyTest {

    static class SmsService {
        public String send(String message) {
            System.out.println("send message:" + message);
            return message;
        }
    }

    static class DebugMethodInterceptor implements MethodInterceptor {

        /**
         * @param o           被代理的对象(需要增强的对象)
         * @param method      被拦截的方法(需要增强的方法)
         * @param args        方法入参
         * @param methodProxy 用于调用原始方法
         */
        @Override
        public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            //调用方法之前,我们可以添加自己的操作
            System.out.println("before method " + method.getName());
            Object object = methodProxy.invokeSuper(o, args);
            //调用方法之后,我们同样可以添加自己的操作
            System.out.println("after method " + method.getName());
            return object;
        }

    }

    static class CglibProxyFactory {

        public static <T> T getProxy(Class<T> clazz) {
            // 创建动态代理增强类
            Enhancer enhancer = new Enhancer();
            // 设置类加载器
            enhancer.setClassLoader(clazz.getClassLoader());
            // 设置被代理类
            enhancer.setSuperclass(clazz);
            // 设置方法拦截器
            enhancer.setCallback(new DebugMethodInterceptor());
            // 创建代理类
            return (T) enhancer.create();
        }
    }
    
    @Test
    public void test() {
        SmsService smsService = CglibProxyFactory.getProxy(SmsService.class);
        smsService.send("cglib");
    }
}