JDK的动态代理这个词大佬们经常挂在嘴边,可我却是云里雾里。
这几个字分开倒是都认识,组合在一起就不明白它是表达的什么意思了

talk is cheap, show me the code.

基础实战

main入口类

  • 新建一个含有main方法的测试类TestMain.java
package com.itplh.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;

/**
 * JDK动态代理
 * Proxy 代理的本质是方法的拦截
 *
 * $Proxy0 extends Proxy implements Student,我们看到代理类继承了Proxy类,
 * 所以也就决定了java动态代理只能对接口进行代理,Java的继承机制注定了这些动态代理类们无法实现对class的动态代理
 *
 * 因为JDK的动态代理只能基于接口,所以 newProxyInstance 的第二个参数 Class<?>[] interfaces 的元素必须是接口
 *
 *
 * @author: tanpeng
 * @since: 2020-06-02 9:57
 */
public class TestMain {
    public static void main(String[] args) {
        // 使用匿名内部类的方式声明一个Teacher接口的实现类
        Student student = () -> "I'm Student interface of implements class.";

        // 一、Proxy.newProxyInstance + 匿名内部类
        Student studentProxy1 = (Student) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                new Class[]{Student.class},
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        print("Proxy.newProxyInstance + 匿名内部类",
                                (String) method.invoke(student, args),
                                method.getAnnotation(TestAnnotation.class).value()[0].toUpperCase());
                        return null;
                    }
                });
        studentProxy1.hi();

        // 二、Proxy.newProxyInstance + lambda表达式
        Student studentProxy2 = (Student) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                new Class[]{Student.class},
                (proxy, method, args1) -> {
                    print("Proxy.newProxyInstance + lambda表达式.",
                            (String) method.invoke(student, args1),
                            method.getAnnotation(TestAnnotation.class).value()[0].toUpperCase());
                    return null;
                });
        studentProxy2.hi();

        // 三、Proxy.newProxyInstance + InvocationHandler实现类
        Student studentProxy3 = (Student) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                new Class[]{Student.class},
                new TestInvocationHandler<Student>(student));
        studentProxy3.hi();

        // 四、封装ProxyFactory 代理Student接口
        Student studentProxy4 = TestProxyFactory.getProxyObject(student);
        studentProxy4.hi();

        // 五、封装ProxyFactory 代理Teacher接口
        Teacher teacher = () -> "I'm teacher.";
        Teacher teacherProxy = TestProxyFactory.getProxyObject(teacher);
        teacherProxy.hi();

        // 这里传实现类 ZhangSan.class 将会报错
        // Exception in thread "main" java.lang.IllegalArgumentException: com.itplh.proxy.ZhangSan is not an interface
//        Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
//                new Class[]{ZhangSan.class},
//                new TestInvocationHandler<Student>(student));
    }

    public static void print(String... text) {
        System.out.println("------------------------------------------");
        Arrays.asList(text).forEach(t -> System.out.println("------ " + t));
        System.out.println("------------------------------------------");
        System.out.println();
    }
}

interface Student {
    @TestAnnotation("select * from student where id = #{id}")
    String hi();
}

class ZhangSan implements Student {
    @Override
    public String hi() {
        return "I'm zhangsan.";
    }
}

interface Teacher {
    @TestAnnotation("select * from teacher where id = #{id}")
    String hi();
}
  • 控制台输出
------------------------------------------
------ Proxy.newProxyInstance + 匿名内部类
------ I'm Student interface of implements class.
------ SELECT * FROM STUDENT WHERE ID = #{ID}
------------------------------------------

------------------------------------------
------ Proxy.newProxyInstance + lambda表达式.
------ I'm Student interface of implements class.
------ SELECT * FROM STUDENT WHERE ID = #{ID}
------------------------------------------

------------------------------------------
------ InvocationHandler of implements class.
------ I'm Student interface of implements class.
------ SELECT * FROM STUDENT WHERE ID = #{ID}
------------------------------------------

------------------------------------------
------ ProxyFactory com.itplh.proxy.Student.
------ I'm Student interface of implements class.
------ SELECT * FROM STUDENT WHERE ID = #{ID}
------------------------------------------

------------------------------------------
------ ProxyFactory com.itplh.proxy.Teacher.
------ I'm Teacher interface of implements class.
------ SELECT * FROM TEACHER WHERE ID = #{ID}
------------------------------------------

其他类

  • 自定义一个注解TestAnnotation.java
package com.itplh.proxy;

import java.lang.annotation.*;

/**
* @author: tanpeng
* @since: 2020-06-02 10:09
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface TestAnnotation {
   String[] value();
}
  • 自定义TestInvocationHandler.java
package com.itplh.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * 动态代理-拦截器
 * 作用:控制目标对象的目标方法的执行
 * 
 * @author: tanpeng
 * @since: 2020-06-02 9:57
 */
public class TestInvocationHandler<T> implements InvocationHandler {

    // InvocationHandler持有的被代理对象
    private T target;

    public TestInvocationHandler(T target) {
        this.target = target;
    }

    /**
     * 调用被代理的对象的方法都会进入本方法
     *
     * @param proxy  代表动态代理对象
     * @param method 代表正在执行的方法
     * @param args   代表调用目标方法时传入的实参
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        TestMain.print("InvocationHandler of implements class.",
                (String) method.invoke(target, args),
                method.getAnnotation(TestAnnotation.class).value()[0].toUpperCase());
        return null;
    }
}
  • 封装一个代理工厂TestProxyFactory.java
package com.itplh.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

/**
 * @author: tanpeng
 * @since: 2020-06-02 11:08
 */
public class TestProxyFactory {

    /**
     * static <T> 表示将该方法声明为泛型方法
     *
     * @param target 目标代理对象
     * @return
     */
    public static <T> T getProxyObject(T target) {
        return getProxyObject(target, (proxy, method, args) -> {
            TestMain.print("ProxyFactory " + target.getClass().getInterfaces()[0].getName() + ".",
                    (String) method.invoke(target, args),
                    method.getAnnotation(TestAnnotation.class).value()[0].toUpperCase());
            return null;
        });
    }

    /**
     * static<T> 表示将该方法声明为泛型方法
     *
     * @param target 目标代理对象
     * @param invocationHandler
     * @return
     */
    public static <T> T getProxyObject(T target, InvocationHandler invocationHandler) {
        return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                target.getClass().getInterfaces(),
                invocationHandler);
    }
}

测试含有多个方法的接口

  • 新建接口People 、User,及实现类DefaultUser
interface People {
    @TestAnnotation("String hi();")
    String hi();
}

interface User extends People {
    @TestAnnotation("select username from user where id = #{id}")
    String selectUsernameById(String id);

    @TestAnnotation("select email from user where id = #{id}")
    String selectEmailById(String id);
}

class DefaultUser implements User {
    @Override
    public String hi() {
        return "This is DefaultUser#hi.";
    }
    
    @Override
    public String selectUsernameById(String id) {
        return "tanpeng";
    }
    
	@Override
    public String selectEmailById(String id) {
        return "tanpeng@itplh.com";
    }
}
  • main 函数中测试
DefaultUser user = new DefaultUser();

User userProxy = TestProxyFactory.getProxyObject(user);
userProxy.hi();
userProxy.selectUsernameById("1");
userProxy.selectEmailById("1");

People peopleProxy = TestProxyFactory.getProxyObject(user);
peopleProxy.hi();
((User) peopleProxy).selectUsernameById("1");
((User) peopleProxy).selectEmailById("1");
  • 控制台输出
------------------------------------------
------ ProxyFactory com.aden.modules.datax.controller.User.
------ This is DefaultUser#hi.
------ STRING HI();
------------------------------------------

------------------------------------------
------ ProxyFactory com.aden.modules.datax.controller.User.
------ tanpeng
------ SELECT USERNAME FROM USER WHERE ID = #{ID}
------------------------------------------

------------------------------------------
------ ProxyFactory com.aden.modules.datax.controller.User.
------ tanpeng@itplh.com
------ SELECT EMAIL FROM USER WHERE ID = #{ID}
------------------------------------------

------------------------------------------
------ ProxyFactory com.aden.modules.datax.controller.User.
------ This is DefaultUser#hi.
------ STRING HI();
------------------------------------------

------------------------------------------
------ ProxyFactory com.aden.modules.datax.controller.User.
------ tanpeng
------ SELECT USERNAME FROM USER WHERE ID = #{ID}
------------------------------------------

------------------------------------------
------ ProxyFactory com.aden.modules.datax.controller.User.
------ tanpeng@itplh.com
------ SELECT EMAIL FROM USER WHERE ID = #{ID}
------------------------------------------

应用场景

  • 通过代理对类进行增强 设计模式中有一个设计原则是开闭原则,是说对修改关闭对扩展开放。
    我们在工作中有时会接手很多前人的代码,里面代码逻辑让人摸不着头脑(sometimes the code is really like shit)。
    这时就很难去下手修改代码,那么这时我们就可以通过代理对类进行增强。
  • AOP
  • 松散耦合 在使用RPC框架的时候,框架本身并不能提前知道各个业务方要调用哪些接口的哪些方法 。
    那么这个时候,就可用通过动态代理的方式来建立一个中间人给客户端使用,
    也方便框架进行搭建逻辑,某种程度上也是客户端代码和框架松耦合的一种表现。

知识拓展

  • 静态代理
    在代码编译时就确定了被代理的类是哪一个
  • 动态代理
    在代码运行期间才加载被代理的类,编译时并未确定被代理的类是哪一个
  • 静态代理动态代理的区别
    代理涉及到两个关联词代理类委托类 静态代理 一个代理类针对一个委托类,即一对一 动态代理 一个代理类可利用反射机制代理多个委托类,即一对多
  • 两种动态代理的区别
  • JDK的动态代理 基于接口的代理
  • 代理对象和目标对象实现了共同的接口
  • 拦截器必须实现InvocationHanlder接口
  • cglib的动态代理 基于类的代理 (Code Generation Library )
  • 代理对象是目标对象的子类
  • 拦截器必须实现MethodInterceptor接口
  • 动态代理的相关问题
  • 代理对象是由谁产生的
    JVM产生的,不像静态代理,我们自己得new个代理对象出来。
  • 代理对象实现了什么接口
    实现的接口是目标对象实现的接口。同静态代理中代理对象实现的接口。那个继承关系图还是相同的。
    代理对象目标对象都实现一个共同的接口。所以Proxy.newProxyInstance()方法返回的类型就是这个接口类型。
  • 代理对象的方法体是什么
    代理对象的方法体中的内容就是拦截器中invoke方法中的内容。
    所有代理对象的处理逻辑,控制是否执行目标对象的目标方法。都是在invoke方法里面处理的。
  • 拦截器中的invoke方法中的method参数是在什么时候赋值的
    代理对象调用目标方法的时候。
    如,studentProxy1.hi();userProxy.selectUsernameById("1"); 这时实际上进入的是拦截器中的invoke方法,这个时候拦截器中的invoke方法中的method参数会被赋值。

动态代理对象时用JDK的相关代码生成的,叫JDK动态代理 动态代理对象时用cglib的 jar 包,叫cglib动态代理

参考