JDK动态代理学习心得
JDK动态代理是代理模式的一种实现方式,其只能代理接口。应用甚为广泛,比如我们的Spring的AOP底层就有涉及到JDK动态代理(此处后面可能会分享)
1、首先来说一下原生的JDK动态代理如何实现: 首先声明一个接口:

然后一个实现类实现了接口的方法:

然后申明一个类实现InvocationHandler接口,实现里卖弄的invoke方法:红框中的内容就是我们的代理内容

最后上一个测试类:

我们可以看出运行结果:

看源码分析:
根据Proxy类里面的newProxyInstance方法着手跟踪源码可以看出,通过接口反射的到字节码byte[],然后通过一个native(本地) 方法把字节码转成class 这个方法在openJDK 中有 用c++写的(到此就研究不到了)

下面我们就原生的JDK动态代理来手写一个山寨版的JDK动态代理:
1、首先创建一个接口类CoustomInvocationHandler,定义一个invoke方法,传递参数是Method类型:
package com.jdkproxy.myjdkprpoxy;
import java.lang.reflect.Method;
//模拟InvocationHandler
public interface CoustomInvocationHandler {
public Object invoke(Method method);
}
2、创建目标接口
package com.jdkproxy.jdkproxy;
public interface TestDao
{
public String proxy() throws Exception;
public String proxy1(String a) throws Exception;
public String proxy2(String a, int b) throws Exception;
}
3、创建目标接口实现类
package com.jdkproxy.jdkproxy;
public class TestDaoImpl implements TestDao{
public String proxy()throws Exception {
System.out.println("实际逻辑");
return "proxy";
}
public String proxy1(String a)throws Exception {
System.out.println("实际逻辑1");
return "proxy";
}
public String proxy2(String a,int b)throws Exception {
System.out.println("实际逻辑2");
return "proxy";
}
}
3、然后写一个该接口的实现类,实现invoke方法,通过参数传递一个Object的对象(其实就是要代理的目标对象)创建他的构造方法,invoke方法里面通过目标方法和目标对象实现方法的反射调用,在调用目标方法之前就可以写代理的逻辑代码了;也就是说该实现类里面只要拿到要执行的方法和目标对象,就可以调用到目标方法;
package com.jdkproxy.myjdkprpoxy;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class TestCustomHandler implements CoustomInvocationHandler {
Object target;
public TestCustomHandler(Object target){//此处得到的是传递进来的接口的实现类
this.target=target;
}
public Object invoke(Method method) {
try {
System.out.println("proxy逻辑");
return method.invoke(target);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
}
4、然后根据上面一步的分析可以知道,我们可以通过一个携带了目标对象的CoustomInvocationHandler类型的对象.invoke(传入方法)的方式就可以调用调用到我们的目标方法,解释一下这句话:就是说我们可以通过传入目标对象的方式,new一个实现了CoustomInvocationHandler接口的对象,然后拿到目标对象类里面的所有方法(此处传递一个接口,通过接口得到里面的所有方法),再通过这个CoustomInvocationHandler对象去调用invoke方法,动态传递mothod,就可以实现目标对象的方法的调用了。所以我们需要动态的创建这个么一个类,类里面包含了目标对象的所有方法和携带了目标对象的CoustomInvocationHandler的实现类对象;
需要这么一个类:
package com.jdkproxy.myjdkprpoxy;
import com.jdkproxy.jdkproxy.TestDao;
import java.lang.Exception;import java.lang.reflect.Method;
public class $Proxy implements TestDao {
private CoustomInvocationHandler h;
public $Proxy (CoustomInvocationHandler h){
this.h =h;
}
public String proxy()throws Exception {
Method method = Class.forName("com.luban.proxy.dao.LubanDao").getDeclaredMethod("proxy");
return (String)h.invoke(method);
}
public String proxy1(String a)throws Exception {
Method method = Class.forName("com.luban.proxy.dao.LubanDao").getDeclaredMethod("proxy1");
return (String)h.invoke(method);
}
public String proxy2(String a,int b)throws Exception {
Method method = Class.forName("com.luban.proxy.dao.LubanDao").getDeclaredMethod("proxy2");
return (String)h.invoke(method);
}
}
我们需要编写代码实现动态生成上面这个类文件的方法:
package com.jdkproxy.myjdkprpoxy;
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
public class ProxyUtil {
/**
*
* @param targetInf
* @param h
* @return
*/
public static Object newInstance(Class targetInf, CoustomInvocationHandler h){
Object proxy=null;
Method methods[] =targetInf.getDeclaredMethods();//得到目标接口里面的所有方法
String line="\n";
String tab ="\t";
String infName = targetInf.getSimpleName();//得到目标接口的类名
String content ="";
String packageContent = "package com.google;"+line;//声明包路径
String importContent = "import "+targetInf.getName()+";"+line
+"import com.luban.proxy.dao.CoustomInvocationHandler;"+line
+"import java.lang.Exception;"+line
+"import java.lang.reflect.Method;"+line;//申明import依赖类
String clazzFirstLineContent = "public class $Proxy implements "+infName+"{"+line;
String filedContent =tab+"private CoustomInvocationHandler h;"+line;
String constructorContent =tab+"public $Proxy (CoustomInvocationHandler h){" +line
+tab+tab+"this.h =h;"
+line+tab+"}"+line;
String methodContent = "";
for (Method method : methods) {
String returnTypeName = method.getReturnType().getSimpleName();
String methodName =method.getName();
Class args[] = method.getParameterTypes();
String argsContent = "";
String paramsContent="";
int flag =0;
for (Class arg : args) {
String temp = arg.getSimpleName();
argsContent+=temp+" p"+flag+",";
paramsContent+="p"+flag+",";
flag++;
}
if (argsContent.length()>0){
argsContent=argsContent.substring(0,argsContent.lastIndexOf(",")-1);
paramsContent=paramsContent.substring(0,paramsContent.lastIndexOf(",")-1);
}
methodContent+=tab+"public "+returnTypeName+" "+methodName+"("+argsContent+")throws Exception {"+line
+tab+tab+"Method method = Class.forName(\""+targetInf.getName()+"\").getDeclaredMethod(\""+methodName+"\");"+line
+tab+tab+"return ("+returnTypeName+")h.invoke(method);"+line;
methodContent+=tab+"}"+line;
}
content=packageContent+importContent+clazzFirstLineContent+filedContent+constructorContent+methodContent+"}";
File file =new File("d:\\com\\google\\$Proxy.java");
try {
if (!file.exists()) {
file.createNewFile();
}
FileWriter fw = new FileWriter(file);
fw.write(content);
fw.flush();
fw.close();
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
Iterable units = fileMgr.getJavaFileObjects(file);
JavaCompiler.CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
t.call();
fileMgr.close();
URL[] urls = new URL[]{new URL("file:D:\\\\")};
URLClassLoader urlClassLoader = new URLClassLoader(urls);
Class clazz = urlClassLoader.loadClass("com.google.$Proxy");
Constructor constructor = clazz.getConstructor(CoustomInvocationHandler.class);
proxy = constructor.newInstance(h);
}catch (Exception e){
e.printStackTrace();
}
return proxy;
}
}
5、最后创建测试类:
package com.jdkproxy.myjdkprpoxy;
import com.jdkproxy.jdkproxy.TestDao;
import com.jdkproxy.jdkproxy.TestDaoImpl;
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args) {
//自定义实现 通过传递目标接口(可以拿到方法)、目标对象
TestDao proxy = (TestDao) ProxyUtil.newInstance(TestDao.class,new TestCustomHandler(new TestDaoImpl()));
try {
proxy.proxy();
} catch (Exception e) {
e.printStackTrace();
}
}
}
运行得到结果如下:
proxy逻辑
实际逻辑
JDK动态代理学习心得的更多相关文章
- java jdk动态代理学习记录
转载自: https://www.jianshu.com/p/3616c70cb37b JDK自带的动态代理主要是指,实现了InvocationHandler接口的类,会继承一个invoke方法,通过 ...
- jdk动态代理学习
在jdk的好多底层代码中很多都使用jdk的动态代理,下面就写写简单的代码来look look. 老规矩先上代码: public interface SayDao { public String say ...
- AOP学习心得&jdk动态代理与cglib比较
什么是AOP AOP(Aspect-OrientedProgramming,面向方面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善.OOP引入 ...
- aop学习总结一------使用jdk动态代理简单实现aop功能
aop学习总结一------使用jdk动态代理实现aop功能 动态代理:不需要为目标对象编写静态代理类,通过第三方或jdk框架动态生成代理对象的字节码 Jdk动态代理(proxy):目标对象必须实现接 ...
- 动态代理学习(二)JDK动态代理源码分析
上篇文章我们学习了如何自己实现一个动态代理,这篇文章我们从源码角度来分析下JDK的动态代理 先看一个Demo: public class MyInvocationHandler implements ...
- 动态代理学习(一)自己动手模拟JDK动态代理
最近一直在学习Spring的源码,Spring底层大量使用了动态代理.所以花一些时间对动态代理的知识做一下总结. 我们自己动手模拟一个动态代理 对JDK动态代理的源码进行分析 文章目录 场景: 思路: ...
- 学习CGLIB与JDK动态代理的区别
动态代理 代理模式是Java中常见的一种模式.代理又分为静态代理和动态代理.静态代理就是显式指定的代理,静态代理的优点是由程序员自行指定代理类并进行编译和运行,缺点是一个代理类只能对一个接口的实现类进 ...
- Java学习笔记--JDK动态代理
1.JDK动态代理 JDK1.3之后,Java提供了动态代理的技术,允许开发者在运行期创建接口的代理实例.JDK的动态代理主要涉及到java.lang.reflect包中的两个类:Proxy和 ...
- java学习笔记(中级篇)—JDK动态代理
一.什么是代理模式 相信大家都知道代理商这个概念,在商业中,代理商无处不在.假设你要去买东西,你不可能去找真正的厂家去买,也不可能直接跟厂家提出需求,代理商就是这中间的一桥梁,连接买家和厂商.你要买或 ...
随机推荐
- N叉树的前后序遍历和最大深度
package NTree; import java.util.ArrayList; import java.util.List; /** * N叉树的前后序遍历和最大深度 */ public cla ...
- ES7异步函数解决进程等待相关业务问题
业务需求场景描述: 在接口只能单一检测的情况下,批量检测资源名称是否存在数据库,如果资源群中某一个资源已存在:给出交互让用户决定是否覆盖资源,最后形成不存在的资源和用户确定覆盖的资源群,进行提交. 业 ...
- 在Linux命令行中使用计算器的5个命令
大家好,我是良许. 在使用 Linux 时,我们有时会需要做一些计算,那么我们就可能需要用到计算器.在 Linux 命令行里,有许多计算器工具,这些命令行计算器可以让我们执行科学计算.财务计算或者一些 ...
- 系综的实现方式(nve、nvt、npt)
一.NVE系综 更新位置和速度,v和e恒定. 二.NVT系综 几种实现的方式: 如: fix 1 all nve #更新位置和速度,e和V保持不变.若只有这个命令,就只nve系综,如果和控温命令一起, ...
- WPF实现飞控姿态仪表盘控件Attitude dashboard
一.概要 近期项目当中需要用到飞机控制仪表盘的姿态仪,一开始去各大网站搜索解决方案要么就是winfrom要么就是很老的代码根本不能运行更甚者是居然有的还要下载积分. 只能自己手动从0开始写一个控件.这 ...
- oracle的system登不了
(密码对的,密码错直接就是被拒了) 这个一直弹出改密码 但是改了点[确定],又说 oracle改system密码 [oracle@localhost ~]$ sqlplus / as sysdba S ...
- Asterisk-WebRTC客户端的部署
很久没有写博客了.最近完成asterisk 和 jssip的库集成,浏览器支持chrome/firefox.在集成的过程中遇到了一些问题,在这儿分享出来,免得大家走弯路. 在网上看一些帖子,环境都是 ...
- 蒲公英 · JELLY技术周刊 Vol.20: Vue3 极致优化——分析 Vue3 Compiler 告诉你为什么这么快
蒲公英 · JELLY技术周刊 Vol.20 性能优化是一条无尽的路,我们总是可以找到各种途径去提升体验,不论是响应时间还是按需加载,亦或是根据框架或者组件有针对性的优化都会是不错的方法.如果你在使用 ...
- 网络请求以及网络请求下载图片的工具类 android开发java工具类
package cc.jiusan.www.utils; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; ...
- Vue.js学习(八)—— 树形结构下拉框组件vue-treeselect
vue-treeselect是一个多选组件,具有对Vue.js的嵌套选项支持. 具有嵌套选项支持的单个和多个选择 模糊匹配 异步搜索 延迟加载(仅在需要时加载深层选项的数据) 键盘支持(使用Arrow ...