动态代理

动态代理类似于设计模式中的代理模式,在Java中的体现就是,给目标类生成一个代理类,用代理类来调用目标类的具体方法,并可以在调用前后给目标方法执行一些附加功能,最简单的应用就是Spring中的Aop,使用@Transactional注解可以在不写事务相关代码的情况下给方法增加事务

动态代理的实现方式

有两种实现方式,

  • JDK动态代理,即Java语言提供的动态代理实现
  • CGLIB动态代理,第三方库实现的动态代理

JDK动态代理

JDK动态代理的实现需要目标类必须实现了某个接口,然后才能够给目标类添加增强方法

Talk is cheap, Show me the code

// 一个接口
public interface HelloService {

    void sayHi();
}

// 一个实现类
public class HelloServiceImpl implements HelloService {

    @Override
    public void sayHi() {
        System.out.println("你好,我是HelloService的实现类");
    }
}

// 一个动态代理类
public class HelloProxy implements InvocationHandler {

    /**
     * 目标类的实例
     */
    private Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("我是代理,实现增强开始...");

        final Object result = method.invoke(target, args);

        System.out.println("我是代理,实现增强结束...");

        return result;
    }
}

// 一个测试类
public class JdkDynamicProxyDemo {

    public static void main(String[] args) throws Exception {
        // 创建代理类并执行代理方法的第一种方法

        // 1.生成$Proxy0.class文件
        System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

        // 2.获取动态代理类
        final Class<?> proxyClass = Proxy.getProxyClass(HelloService.class.getClassLoader(), HelloService.class);

        // 3.获取代理类的构造器并传入InvocationHandler.class
        final Constructor<?> constructor = proxyClass.getConstructor(InvocationHandler.class);

        // 4.通过构造器生成代理对象
        final HelloService proxyObject = (HelloService) constructor.newInstance(new HelloProxy(new HelloServiceImpl()));

        // 5.调用目标方法
        proxyObject.sayHi();
        
        // 创建代理类并执行代理方法的第二种方法
        
        // 直接使用proxy类的静态方法newProxyInstance,整合了第一种方法的五个步骤
        final HelloService proxyObject2 = (HelloService) Proxy.newProxyInstance(
                HelloService.class.getClassLoader(),
                new Class[]{HelloService.class},
                new HelloProxy(new HelloServiceImpl())
        );
    }
}

输出如下:
>>> 我是代理,实现增强开始...
>>> 你好,我是HelloService的实现类
>>> 我是代理,实现增强结束...

想要使用JDK动态代理的目标类,必须要实现一个接口,否则就不能被成功代理

CgLib实现动态代理

CgLib实现动态代理与JDK动态代理不同的是,CgLib不要求被代理类实现接口,CgLib的实现原理是,对目标类生成一个代理类继承于目标类,然后调用代理类的超类的方法,在方法调用前后进行方法增强操作

因为是第三方库,我们需要添加以下依赖或者直接引入spring-framework的依赖也可以

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>
// 一个目标类
public class TargetClass {

    final public void sayHello() {
        System.out.println("大家好,我是final的sayHello");
    }

    public void sayHi() {
        System.out.println("大家好,我是自由的sayHi");
    }
}

// 一个代理类
public class ProxyClass implements MethodInterceptor {

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("这是cglib动态代理增强, 开始");
        // 调用父类的方法
        final Object result = methodProxy.invokeSuper(o, objects);
        System.out.println("这是cglib动态代理增强, 结束");

        return result;
    }
}

// 一个测试类
public class CgLibProxyDemo {

    public static void main(String[] args) {
        // 生成代理对象$Proxy0.class
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/Users/daniel/Downloads/development/workSpace/Redis/Java-demo");

        // 创建Enhancer对象,类似于JDK动态代理的Proxy类
        Enhancer enhancer = new Enhancer();

        // 设置目标类
        enhancer.setSuperclass(TargetClass.class);

        // 设置回调
        enhancer.setCallback(new ProxyClass());

        // 创建代理类
        final TargetClass targetClass = (TargetClass) enhancer.create();

        // 调用代理方法
        targetClass.sayHi();
    }
}