Java动态代理技术

代理模式的优势是可以很好地遵循设计模式中的开放封闭原则,对扩展开发,对修改关闭。你不需要关注目标类的实现细节,通过代理模式可以在不修改目标类的情况下,增强目标类功能的行为。
Spring AOP 是 Java 动态代理机制的经典运用,我们在项目开发中经常使用 AOP 技术完成一些切面服务,如耗时监控、事务管理、权限校验等,所有操作都是通过切面扩展实现的,不需要对源代码有所侵入。

JDK 动态代理

JDK 动态代理实现依赖java.lang.reflect包中的两个核心类:InvocationHandler 接口和Proxy 类。

  • InvocationHandler 接口
    JDK 动态代理所代理的对象必须实现一个或者多个接口,生成的代理类也是接口的实现类,然后通过 JDK 动态代理是通过反射调用的方式代理类中的方法,不能代理接口中不存在的方法。每一个动态代理对象必须提供 InvocationHandler 接口的实现类,InvocationHandler 接口中只有一个 invoke() 方法。当我们使用代理对象调用某个方法的时候,最终都会被转发到 invoke() 方法执行具体的逻辑。
    invoke() 方法的定义如下:
public interface InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
  • Proxy 类
    Proxy 类可以理解为动态创建代理类的工厂类,它提供了一组静态方法和接口用于动态生成对象和代理类。通常我们只需要使用 newProxyInstance() 方法.
    方法定义如下所示:
public static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) {

    Objects.requireNonNull(h);

    Class caller = System.getSecurityManager() == null ? null : Reflection.getCallerClass();

    Constructor cons = getProxyConstructor(caller, loader, interfaces);

    return newProxyInstance(caller, cons, h);

}

其中 ,

  1. loader 参数表示需要装载的类加载器 ClassLoader,
  2. interfaces 参数表示代理类实现的接口列表,
  3. InvocationHandler 接口类型的处理器,所有动态代理类的方法调用都会交由该处理器进行处理.

示例:

public class JdkProxyTest {

    interface UserDao {
        void insert();
    }

    public static class UserDaoImpl implements UserDao {

        @Override
        public void insert() {
            System.out.println("insert...");
        }
    }

    public static class TransactionProxy {
        private Object target;

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

        public Object getProxy() {
            return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
                    ((proxy, method, args) -> {
                        System.out.println("insert before");
                        Object result = method.invoke(target, args);
                        System.out.println("insert after");
                        return result;
                    }));
        }
    }

    @Test
    public void test() {
        UserDao proxy = (UserDao)new TransactionProxy(new UserDaoImpl()).getProxy();
        proxy.insert();
    }
}

Cglib 动态代理

Cglib 动态代理是基于 ASM 字节码生成框架实现的第三方工具类库,相比于 JDK 动态代理,Cglib 动态代理更加灵活,它是通过字节码技术生成的代理类,所以代理类的类型是不受限制的。使用 Cglib 代理的目标类无须实现任何接口,可以做到对目标类零侵入。

Cglib 动态代理是对指定类以字节码的方式生成一个子类,并重写其中的方法,以此来实现动态代理。因为 Cglib 动态代理创建的是目标类的子类,所以目标类必须要有无参构造函数,而且目标类不要用 final 进行修饰。

  1. 引入依赖

    cglib
    cglib
    3.3.0

Cglib动态代理依赖MethodInterceptorEnhancer类,
1.1 MethodInterceptor
MethodInterceptor 接口只有 intercept() 一个方法,所有被代理类的方法执行最终都会转移到 intercept() 方法中进行行为增强,真实方法的执行逻辑则通过 Method 或者 MethodProxy 对象进行调用。
1.2 Enhancer
Enhancer 类是 Cglib 中的一个字节码增强器,它为我们对代理类进行扩展时提供了极大的便利。Enhancer 类的本质是在运行时动态为代理类生成一个子类,并且拦截代理类中的所有方法。我们可以通过 Enhancer 设置 Callback 接口对代理类方法执行的前后执行一些自定义行为,其中 MethodInterceptor 接口是我们最常用的 Callback 操作。

public class CglibProxy implements MethodInterceptor {

    private Object target;

    public CglibProxy() {
        this.target = new UserDaoImpl();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("insert before");
        Object result = methodProxy.invokeSuper(o, objects);
        System.out.println("insert after");
        return result;
    }

    public Object getProxyInstance(){
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Test
    public void test(){
        CglibProxy cglibProxy = new CglibProxy();
        UserDao proxyInstance =(UserDao) cglibProxy.getProxyInstance();
        proxyInstance.insert();
    }


    interface UserDao {
        void insert();
    }

    public static class UserDaoImpl implements UserDao {

        @Override
        public void insert() {
            System.out.println("insert...");
        }
    }
}

Jdk动态代理和Cglib动态代理对比:

  1. Jdk动态代理本质是基于反射,Cglib动态代理本质是基于继承代理类,重写子类方法实现代理。
  2. Jdk动态代理代理类必须要实现接口,并且只能代理接口中的方法,Cglib动态代理,代理类本身不需要实现任何方法即可实现代理,但是由于实现原理是基于继承代理类实现,所以,代理类以及代理的方法不能用final修饰。
  3. Jdk动态代理基于反射实现,在运行期间完成代理工作,Cglib动态代理是通过字节码增强技术,在编译器完成代理,所以,Cglib动态代理的效率要稍高于Jdk动态代理。

你可能感兴趣的:(Java动态代理技术)