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动态代理
一.什么是代理模式 相信大家都知道代理商这个概念,在商业中,代理商无处不在.假设你要去买东西,你不可能去找真正的厂家去买,也不可能直接跟厂家提出需求,代理商就是这中间的一桥梁,连接买家和厂商.你要买或 ...
随机推荐
- Centos7修改用户名
系统原来的用户lou,改为scrapy,要改以下个地方,注:没有修改对应密码 1. # vi /etc/passwd 修改其中的用户名部分.用户组部分.主目录部分2. 修改用户组的配置文件 # ...
- 使用分区挂载 ftp 目录
ftp挂载分区上去后无法识别的问题困扰了我好几天,今天有时间把它研究了一下,拿出来与大家分享一下. ftp目录挂载分区前与挂载分区后的区别[root@localhost ~]# ls -Zd /var ...
- neutron-server Connection pool is full, discarding connection 连接池过满
参考链接:https://zhiliao.h3c.com/Theme/details/48291 问题: -- ::33.235 WARNING requests.packages.urllib3.c ...
- Webstorm的常用快捷键
编辑 Ctrl + Space 基本代码完成 (任何类. 方法或变量名称) Ctrl + Shift + Enter 完整的语句 Ctrl + P (在方法调用参数) 内的参数信息 Ctrl + Q ...
- 区块链入门到实战(29)之Solidity – 环境搭建
在线开发环境Remix(推荐) 学习Solidity推荐使用在线开发环境Remix,本教程的例子将使用Remix开发运行. 安装本地编译器 安装 nodejs / npm node官方网站下载node ...
- 如何检查nofollow超链接属性是否有效
http://www.wocaoseo.com/thread-88-1-1.html nofollow 标签的重要性就不用阐述了,在这里武汉SEO与大家分享一些nofollow 标签的基本知识 ...
- ssm简单整合
pom.xml: <?xml version="1.0" encoding="UTF-8"?> <project xmlns="ht ...
- Asp.Net WebAPI的简介及创建
一.WebAPI简介 ASP.NET Web API 是一种框架,用于轻松构建可以由多种客户端(包括浏览器和移动设备)访问的 HTTP 服务.ASP.NET Web API 是一种用于在 .NET F ...
- day45:JS中的json&JS的BOM操作和DOM操作
目录 1.补充:CSS中的弹性盒子 2.JS中json的序列化 3.JS中的BOM操作 3.1 location操作 3.2 计时器 4.JS中的DOM操作 4.1 创建标签 4.2 查找标签 4.3 ...
- 期望概率DP
期望概率DP 1419: Red is good Description 桌面上有\(R\)张红牌和\(B\)张黑牌,随机打乱顺序后放在桌面上,开始一张一张地翻牌,翻到红牌得到1美元,黑牌则付 ...