JDK动态代理机制
代理模式
代理模式简单来说就是,使用代理对象代替真实对象的访问。可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能,比如在某个方法执行前后,可以增加一些自定义的操作。
[静态代理](多线程详解.md/#静态代理模式 static proxy)
动态代理
静态代理中,对目标对象的每个方法的扩展都是手动完成的,非常不灵活(比如一旦新增方法,目标对象和代理对象都要进行修改)、麻烦(需要对每个目标类都单独写一个代理类)。日常开发几乎看不到使用静态代理的场景。
相比于静态代理来说,动态代理更加灵活。不需要针对每个目标类都单独创建一个代理类,并且也不需要我们必须实现接口,可以直接代理实现类(CGLIB 动态代理机制)。从JVM角度说,动态代理是在运行时 动态生成类字节码,并加载到JVM中。
Spring AOP、RPC框架的实现都依赖了动态代理。动态代理在日常开发中使用相对较少,但在框架中是必用的一门技术,对于各种框架原理的理解和学习很有帮助。就Java来说,动态代理的实现方式有很多种,比如JDK 动态代理、CGLIB 动态代理。
JDK 动态代理机制
InvocationHandler接口和Proxy类是核心。
Proxy.newProxyInstance,该方法主要用来生成一个代理对象。通过Proxy类的newProxyInstance()方法创建的代理对象,在调用方法的时候,实际会调用到实现InvocationHandler接口的类的invoke()方法。可以在invoke()方法种自定义处理逻辑,比如在方法执行前后做什么事情。
使用步骤
- 定义一个接口及其实现类。
 - 实现
InvocationHandler接口并重写invoke方法,在其中调用被代理类的方法,并自定义一些处理逻辑。 - 通过
Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)方法创建代理对象。ClassLoader loader:指定用哪个类加载器来加载代理类。Class<?>[] interfaces:指定代理类需要实现的接口(返回的对象可以被安全地转型为实现了这个接口的类型)InvocationHandler h:指定第2步创建的调用处理器
 
示例
定义一个发送短信的接口及其实现类
public interface SmsService {
    String send(String message);
}
public class SmsServiceImpl implements SmsService {
    @Override
    public String send(String message) {
        System.out.println("send message: " + message);
        return message;
    }
}
实现InvocationHandler接口并重写invoke方法
public class JdkInvocationHandler implements InvocationHandler {
    /**
     * 代理类中的真实对象
     */
    private final Object target;
    public JdkInvocationHandler(Object target) {
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before method " + method.getName());
        Object result = method.invoke(target, args);
        System.out.println("after method " + method.getName());
        return result;
    }
}
通过工厂类创建代理对象
public class JdkProxyFactory {
    public static Object getProxy(Object target) {
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(), // 加载代理类的类加载器
                target.getClass().getInterfaces(), // 代理类需要实现的接口,即代理的对象
                new JdkInvocationHandler(target) // 调用处理器
        );
    }
}
实际使用
public class Main {
    public static void main(String[] args) {
        SmsService smsService = (SmsService) JdkProxyFactory.getProxy(new SmsServiceImpl());
        smsService.send("java");
    }
}
CGLIB 动态代理机制
JDK 动态代理有一个最致命的问题:只能代理实现了接口的类。CGLIB 动态代理机制可以避免这个问题。
CGLIB(Code Generation Library) 是一个字节码生成库,它允许我们在运行时对字节码进行修改和动态生成。CGLIB 通过继承方式实现代理。Spring中的AOP模块,如果目标对象实现了接口,则默认采用JDK动态代理,否则采用CGLIB动态代理。
在CGLIB动态代理机制中,MethodInterceptor接口和Enhancer类是核心。
使用步骤
- 定义一个类
 - 实现
MethodInterceptor接口并重写intercept方法,intercept用于拦截扩展被代理类的方法,和 JDK 动态代理中的invoke方法类似。 - 通过
Enhancer类的create()方法创建代理类。 
<!-- CGLIB 动态代理 -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>
示例
定义一个发送短信的类
public class SmsService {
    public String send(String message) {
        System.out.println("send message: " + message);
        return message;
    }
}
实现方法拦截器MethodInterceptor
public class CglibMethodInterceptor implements MethodInterceptor {
    /**
     * 扩展代理对象
     *
     * @param obj 被扩展的代理对象
     * @param method 被拦截的方法(需要扩展的方法)
     * @param args 方法入参
     * @param methodProxy 用于调用原始方法
     * @return 被代理方法调用后的结果
     */
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("before method " + method.getName());
        Object result = methodProxy.invokeSuper(obj, args);
        System.out.println("after method " + method.getName());
        return result;
    }
}
通过工厂类创建代理对象
public class CglibProxyFactory {
    public static Object getProxy(Class<?> clazz) {
        // 创建动态代理扩展类
        Enhancer enhancer = new Enhancer();
        // 设置类加载器
        enhancer.setClassLoader(clazz.getClassLoader());
        // 设置被代理类
        enhancer.setSuperclass(clazz);
        // 设置方法拦截器
        enhancer.setCallback(new CglibMethodInterceptor());
        // 创建代理类
        return enhancer.create();
    }
}
实际使用
public class Main {
    public static void main(String[] args) {
        SmsService smsService = (SmsService) CglibProxyFactory.getProxy(SmsService.class);
        smsService.send("java");
    }
}
JDK 动态代理 和 CGLIB 动态代理的对比
- JDK动态代理只能代理实现了接口的类,CGLIB可以代理未实现任何接口的类。另外,CGLIB动态代理是通过生成一个被代理的子类,来拦截被代理类的调用,因此不能代理声明为final类型的类和方法。
 - JDK 动态代理的效率更高
 
静态代理和动态代理的对比
灵活性
动态代理更加灵活,不需要实现接口,可以直接代理实现类。静态代理中,接口一旦新增方法,目标对象和代理对象都需要修改,非常麻烦
JVM层面
静态代理在编译时,就将接口、实现类、代理类这些都变成了实际的class文件;动态代理是在运行时动态生成类字节码,并加载到JVM中。