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动态代理