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动态代理学习心得的更多相关文章

  1. java jdk动态代理学习记录

    转载自: https://www.jianshu.com/p/3616c70cb37b JDK自带的动态代理主要是指,实现了InvocationHandler接口的类,会继承一个invoke方法,通过 ...

  2. jdk动态代理学习

    在jdk的好多底层代码中很多都使用jdk的动态代理,下面就写写简单的代码来look look. 老规矩先上代码: public interface SayDao { public String say ...

  3. AOP学习心得&jdk动态代理与cglib比较

    什么是AOP AOP(Aspect-OrientedProgramming,面向方面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善.OOP引入 ...

  4. aop学习总结一------使用jdk动态代理简单实现aop功能

    aop学习总结一------使用jdk动态代理实现aop功能 动态代理:不需要为目标对象编写静态代理类,通过第三方或jdk框架动态生成代理对象的字节码 Jdk动态代理(proxy):目标对象必须实现接 ...

  5. 动态代理学习(二)JDK动态代理源码分析

    上篇文章我们学习了如何自己实现一个动态代理,这篇文章我们从源码角度来分析下JDK的动态代理 先看一个Demo: public class MyInvocationHandler implements ...

  6. 动态代理学习(一)自己动手模拟JDK动态代理

    最近一直在学习Spring的源码,Spring底层大量使用了动态代理.所以花一些时间对动态代理的知识做一下总结. 我们自己动手模拟一个动态代理 对JDK动态代理的源码进行分析 文章目录 场景: 思路: ...

  7. 学习CGLIB与JDK动态代理的区别

    动态代理 代理模式是Java中常见的一种模式.代理又分为静态代理和动态代理.静态代理就是显式指定的代理,静态代理的优点是由程序员自行指定代理类并进行编译和运行,缺点是一个代理类只能对一个接口的实现类进 ...

  8. Java学习笔记--JDK动态代理

    1.JDK动态代理     JDK1.3之后,Java提供了动态代理的技术,允许开发者在运行期创建接口的代理实例.JDK的动态代理主要涉及到java.lang.reflect包中的两个类:Proxy和 ...

  9. java学习笔记(中级篇)—JDK动态代理

    一.什么是代理模式 相信大家都知道代理商这个概念,在商业中,代理商无处不在.假设你要去买东西,你不可能去找真正的厂家去买,也不可能直接跟厂家提出需求,代理商就是这中间的一桥梁,连接买家和厂商.你要买或 ...

随机推荐

  1. Jmeter 常用函数(24)- 详解 __digest

    如果你想查看更多 Jmeter 常用函数可以在这篇文章找找哦 https://www.cnblogs.com/poloyy/p/13291704.htm 作用 在特定的哈希算法中返回加密的值,并带有可 ...

  2. Python中print()函数不换行的方法以及分隔符替换

    一.让print()函数不换行 在Python中,print()函数默认是换行的.但是,在很多情况下,我们需要不换行的输出(比如在算法竞赛中).那么,在Python中如何做到这一点呢? 其实很简单.只 ...

  3. shell 三剑客之 awk

    awk 是shell 里的常用命令,非常强大!

  4. Qt QString转char[]数组

    Qt QString转char[]数组 QString s1="1234456";char str[20]={0};strcpy(str,s1.toStdString().c_st ...

  5. android开发之gridView的一些属性。(项目经验总结)

    1.android:numColumns="auto_fit"   //GridView的列数设置为自动 2.android:columnWidth="90dp &quo ...

  6. C++从LPEXCEPTION_POINTERS获取调用堆栈

    #pragma once #include <map> #include <vector> struct FunctionCall { DWORD64 Address; std ...

  7. 组件给App全局传值vue-bus的使用

    npm安装 npm install vue-bus main.js引入 import VueBus from 'vue-bus' Vue.use(VueBus) 组件 getHouse(e){ thi ...

  8. Activiti7 生成表结构

    首先创建一个Maven项目 整体的项目结构 activiti.cfg.xml配置文件 <?xml version="1.0" encoding="UTF-8&quo ...

  9. Cocos Creator 性能优化:DrawCall

    前言 在游戏开发中,DrawCall 作为一个非常重要的性能指标,直接影响游戏的整体性能表现. 无论是 Cocos Creator.Unity.Unreal 还是其他游戏引擎,只要说到游戏性能优化,D ...

  10. SpringBean容器启动流程+Bean的生命周期【附源码】

    如果对SpringIoc与Aop的源码感兴趣,可以访问参考:https://javadoop.com/,十分详细. 目录 Spring容器的启动全流程 Spring容器关闭流程 Bean 的生命周期 ...