框架源码常客JDK动态代理模式
目录
3、InvocationHandler的实现类,Cache调用处理器
一、前言
JDK动态代理,在源码的世界里扮演的角色格外重要,如MyBatis的日志模块、binding模块等,Spring的AOP等。使用代理的一个重要目标是控制对被代理类的访问,可以在代理类中做一些前置/后置处理,如:缓存、日志、事务、权限等等。使用JDK动态代理的前提是:被代理类必须是一个接口或者其实现类。如果被代理类不是接口或者接口实现类,而你又想使用动态代理,可考虑使用CGLIB、Javassist技术。这是一项硬技能,好的Java工程师、架构师都必须具备的知识。
二、开发中使用的组件及其方法
开发中用到的JDK动态代理组件为InvocationHandler接口以及Proxy类,它们的职责如下表:
类 | 类的职责 |
Proxy类 | 负责生成代理类并关联调用处理器(InvocationHandler接口的实现类) |
InvocationHandler接口 | 负责处理代理的逻辑,如:缓存、日志、事务、权限等等 |
1、Proxy生成的代理类是不处理逻辑的,抽象又很真实你连代理类体都没看到如何写逻辑,但是它会委派给调用处理器的invoke()方法处理逻辑,这也就是为什么多了个InvocationHandler接口及其实现类的原因。我们都知道,要调用一个类的非静态方法,一般都得经过它的实例去调用方法。好办,Proxy生成的代理类不处理逻辑,而委托给调用处理器的invoke()方法处理,那么要调用调用处理器的方法,你至少要拿到它的实例。故Proxy的newProxyInstance()方法(这个方法是开发中经常用到的)中就得把调用处理器的实例传入进来了。我们来看看源码中这个方法有哪些参数:
/**
* Returns an instance of a proxy class for the specified interfaces
* that dispatches method invocations to the specified invocation
* handler.
* @param loader the class loader to define the proxy class
* @param interfaces the list of interfaces for the proxy class
* to implement
* @param h the invocation handler to dispatch method invocations to
* @return a proxy instance with the specified invocation handler of a
* proxy class that is defined by the specified class loader
* and that implements the specified interfaces
*/
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
方法职责是创建代理类实例,并且代理类关联接管处理逻辑任务的调用处理器。
loader:类加载器,负责加载代理类。
interfaces:数组类型,表示提供了一组接口对象数组,那么代理类就实现了这些接口的所有方法并可调用。
h:InvocationHandler接口类型,代理类会通过InvocationHandler实现类的实例去调用invoke()方法处理逻辑。
2、上面已经说了,代理类会委派给InvocationHandler接口的实现类去处理任务,任务逻辑必经invoke()方法,那看看源码中它是什么样子的:
/**
* Processes a method invocation on a proxy instance and returns
* the result. This method will be invoked on an invocation handler
* when a method is invoked on a proxy instance that it is
* associated with.
*
* @param proxy the proxy instance that the method was invoked on
*
* @param method the {@code Method} instance corresponding to
* the interface method invoked on the proxy instance. The declaring
* class of the {@code Method} object will be the interface that
* the method was declared in, which may be a superinterface of the
* proxy interface that the proxy class inherits the method through.
*
* @param args an array of objects containing the values of the
* arguments passed in the method invocation on the proxy instance,
* or {@code null} if interface method takes no arguments.
* Arguments of primitive types are wrapped in instances of the
* appropriate primitive wrapper class, such as
* {@code java.lang.Integer} or {@code java.lang.Boolean}.
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
proxy:被代理的类。
method:调用时被代理的类的方法的Method对象。
args:调用时被代理的类的方法的参数。
三、实验数据
1、Subject接口
public interface Subject {
String doSomething();
}
2、Subject接口的实现类RealSubject
public class RealSubject implements Subject {
@Override
public String doSomething() {
System.out.println("我是真实的Object");
return "我是RealSubject";
}
}
3、InvocationHandler的实现类,Cache调用处理器
package com.ceam.designer.proxy.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class Cache implements InvocationHandler {
/**
* 这里封装真实类一般可以是:Object类型、接口类型、Class类型
*/
// private Object object;
//
// Cache(Object object) {
// this.object = object;
// }
private Subject object;
/**
* 初始化字段,method.invoke(object, args)中会用到该字段
*
* @param object 真实对象
*/
Cache(Subject object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("----Before handler----");
// 调用真实对象的方法,返回值result
Object result = method.invoke(object, args);
System.out.println((String)result);
System.out.println("----After handler----");
// 返回返回值
return result;
}
}
4、测试类
package com.ceam.designer.proxy.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class JDKProxyTest {
public static void main(String[] args) {
// 声明父类创建子类
Subject subject = new RealSubject();
// 封装真实的类
InvocationHandler cache = new Cache(subject);
// 生成代理实例,关联调用处理器
/**
* 关于入参的第一个第二个参数,一般是:第一个为接口的类类加载器,第二个为new Class[]{接口的Class}
*/
Subject proxy = (Subject)Proxy.newProxyInstance(Subject.class.getClassLoader(),
new Class[]{Subject.class}, cache);
String result = proxy.doSomething();
System.out.println("返回结果:" + result);
System.out.println("***********************************");
Subject proxy2 = (Subject)Proxy.newProxyInstance(Subject.class.getClassLoader(),
subject.getClass().getInterfaces(), cache);
proxy2.doSomething();
}
}
控制台打印:
----Before handler----
我是真实的Object
我是RealSubject
----After handler----
返回结果:我是RealSubject
***********************************
----Before handler----
我是真实的Object
我是RealSubject
----After handler----
Process finished with exit code 0
实验不会说谎,多动动手去做做实验,加深印象。改变一些数据,你可能获得意外的惊喜噢!本篇文章是本人在研究MyBatis源码时总结出的一些结论,含金量饱满,分享给大家,可谓用心良苦。
相关文章
- WIN7 64位系统安装JDK并配置环境变量
- Spring AOP源码分析(二)JDK动态代理和CGLIB介绍
- JRE和JDK的区别
- intellij idea 2021.2 修改一个springboot项目的jdk版本(jdk 15)
- [Java] JDK 系统环境变量设置 bat
- SpringAOP编程-传统基于JDK代理的AOP开发
- java动态代理(JDK和CGLIB)笔记
- Spring读源码系列之AOP--06---AopProxy===>spring使用jdk和cglib生成代理对象的终极奥义
- Atitit spring原理 反射 ioc 与注解api 目录 1. 反射的使用1 1.1. 使用jdk原生反射api1 1.2. 使用apache 工具包 commons-beanutil
- 基于JDK动态代理实现的接口链式调用(Fluent Interface)工具
- 明明白白WAS&amp;IBM JDK的版本以及它们各安装了哪些补丁!
- 分享一个JDK批量异步任务工具CompletionService,超好用
- JDK 动态代理与 CGLIB 动态代理,它俩真的不一样
- Spring的两种动态代理:Jdk和Cglib 的区别和实现
- Eclipse中如何查看使用的JDK版本
- CGLib与JDK的动态代理
- Mac下改动Android Studio 所用的JDK版本号
- 性能优于JDK代理,CGLib如何实现动态代理
- Eclipse创建java webproject配置Tomacat和JDK
- 程序员自己编写的类和JDK类是一种合作关系
- Spring JDK动态代理(附带实例)
- 【java】JDK动态代理原理