动态代理实现思路

实现功能:通过Proxy.newProxyInstance返回代理对象

1、创建一个处理业务逻辑的接口,我们也和JDK一样,都使用InvocationHandler作为接口名,然后接口里面一个invoke方法,jdk呢是有三个参数,我们简化了一下就只要两个参数即可。

2、声明一段java代码字符串(动态产生代理类)

3、使用IO创建代理类的java文件,将java代码字符串写入java文件

4、编译代理类,通过URLClassLoader把代理类加载到内存中

5、使用Constructor(构造器)来创建代理类的实例

6、return 代理类

一、创建一个处理业务逻辑的接口(InvocationHandler)
package com.handler;
import java.lang.reflect.Method; public interface InvocationHandler {
public Object invoke(Method method,Object args[]);
}

二、创建一个Proxy里面只有一个静态方法newProxyInstance

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.JavaCompiler.CompilationTask;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import com.handler.InvocationHandler;
public class Proxy {
/**
* 动态生成一个动态代理类
* @param interfaceClass 实现的接口
* @param handler 处理的业务逻辑
* @return
*/
public static Object newProxyInstance(Class interfaceClass,InvocationHandler handler){
String interfaceName=interfaceClass.getName();
String interfaceSimpleName=interfaceClass.getSimpleName();
String enter="\n";
String tab="\t"; //生成代码字符串1
String proxyStr1="import "+interfaceName+";\n"
+ "import com.handler.InvocationHandler;\n"
+ "import java.lang.reflect.Method;"+
enter+"public class Proxy$ implements "+interfaceSimpleName+"{"+
enter+tab+"InvocationHandler handler;"+
enter+tab+"public Proxy$("+handler.getClass().getName()+" handler){"+
enter+tab+tab+"this.handler=handler;"+
enter+tab+"}"; //生成代码字符串2
String proxyStr2="";
//获得所接口的所有方法
Method me[]=interfaceClass.getMethods();
for (Method method : me) { String paramStr=""; //形参字符串
String paramName=""; //参数名字字符串
String paramClass="";//参数类型字符串
int i=1; //得到方法所有参数
Class paramTypeClass[]=method.getParameterTypes();
for (Class class1 : paramTypeClass) { //遍历所有的参数
paramStr+=class1.getSimpleName()+" args"+i+","; //动态得到形参
paramName+="args"+i+","; //动态创建各个参数名字
paramClass+=class1.getSimpleName()+".class,"; //动态创建获得“参数类型”的“类对象”字符串
i++;
} //判断该方法是否有参数,有参数的话 length一定会大于0的
if(paramStr.length()>0){
//截取掉最后一个","
paramStr=paramStr.substring(0,paramStr.length()-1);
paramName=paramName.substring(0,paramName.length()-1);
paramClass=","+paramClass.substring(0,paramClass.length()-1);
} //得到返回值类型
String returnType=method.getReturnType().getSimpleName();
proxyStr2+=enter+tab+"public "+returnType+" "+method.getName()+"("+paramStr+") {"+ enter+tab+tab+"try{";
//判断是否有返回值
if(returnType.equals("void")){
//生成获得Method的字符串
proxyStr2+=enter+tab+tab+"Method md ="+interfaceSimpleName+".class.getMethod(\""+method.getName()+"\""+paramClass+");";
//调用传入handler的invoke方法,实现代理功能
proxyStr2+=enter+tab+tab+"handler.invoke(md,new Object[]{"+paramName+"});";
proxyStr2+=enter+tab+tab+"} catch (Exception e) {"+
enter+tab+tab+"e.printStackTrace();"+
enter+tab+tab+"}";
proxyStr2+=enter+tab+"}";
}else{
proxyStr2+=enter+tab+tab+"Method md ="+interfaceSimpleName+".class.getMethod(\""+method.getName()+"\""+paramClass+");";
proxyStr2+=enter+tab+tab+"return ("+returnType+") handler.invoke(md,new Object[]{"+paramName+"});";
proxyStr2+=enter+tab+tab+"} catch (Exception e) {"+
enter+tab+tab+"e.printStackTrace();"+
enter+tab+tab+"}"; //判断返回类型是否为基本数据类型
if( returnType.equals("int")||
returnType.equals("short")||
returnType.equals("long")||
returnType.equals("char")||
returnType.equals("float")||
returnType.equals("double")||
returnType.equals("byte")){
proxyStr2+=enter+tab+tab+"return 0;";
}else if(returnType.equals("boolean")){
proxyStr2+=enter+tab+tab+"return false;";
}else{
proxyStr2+=enter+tab+tab+"return null;";
} proxyStr2+=enter+tab+"}";
} }
proxyStr2+=enter+tab+"}"; //创建proxy$.java文件到项目外的一个目录下
File file=new File("C:\\Users\\Administrator\\AppData\\Local\\Temp\\Proxy$.java");
try {
file.createNewFile();
FileWriter fw=new FileWriter(file);
fw.write(proxyStr1+proxyStr2);//把生成的代码字符串写入到创建的java文件中
fw.flush(); //使用javaCompiler(java编译器)编译刚才创建的java类
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
//文件管理者
StandardJavaFileManager fileMange = compiler.getStandardFileManager(null, null, null);
//获取文件
Iterable it=fileMange.getJavaFileObjects(file.getPath());
//编译任务
CompilationTask t = compiler.getTask(null, fileMange, null, null, null, it);
//进行编译
t.call(); //使用URL得到该java类所在的路径,并放入URL数组中
URL url = new URL("file:/C:\\Users\\Administrator\\AppData\\Local\\Temp\\");
URL[] urls = new URL[]{url}; //通过URLClassLoader(URL类加载器)加载urls里面的类到内存中
URLClassLoader urlClassLoader = new URLClassLoader(urls);
//通过类全名得到类加载器中的代理类的类对象 (这里填写类全名,因为该类没有包名,所以这里的全名就是该java文件名)
Class clazzProxy =urlClassLoader.loadClass("Proxy$"); //因为我们产生的java类不是在我们的项目中,所以,我们不能使用 Class.forName()来得到该类的实例,
//那么我们可以通过“Constructor(构造器)”来得到一个类的实例
@SuppressWarnings("unchecked")
//从代理类中得到一个构造方法,通过构造方法来实例该类
Constructor constructor= clazzProxy.getConstructor(handler.getClass());
Object object =constructor.newInstance(handler);
//返回代理类
return object;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} return null;
} }

三、好啦, 那么到这里我们的jdk动态代理已经模仿完成啦, 我们来测试一下吧

1、我们模仿的动态代理和jdk动态代理,使用时大同小异的哈。我们新建一个日志的代理类吧,让他实现我们的业务逻辑处理接口,并重写invoke方法

package com.dao;
import java.lang.reflect.Method;
import com.handler.InvocationHandler; public class StudentDaoImplTimeProxy implements InvocationHandler{
Object obj;
public StudentDaoImplTimeProxy(Object obj) {
this.obj=obj;
}
@Override
public Object invoke(Method method, Object[] args) {
Object ob=null;
try {
System.out.println("===========开始记录日志===========");
ob=method.invoke(obj,args);
System.out.println("===========结束记录===========");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return ob;
}
}

2、我们代理的目标类

package com.dao;
public class StudentDaoImpl implements StudentDao2 {
@Override
public void query(int i, String str) {
// TODO Auto-generated method stub
System.out.println("########查询数据库#######");
}
@Override
public void add(int i) {
// TODO Auto-generated method stub
System.out.println("########添加数据#######");
}
}

3、实现的接口

package com.dao;

public interface StudentDao {
public int query(int i);
public void add();
}
package com.test;

import java.lang.reflect.Method;

import com.dao.StudentDao;
import com.dao.StudentDao2;
import com.dao.StudentDaoImpl;
import com.dao.StudentDaoImpl1;
import com.dao.StudentDaoImplLogProxy;
import com.dao.StudentDaoImplTimeProxy;
import com.handler.InvocationHandler;
import com.proxy.Proxy; public class Test {
public static void main(String[] args) {
StudentDao sd=new StudentDaoImpl1();
//custom仿jdk动态代理
InvocationHandler h1=new StudentDaoImplTimeProxy(sd);
StudentDao dao1=(StudentDao)Proxy.newProxyInstance(StudentDao.class,h1);
System.out.println(dao1.query(1));
}



高仿JDK动态代理 底层源码实现的更多相关文章

  1. JDK动态代理实现源码分析

    JDK动态代理实现方式 在Spring框架中经典的AOP就是通过动态代理来实现的,Spring分别采用了JDK的动态代理和Cglib动态代理,本文就来分析一下JDK是如何实现动态代理的. 在分析源码之 ...

  2. JAVA设计模式-动态代理(Proxy)源码分析

    在文章:JAVA设计模式-动态代理(Proxy)示例及说明中,为动态代理设计模式举了一个小小的例子,那么这篇文章就来分析一下源码的实现. 一,Proxy.newProxyInstance方法 @Cal ...

  3. jdk动态代理底层实现

    一.代理设计模式 代理设计模式是Java常用的设计模式之一. 特点: 01.委托类和代理类有共同的接口或者父类: 02.代理类负责为委托类处理消息,并将消息转发给委托类: 03.委托类和代理类对象通常 ...

  4. 【SpringCloud原理】万字剖析OpenFeign之FeignClient动态代理生成源码

    年前的时候我发布两篇关于nacos源码的文章,一篇是聊一聊nacos是如何进行服务注册的,另一篇是一文带你看懂nacos是如何整合springcloud -- 注册中心篇.今天就继续接着剖析Sprin ...

  5. 慕课网,vue高仿饿了吗ASP源码视频笔记

    1.源码笔记 我的源码+笔记(很重要):http://pan.baidu.com/s/1geI4i2Z 感谢麦子学院项目相关视频 2.参考资料 Vue.js官网(https://vuejs.org.c ...

  6. Vue高仿网易云网页端源码

      音乐播放器虽然烂大街了,但是作为前端没自己撸一个一直是个遗憾, 而且偶然间发现 pc 端 web 版的网易云音乐做的实在是太简陋了, 社区仿 pc 客户端的网易云也不多见,为了弥补这个遗憾, 就用 ...

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

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

  8. JDK动态代理深入理解分析并手写简易JDK动态代理(下)

    原文同步发表至个人博客[夜月归途] 原文链接:http://www.guitu18.com/se/java/2019-01-05/27.html 作者:夜月归途 出处:http://www.guitu ...

  9. 代理模式(静态代理、JDK动态代理原理分析、CGLIB动态代理)

    代理模式 代理模式是设计模式之一,为一个对象提供一个替身或者占位符以控制对这个对象的访问,它给目标对象提供一个代理对象,由代理对象控制对目标对象的访问. 那么为什么要使用代理模式呢? 1.隔离,客户端 ...

随机推荐

  1. linux动态内核模块编程-3

    将一组与模块相关的命令加载进内核 完成功能类似2,打印proc下的相关信息.但是不用重新编译内核,节省时间,更为灵活 内核模块介绍 模块是在内核空间运行的程序,实际上是一种目标文件,不能单独运行但其代 ...

  2. dp-矩阵连乘

    参考:http://blog.csdn.net/liufeng_king/article/details/8497607 使用备忘录算法复杂度降至O(n^3) #include<stdio.h& ...

  3. 进程与进程之间通信Manager

    #!/usr/bin/env python from multiprocessing import Process,Manager #Manager进程与进程之间通信 def Foo(i,dic): ...

  4. windows下单机版的伪分布式solrCloud环境搭建Tomcat+solr+zookeeper

    原文出自:http://sbp810050504.blog.51cto.com/2799422/1408322           按照该方法,伪分布式solr部署成功                 ...

  5. UWP蓝牙的例子

    https://answers.microsoft.com/zh-hans/windows/forum/windows_10-networking/%e5%9c%a8win10%e7%8e%af%e5 ...

  6. gcc 升级方法

    Want GCC 4.8 with c++11 complete feature? Well here’s how to install it in Ubuntu 12.04, Ubuntu 13.0 ...

  7. 设置 PyCharm 软件中 Terminal 窗口 中启动的 python 交互命令的版本

    设置 PyCharm 软件中 Terminal 窗口 中启动的 python 交互命令的版本 python2 和 python3 有很大的不同,使用python2 编写的程序,如果使用python3 ...

  8. 简单好用的General开发框架

    1.开篇概述 从2004年学习编程,2007年学习C#以来,做的多半都是跟数据库打交道的工作,所以也积累了很多数据库方面的知识,用过一些ORM框架,从了解掌握到自己实现,慢慢积累了很多代码,直到201 ...

  9. 树莓派研究笔记(3)-- 安装VNC

    小屏幕太小了,眼睛快看瞎了,必须安装VNC 才行啊. 更新—2018-02-04 最新版本的系统中自带了VNC了,只需要在 菜单 Preferences -> Raspberry Pi Conf ...

  10. HttpServletResponse和HttpServletRequest详解.RP

    HttpServletResponse,HttpServletRequest详解 1.相关的接口   HttpServletRequest HttpServletRequest接口最常用的方法就是获得 ...