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. Spring注解驱动开发01(组件扫描使用详解)

    使用Spring注解代替XML的方式 以前都是通过xml配bean的方式来完成bean对象放入ioc容器,即使通过@Aotuwire自动装配bean,还是要创建一个xml文件,进行包扫描,显得过于繁琐 ...

  2. RabbitMQ set password

    问题: -- ::09.387 ERROR oslo.messaging._drivers.impl_rabbit [req-51faf017-4f1f-4a24-ab79-624b302b839b ...

  3. aarch64 packages 地址链接收集

    1.http://dl.fedoraproject.org/pub/epel/7/aarch64 2.https://rpmfind.net/linux/epel/7/aarch64/ 3.http: ...

  4. cinderclient命令行源码解析

    一.简介 openstack的各个模块中,都有相应的客户端模块实现,其作用是为用户访问具体模块提供了接口,并且也作为模块之间相互访问的途径.Cinder也一样,有着自己的cinder-client. ...

  5. WordCount of Software Engineering

    1.Github项目地址:https://github.com/BayardM/WordCount 2.PSP表格(before): PSP2.1 Personal Software Process ...

  6. jdk8的下载、安装、配置

    jdk官方网址:https://www.oracle.com/java/technologies/javase-downloads.html 小编微信和公众号(很高兴能与各位交流学习) 以下是具体步骤 ...

  7. Vue基础(一)---- 模板语法

    1.基本理解 Vue其实是一个渐进式JavaScript框架,封装好了一些方法,不再需要操作通过操作DOM,在相同的目标下能够更快的编写代码. 声明式渲染→组件系统→客户端路由→集中式状态管理→项目构 ...

  8. 网络请求以及网络请求下载图片的工具类 android开发java工具类

    package cc.jiusan.www.utils; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; ...

  9. unity 对Animator动画系统的研究

    unity的新动画系统叫Mecanim,使用Animator来取代旧系统Animation,按Unity文档的惯例:知识点主要分2部分:unity manual和unity script,读者可以边看 ...

  10. unity3d屏幕截图功能

    function OnGUI(){ if(GUI.Button(Rect(Screen.width*0.5-50,Screen.height*0.5-50,100,100),"screen& ...