jdk动态代理底层实现
一、代理设计模式
代理设计模式是Java常用的设计模式之一。
特点:
01.委托类和代理类有共同的接口或者父类;
02.代理类负责为委托类处理消息,并将消息转发给委托类;
03.委托类和代理类对象通常存在关联关系,一个代理类对象与一个委托类对象关联;
04.代理类本身不是真正的实现者,而是通过调用委托类方法来实现代理功能;
二、静态代理与动态代理
按照代理类创建的时机,代理类分为两种:
01.静态代理:由我们程序猿或者特定的工具自动生成了源代码,在程序运行之前,class文件已经存在了;例如在serviceImpl.java中调用dao.xx(),真正的实现者是dao,service就可以理解为一个代理类;
02.动态代理:在程序运行期间,通过反射创建出来的代理类;
三、jdk动态代理
顾名思义,这种方式是由jdk为我们提供的。下面通过一个例子来演示。
01.创建一个Person接口
public interface Person {
void eat();
void sleep();
}
02.创建ZhangSan.java实现Person接口
public class ZhangSan implements Person { public void eat() {
System.out.println("吃饭...");
} public void sleep() {
System.out.println("睡觉...");
} }
03.创建代理类ZSProxy.java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method; public class ZSProxy implements InvocationHandler { //被代理类对象引用
private ZhangSan zhangSan; public ZSProxy(ZhangSan zhangSan) {
super();
this.zhangSan = zhangSan;
} public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
//zhangSan.eat();
Object result = method.invoke(zhangSan, args);//可以获取目标方法的返回值
after();
return null;
} private void before() {
System.out.println("前置...");
}
private void after() {
System.out.println("后置...");
}
}
jdk动态代理中必须了解一个类和一个接口:Proxy类和InvocationHandler接口。
001.上述中的代理类实现了 InvocationHandler接口,此接口只有一个invoke方法
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
proxy:代理类对象
method:被代理的方法
args:被代理方法的参数列表
002.Proxy类
public class Proxy implements java.io.Serializable {
... public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException ...
}
loader:类加载器
interfaces:代理类实现的所有接口
h:InvocationHandler接口的一个实例
public class Test { public static void main(String[] args) throws Throwable {
System.out.println("----------------------JDK动态代理----------------------------");
//获取代理类对象
Person proxyInstance = (Person)Proxy.newProxyInstance(Person.class.getClassLoader(),
new Class[] {Person.class}, new ZSProxy(new ZhangSan()));
proxyInstance.eat();
}
通过Proxy的newProxyInstance方法创建出代理对象,再有代理对象执行方法。
程序运行结果:
虽然效果实现了,但我们并不能从代码看到哪里调用的invoke方法??那么底层到底是怎么执行的呢??
首先要了解一个类 ==》$Proxy0.java
001. $Proxy0 是内存中的代理类,在$Proxy0中会持有一个InvocationHandler接口的实例类的引用,所以此Test类先是调用了内存中的$Proxy0.eat();
002.执行$Proxy0类中的invoke
我们debug运行观察:
内存中代理类的特征:
01.它是对目标类的代理,那么这个内存中中的代理类的类型必须和被代理类(目标类)的类型一致。
代码中 (Person)Proxy.newProxyInstance.. 进行了向下转型操作。
02.内存中的代理类必须持有InvocationHandler接口的实现类引用。
整个过程中$Proxy0我们是看不到的!那么有没有办法让它原形毕露呢?
/**
* 使用IO的方式将内存中代理类写入到文件中,然后反编译出来进行观察
*/
private static void createProxyClassFile() {
byte[] data = ProxyGenerator.generateProxyClass("$Proxy0.class", new Class[] {Person.class}); try {
FileOutputStream out = new FileOutputStream("$Proxy0.class");
out.write(data);
out.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
对$Proxy0.java反编译后
public final class class extends Proxy
implements Person
{ private static Method m1;
private static Method m4;
private static Method m2;
private static Method m3;
private static Method m0; public class(InvocationHandler invocationhandler) //通过构造将handler传入
{
super(invocationhandler);
} public final boolean equals(Object obj)
{
try
{
return ((Boolean)super.h.invoke(this, m1, new Object[] {
obj
})).booleanValue();
}
catch (Error ) { }
catch (Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
} public final void eat()
{
try
{
super.h.invoke(this, m4, null);// h:就是上文说的 此类中必须存在InvocationHandler接口实现类的引用,也是这里真正调用了invoke方法
return;
}
catch (Error ) { }
catch (Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
} public final String toString()
{
try
{
return (String)super.h.invoke(this, m2, null);
}
catch (Error ) { }
catch (Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
} public final void sleep()
{
try
{
super.h.invoke(this, m3, null);
return;
}
catch (Error ) { }
catch (Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
} public final int hashCode()
{
try
{
return ((Integer)super.h.invoke(this, m0, null)).intValue();
}
catch (Error ) { }
catch (Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
} static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {
Class.forName("java.lang.Object")
});
m4 = Class.forName("cn.yzx.jdkProxy.Person").getMethod("eat", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m3 = Class.forName("cn.yzx.jdkProxy.Person").getMethod("sleep", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
}
catch (NoSuchMethodException nosuchmethodexception)
{
throw new NoSuchMethodError(nosuchmethodexception.getMessage());
}
catch (ClassNotFoundException classnotfoundexception)
{
throw new NoClassDefFoundError(classnotfoundexception.getMessage());
}
}
}
那么就真相大白了。
********************************************************************************************************************************************
以上就是jdk为我们提供的动态代理。我们也可以模仿它的实现原理,自定义我们的动态代理。
01.创建Person接口
package cn.yzx.myProxy; public interface Person {
void eat() throws Throwable;
void sleep() throws Throwable;
}
02.创建ZhangSan实现类
package cn.yzx.myProxy; public class ZhangSan implements Person { public void eat() {
System.out.println("吃饭...");
} public void sleep() {
System.out.println("睡觉...");
}
}
03.ZSProxy代理类
package cn.yzx.myProxy; import java.lang.reflect.Method; public class ZSProxy implements MyInvocationHandler {
//目标对象
private Person person; public ZSProxy(Person person) {
this.person = person;
} @Override
public Object invoke(Object proxy, Method method, Object args) {
before();
try {
person.eat();
} catch (Throwable e) {
e.printStackTrace();
}
after();
return null;
} private void before() {
System.out.println("前置...");
}
private void after() {
System.out.println("后置...");
} }
04.自定义动态代理Proxy类
package cn.yzx.myProxy; import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider; /**
* 自定义的动态代理Proxy类
* @author Xuas
*/
public class MyProxy {
static String rt = "\r\n";
/**
* 自定义创建内存中代理实例的方法
* @param loader
* @param interfaces
* @param handler
* @return
*/
public static Object newProxyInstance(ClassLoader loader, Class interfaces, MyInvocationHandler handler) { try {
Method[] methods = interfaces.getMethods();
//01.使用拼凑字符串的方式将内存中的代理类拼出来
String proxyClass = "package cn.yzx.myProxy;" + rt + "import java.lang.reflect.Method;" + rt
+ "public class $Proxy0 implements " + interfaces.getName() + "{" + rt + "MyInvocationHandler h;" + rt
+ "public $Proxy0(MyInvocationHandler h) {" + rt + "this.h = h;" + rt + "}"
+ getMethodString(methods, interfaces) + rt + "}";
//02.使用IO将拼凑的代理类写入到文件中
String filePathName = "F:/Java/SpringAopDemo/src/main/java/cn/yzx/myProxy/$Proxy0.java";
File file = new File(filePathName);
FileWriter fw = new FileWriter(file);
fw.write(proxyClass);
fw.flush();
fw.close(); //03.编译上面生成的java文件
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null,
null, null);//文件管理器
Iterable javaFileObjects = fileManager.getJavaFileObjects(filePathName);//获取java文件
CompilationTask task = compiler.getTask(null, fileManager, null, null, null, javaFileObjects);//获取编译任务
task.call();//编译
fileManager.close();//关闭文件管理器
/**
* 此时被编译后的class文件还在硬盘之中!它应该存在于JVM内存中!
* 所以要把硬盘中class文件加载到JVM内存中去!
*/
//04.把硬盘中的class文件加载到内存中去,要使用ClassLoader
MyClassLoader loader1 = new MyClassLoader("F:/Java/SpringAopDemo/src/main/java/cn/yzx/myProxy"); //proxy0Clazz:就是内存中代理类的class对象
Class<?> proxy0Clazz = loader1.findClass("$Proxy0");//返回被代理类的class对象
/**
* public $Proxy0(MyInvocationHandler h) {..}
* 要通过构造器的参数将handler对象的引用传进来
*/
Constructor<?> constructor = proxy0Clazz.getConstructor(MyInvocationHandler.class);
Object instance = constructor.newInstance(handler);//返回内存中代理类实例
return instance;
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
} private static String getMethodString(Method[] methods, Class interfaces) { String proxyMe = ""; for (Method method : methods) {
proxyMe += "public void " + method.getName() + "() throws Throwable {" + rt + "Method md = "
+ interfaces.getName() + ".class.getMethod(\"" + method.getName() + "\",new Class[]{});" + rt
+ "this.h.invoke(this,md,null);" + rt + "}" + rt;
} return proxyMe;
}
}
05.自定义InvocationHandler接口
package cn.yzx.myProxy; import java.lang.reflect.Method; /**
* 自定义InvocationHandler接口
* @author Xuas
*
*/
public interface MyInvocationHandler {
public Object invoke(Object proxy, Method method, Object args);
}
06.自定义的类加载器
package cn.yzx.myProxy; import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException; public class MyClassLoader extends ClassLoader { private File dir; public MyClassLoader(String path) {//要加载的文件路径
dir = new File(path);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException { if(null != dir) {
File clazzFile = new File(dir, name + ".class");
if(clazzFile.exists()) {
try {
FileInputStream input = new FileInputStream(clazzFile);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
while((len = input.read(buffer)) != -1) {
baos.write(buffer, 0, len);
}
return defineClass("cn.yzx.myProxy."+name,
baos.toByteArray(),
0,
baos.size());//最终把输出流输出到JVM内存中
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return super.findClass(name);
} }
07.内存中自定义生成的 $Proxy0.java
package cn.yzx.myProxy;
import java.lang.reflect.Method;
public class $Proxy0 implements cn.yzx.myProxy.Person{
MyInvocationHandler h;
public $Proxy0(MyInvocationHandler h) {
this.h = h;
}public void sleep() throws Throwable {
Method md = cn.yzx.myProxy.Person.class.getMethod("sleep",new Class[]{});
this.h.invoke(this,md,null);
}
public void eat() throws Throwable {
Method md = cn.yzx.myProxy.Person.class.getMethod("eat",new Class[]{});
this.h.invoke(this,md,null);
} }
08.测试类
public class Test { public static void main(String[] args) throws Throwable {
System.out.println("----------------------自定义动态代理----------------------------");
cn.yzx.myProxy.Person person2 = (cn.yzx.myProxy.Person)MyProxy.newProxyInstance(cn.yzx.myProxy.Person.class.getClassLoader(),
cn.yzx.myProxy.Person.class,
new cn.yzx.myProxy.ZSProxy(new cn.yzx.myProxy.ZhangSan()));
person2.eat();
}
}
程序运行结果:
************************************************************************************************************************************************
总结:
通过观察Proxy中的newProxyInstance方法的参数可知,jdk动态代理只支持委托类和代理类实现共同的接口的方式。
如果是实现共同父类的情况,不能使用jdk动态代理。可以使用cglib动态代理。
既然写到这了,那我就再简单介绍下cglib动态代理吧 -。 -
cglib动态代理(接口和父类的情况都可行)
两个关键因素:
01.MethodInterceptor接口中的interceptor方法。
public Object intercept(Object obj,
Method method,Object[] args,MethodProxy proxy) throws Throwable;
interceptor是所有拦截器执行的方法,类似于jdk动态代理中的invoke
02.Enhancer类
public void setSuperclass(Class superclass) {
if (superclass != null && superclass.isInterface()) {
setInterfaces(new Class[]{ superclass });
} else if (superclass != null && superclass.equals(Object.class)) {
// affects choice of ClassLoader
this.superclass = null;
} else {
this.superclass = superclass;
}
}
设置委托类和代理类的公共接口或者公共父类。
代理类执行完毕后,通知委托类:
public void setCallback(final Callback callback) {
setCallbacks(new Callback[]{ callback });
}
在Enhancer的父类AbstractClassGenerator中有一个方法 作用是创建需要的代理类
protected Object create(Object key)
写个例子:
01.创建父类Animal
package cn.yzx.cglib; public abstract class Animal {
abstract String eat();
}
02.实现类Cat
package cn.yzx.cglib; public class Cat extends Animal{
public String eat() {
System.out.println("cat吃饭");
return "haha";
}
}
03.代理类
package cn.yzx.cglib; import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.junit.Test; import java.lang.reflect.Method; public class CglibProxy implements MethodInterceptor { private Enhancer enhancer = new Enhancer(); /**
* 创建代理类对象
* 参数:委托类Class对象
* 返回值:代理类对象
*/
public Object createProxyObject(Class<?> clazz){
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
} /**
* 代理类执行委托类方法
*/
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("前置");
Object result = methodProxy.invokeSuper(o,args);
System.out.println("后置");
System.out.println(result);
return result;
} /**
* 测试方法
*/ @Test
public void test(){
CglibProxy proxy = new CglibProxy();
Animal cat = (Animal) proxy.createProxyObject(Cat.class);
cat.eat();
}
}
测试结果:
ok...
就酱吧
jdk动态代理底层实现的更多相关文章
- 高仿JDK动态代理 底层源码实现
动态代理实现思路 实现功能:通过Proxy.newProxyInstance返回代理对象 1.创建一个处理业务逻辑的接口,我们也和JDK一样,都使用InvocationHandler作为接口名,然后接 ...
- AOP的底层实现:JDK动态代理与Cglib动态代理
转载自 https://www.cnblogs.com/ltfxy/p/9872870.html SpringAOP底层的实现原理: JDK动态代理:只能对实现了接口的类产生代理.(实现接口默认JDK ...
- JDK动态代理[2]----JDK动态代理的底层实现之Proxy源码分析
在上一篇里为大家简单介绍了什么是代理模式?为什么要使用代理模式?并用例子演示了一下静态代理和动态代理的实现,分析了静态代理和动态代理各自的优缺点.在这一篇中笔者打算深入源码为大家剖析JDK动态代理实现 ...
- spring AOP底层原理实现——jdk动态代理
spring AOP底层原理实现——jdk动态代理
- AOP的底层实现-CGLIB动态代理和JDK动态代理
AOP是目前Spring框架中的核心之一,在应用中具有非常重要的作用,也是Spring其他组件的基础.它是一种面向切面编程的思想.关于AOP的基础知识,相信多数童鞋都已经了如指掌,我们就略过这部分,来 ...
- jdk动态代理和cglib动态代理底层实现原理详细解析(cglib动态代理篇)
代理模式是一种很常见的模式,本文主要分析cglib动态代理的过程 1. 举例 使用cglib代理需要引入两个包,maven的话包引入如下 <!-- https://mvnrepository.c ...
- jdk动态代理和cglib动态代理底层实现原理超详细解析(jdk动态代理篇)
代理模式是一种很常见的模式,本文主要分析jdk动态代理的过程 1.举例 public class ProxyFactory implements InvocationHandler { private ...
- Spring中的JDK动态代理
Spring中的JDK动态代理 在JDK1.3以后提供了动态代理的技术,允许开发者在运行期创建接口的代理实例.在Sun刚推出动态代理时,还很难想象它有多大的实际用途,现在动态代理是实现AOP的绝好底层 ...
- JDK动态代理与CGLib动态代理
1.JDK动态代理 JDK1.3以后java提供了动态代理技术,允许开发者在运行期创建接口的代理实例,动态代理是实现AOP的绝好底层技术. JDK的动态代理主要涉及到java.lang.reflect ...
随机推荐
- ubuntu set up 6 - NTFS Mount
1. NTFS Mounted as read-only https://askubuntu.com/questions/1138076/ubuntu-18-04-cant-write-on-ntfs ...
- IntelliJ IDEA 如何彻底删除项目的步骤
原文参考链接:https://www.jb51.net/article/129473.htm 本文介绍了IntelliJ IDEA 如何彻底删除项目的步骤,分享给大家,顺便给自己留个笔记,具体如下: ...
- SQLServer 数据库索引碎片
--改成当前库 use DB_Name --创建变量 指定要查看的表 declare @table_id int set @table_id=object_id('TableName') --执行 d ...
- react-native构建基本页面5---调用拍照摄像头
调用摄像头拍照 react-native-image-picker的github官网 yarn add react-native-image-picker 运行react-native link自动注 ...
- 统计redis大key信息(前topN)
相关包下载链接 https://github.com/sripathikrishnan/redis-rdb-tools/releaseshttps://pypi.org/project/python- ...
- css实现梯形样式(含有border)
类似本文热门评论 效果 .hot-comment-title{ float:right; position:absolute; right: -8px; top: -30px; padding: 0; ...
- Win10安装3 —— U盘启动工具安装
本文内容皆为作者原创,如需转载,请注明出处:https://www.cnblogs.com/xuexianqi/p/12364593.html 一:准备一个空U盘 U盘容量推荐至少8个G,先提前备份好 ...
- jQuery使用ajax跨域请求获取数据
jQuery使用ajax跨域请求获取数据 跨域是我在日常面试中经常会问到的问题,这词在前端界出现的频率不低,主要原因还是由于安全限制(同源策略, 即JavaScript或Cookie只能访问同域下的 ...
- Selenium3+python自动化007-等待时间
import time 一.强制等待 sleep(秒) 二.显示等待 使WebDriver等待指定元素条件成立时继续执行,否则在达到最大时长时抛出超时异常(TimeoutException) 1. 导 ...
- AntDesign(React)学习-8 Menu使用 切换框架页内容页面
本节实现一个点击左侧menu在右侧content切换页面效果,原始代码请从UMI学习-6开始看 1.在pages下添加两个组件,User,UserRole import React from 'rea ...