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动态代理
一.什么是代理模式 相信大家都知道代理商这个概念,在商业中,代理商无处不在.假设你要去买东西,你不可能去找真正的厂家去买,也不可能直接跟厂家提出需求,代理商就是这中间的一桥梁,连接买家和厂商.你要买或 ...
随机推荐
- .Net Core中的诊断日志DiagnosticSource讲解
前言 近期由于需要进行分布式链路跟踪系统的技术选型,所以一直在研究链路跟踪相关的框架.作为能在.Net Core中使用的APM,SkyWalking自然成为了首选.SkyAPM-dotnet是 ...
- Mybatis入门(一)------基本概念操作
Mybatis简介 Mybatis是支持定制化 SQL.存储过程以及高级映射的优秀的持久层框架.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBatis 可以对配置 ...
- 介绍 golang json数据的处理
原文链接:https://blog.csdn.net/weixin_43223076/article/details/83550229 demo1: package main import ( &qu ...
- Shell脚本最佳实践
Shell脚本最佳实践 0. 编码.缩进.文件命名和权限设置等 使用utf-8编码: 统一使用tab缩进或空格缩进,不要混用: 文件名以.sh结尾,并且统一风格: 添加可执行权限: chmod +x ...
- python 列表和字典的引用与复制(copy)
列表或字典的引用: 引用针对变量的时候,传递引用后,对引用后的对象的值进行改变是不会影响到原值的:而列表不一样如: spam =42 cheese = spam spam =100 print(spa ...
- Vue企业级优雅实战-00-开篇
从2018.1.开始参与了多个企业的中台建设,这些中台的技术选型几乎都是基于 Spring Cloud 微服务架构 + 基于 Vue 全家桶的前端.我前后端架构及开发我几乎各占一半的精力,在企业级前端 ...
- Orleans 知多少 | Orleans 中文文档上线
Orleans 简介 Orleans是一个跨平台框架,用于构建健壮,可扩展的分布式应用程序 Orleans建立在.NET开发人员生产力的基础上,并将其带入了分布式应用程序的世界,例如云服务. Orle ...
- 557反转字符串中的单词III
class Solution: # 定义一个反转字符串的函数. def str_rever(self,s): length = len(s) s1 = '' for index in range(le ...
- 攻防世界——Misc新手练习区解题总结<2>(5-8题)
第五题gif: 下载附件后,解压得到这样一个文件 几经寻找无果后,发现是不是可以将gif中的黑白图片看做二进制的数字,进而进行解密 最后用二进制转文本得到flag 第六题掀桌子: 看起来是16进制的密 ...
- Queries for Number of Palindromes(区间dp)
You've got a string s = s1s2... s|s| of length |s|, consisting of lowercase English letters. There a ...