实验的目录结构

1、JDK动态代理

先来一段jdk动态代理的demo,
首先创建一个接口,Person

public interface Person {

    public void eat();
}

实现类PersonImpl

public class PersonImpl implements Person {

    @Override
public void eat() {
System.out.println("吃午饭");
} }

调用处理器类PersonInvocationHandler

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method; public class PersonInvocationHandler implements InvocationHandler { private Object obj; public PersonInvocationHandler(Object obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("吃早饭");
method.invoke(obj, args);
System.out.println("吃晚饭");
return null;
} }

测试类

public class JdkTest {

    public static void main(String[] args) throws Exception {
PersonInvocationHandler personInvocationHandler = new PersonInvocationHandler(new PersonImpl()); //利用JDK的Proxy类的newProxyInstance方法创建代理对象(代理类),该方法需要三个参数:1)目标类 的 类加载器;2)目标类 所实现的所有接口; 3)重写了invoke方法的InvocationHandler类)
Person personProxy = (Person) Proxy.newProxyInstance(
PersonImpl.class.getClassLoader(),
PersonImpl.class.getInterfaces(),
personInvocationHandler); personProxy.eat();
}

测试结果

2、自定义动态代理

针对(1),我们有如下代码,先抄袭JDK的InvocationHandler,改个名字成为MyInvocationHandler

package custom;

import java.lang.reflect.Method;

public interface MyInvocationHandler {

    public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}

编写一个JAVA类MyPersonInvocationHandler继承MyInvocationHandler,这段代码与PersonInvocationHandler的代码无异,如下所示

package custom;

import java.lang.reflect.Method;

public class MyPersonInvocationHandler implements MyInvocationHandler {

    private Object obj;

    public MyPersonInvocationHandler(Object obj) {
this.obj = obj;
} @Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("吃早饭");
method.invoke(obj, args);//new的被代理类实例,传来使用。
System.out.println("吃晚饭");
return null;
} }

针对(2),我们实现一个自己的代理生成类MyProxy,其生成java代理类的步骤分为以下5步

  1. 生成java源碼
  2. 將源码输出到java文件中
  3. 将java文件编译成class文件
  4. 将class加载进jvm
  5. 返回代理类对象

分析说明:

Proxy的newProxyInstance()方法,需要三个参数(类加载器,被代理类实现的所有接口,调用处理器类),返回值为代理类的实例对象,它的工作内容是:

1)生成代理类的java源碼:起个类名并实现所有的接口(传入的第二个参数);定义个有参构造器,调用处理器类作为参数(传入的第三个参数);代理类的eat()方法, 拿到上述的所有接口中的方法的实例对象;运行调用处理类的invoke()方法()。

(所以,我们对这个代理类实例化后,运行eat()方法时,是调用了调用处理器类(MyPersonInvocationHandler)的invoke()方法。这个invoke()方法就是增强功能后的方法,从上面的调用处理器类可以看到,invoke方法里包括增强的功能和method(从代理类传来的接口中方法类的实例)的invoke方法),方法实例的invoke方法里有两个参数,一个是对象实例(即得知道是哪个对象的这个方法,很明显是被代理类的实例),一个是这个方法的参数是什么,说了这么多其实就是增强的功能加原理被代理类的功能。

2)把1)中生成的java代码写到$Proxy0.java文件中;

3)把2)中生成的java文件编译为.calss文件;

4)用自定义的类加载器把.calss文件加载到jvm得到Class对象;

5)对Class对象实例化得到代理类对象返回。

生成的代理类 ,$Proxy0.java

package bean;

import java.lang.reflect.Method;

public class $Proxy0 implements bean.Person {
private MyInvocationHandler h; public $Proxy0(MyInvocationHandler h) {
this.h = h;
} public void eat() {
try {
Method m = bean.Person.class.getMethod("eat", new Class[] {});//这里拿到了接口中的方法的实例对象,但是是空的
this.h.invoke(this, m, null);//把空的方法的实例对象传递到调用处理器的invoke方法中填充允许。
} catch (Throwable e) {
e.printStackTrace();
}
}
}

具体代码如下

public class MyProxy {

    public static final String ln = "\r\n";

    public static Object newProxyInstance(MyClassLoader myClassLoder,
Class<?>[] interfaces, MyInvocationHandler h) {
try{
// 1 java源碼
String src = generateSrc(interfaces);
//System.out.println("java源码:" + src); // 2 將源码输出到java文件中
String filePath = MyProxy.class.getResource("").getPath();
System.out.println(filePath);
File f = new File(filePath + "$Proxy0.java");
FileWriter fw = new FileWriter(f);
fw.write(src);
fw.flush();
fw.close(); //3、将java文件编译成class文件
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager manage = compiler.getStandardFileManager(null,null,null);
Iterable iterable = manage.getJavaFileObjects(f); JavaCompiler.CompilationTask task = compiler.getTask(null,manage,null,null,null,iterable);
task.call();
manage.close(); //4、将class加载进jvm
Class proxyClass=myClassLoder.findClass("$Proxy0");
f.delete(); //5、返回代理类对象
Constructor constructor = proxyClass.getConstructor(MyInvocationHandler.class);
return constructor.newInstance(h);
}catch(Exception e){
e.printStackTrace();
}
return null;
} private static String generateSrc(Class<?>[] interfaces) {
// TODO Auto-generated method stub
StringBuffer sb = new StringBuffer();
sb.append("package custom;" + ln);
sb.append("import java.lang.reflect.Method;" + ln);
sb.append("public class $Proxy0 implements " + interfaces[0].getName() + "{" + ln);
sb.append("private MyInvocationHandler h;"+ln);
sb.append("public $Proxy0(MyInvocationHandler h) { " + ln);
sb.append("this.h = h;"+ln);
sb.append("}" + ln);
for (Method m : interfaces[0].getMethods()) {
sb.append("public " + m.getReturnType().getName() + " "
+ m.getName() + "() {" + ln);
sb.append("try{" + ln);
sb.append("Method m = " + interfaces[0].getName()
+ ".class.getMethod(\"" + m.getName()
+ "\",new Class[]{});" + ln);
sb.append("this.h.invoke(this,m,null);" + ln);
sb.append("}catch(Throwable e){" + ln);
sb.append("e.printStackTrace();" + ln);
sb.append("}"+ln);
sb.append("}"+ln);
}
sb.append("}" + ln);
return sb.toString();
}
}

针对(3),我们继承ClassLoader,实现一套自己的类加载机制MyClassLoader,如下所示,

其实自定义类加载器非常容易,自定义一个类继承ClassLoader类,然后重写它的findClass()方法。

findClass()方法里做两件事:

1)读入 xx.class文件,并放到字节数组中 byte[ ] ;

2)调用defineClass()函数,传入这个Class的名字(即.class的文件名)、字节数组、数组长度,这几个参数,就可以把这二进制的class文件,转换为jvm里的Class对象。

 public class MyClassLoader extends ClassLoader {

     private File classPathfile;

     public MyClassLoader() {
String classpth = MyClassLoader.class.getResource("").getPath();
classPathfile = new File(classpth);
} @Override
public Class<?> findClass(String name) throws ClassNotFoundException {
String className = MyClassLoader.class.getPackage().getName() + "." +name;
if (classPathfile != null) {
File file = new File(classPathfile, name + ".class");
FileInputStream fileInputStream = null;
ByteArrayOutputStream outputStream = null;
try{
fileInputStream = new FileInputStream(file);
outputStream = new ByteArrayOutputStream();
byte[] buff = new byte[1024];
int len;
while((len=fileInputStream.read(buff))!=-1){
outputStream.write(buff, 0, len);
}
return defineClass(className, outputStream.toByteArray(), 0, outputStream.size());
}catch(Exception e){
e.printStackTrace();
}finally{
if(null!=fileInputStream){
try {
fileInputStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(null!=outputStream){
try {
outputStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
return null;
} }

最后测试类代码如下所示

public class CustomTest {

    public static void main(String[] args) {
MyPersonInvocationHandler personInvocationHandler = new MyPersonInvocationHandler(
new PersonImpl());//生成代理类实例传入调用处理器

     //MyProxy的newProxyInstanc方法中的工作,根据第二和第三个参数生成代理类的java代码(具体代码看上面),写入文件xx.java,编译生成.calss,
     //用第一个参数(类加载器),加载.class得到Class对象,然后new得到代理类实例对象返回。
Person personProxy = (Person) MyProxy.newProxyInstance(
new MyClassLoader(), PersonImpl.class.getInterfaces(),
personInvocationHandler);
personProxy.eat();
}
}

结果

3,invoke小实验

public class A {

    public void say(String name){
System.out.println("Hello, " + name);
}
}
import java.lang.reflect.Method;

public class InvokeTest {

    public static void main(String[] args) throws Exception {
Class<A> clz = A.class;//得到A的Class实例
Object o = clz.newInstance();//得到A的实例对象
Method m = clz.getMethod("say", String.class);//根据方法名和方法的参数类型得到方法实例
for (int i = 0; i < 16; i++) {
m.invoke(o, Integer.toString(i));//运行方法实例,传入实例对象(运行哪个对象的这个方法)和方法的参数
}
}
}

运行结果

可以看到方法实例的invoke方法(对象,方法入参),就是运行哪个对象的这个方法。

http://www.cnblogs.com/rjzheng/p/8798556.html

自己实现JDK动态代理的更多相关文章

  1. JDK动态代理

    一.基本概念 1.什么是代理? 在阐述JDK动态代理之前,我们很有必要先来弄明白代理的概念.代理这个词本身并不是计算机专用术语,它是生活中一个常用的概念.这里引用维基百科上的一句话对代理进行定义: A ...

  2. 静态代理和利用反射形成的动态代理(JDK动态代理)

    代理模式 代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问.在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用. 静态代理 1.新建 ...

  3. Spring中的JDK动态代理

    Spring中的JDK动态代理 在JDK1.3以后提供了动态代理的技术,允许开发者在运行期创建接口的代理实例.在Sun刚推出动态代理时,还很难想象它有多大的实际用途,现在动态代理是实现AOP的绝好底层 ...

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

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

  5. JDK动态代理的实现原理

    学习JDK动态代理,从源码层次来理解其实现原理参考:http://blog.csdn.net/jiankunking/article/details/52143504

  6. Java中的JDK动态代理

    所谓代理,其实就是相当于一个中间人,当客户端需要服务端的服务时,不是客户直接去找服务,而是客户先去找代理,告诉代理需要什么服务,然后代理再去服务端找服务,最后将结果返回给客户. 在日常生活中,就拿买火 ...

  7. JDK动态代理与CGLib动态代理

    1.JDK动态代理 JDK1.3以后java提供了动态代理技术,允许开发者在运行期创建接口的代理实例,动态代理是实现AOP的绝好底层技术. JDK的动态代理主要涉及到java.lang.reflect ...

  8. jdk动态代理实现

    1.jdk动态代理的简单实现类 package com.proxy; import java.lang.reflect.InvocationHandler; import java.lang.refl ...

  9. java jdk动态代理

    在面试的时候面试题里有一道jdk的动态代理是原理,并给一个事例直接写代码出来,现在再整理一下 jdk动态代理主要是想动态在代码中增加一些功能,不影响现有代码,实现动态代理需要做如下几个操作 1.首先必 ...

  10. JDK动态代理的实现及原理

    Proxy.newProxyInstance(classloader,Class,invocationHandler) 调用getProxyClass0(loader, interfaces)生成代理 ...

随机推荐

  1. LC 833. Find And Replace in String

    To some string S, we will perform some replacement operations that replace groups of letters with ne ...

  2. Java 谷歌浏览器开发必备插件

    1.谷歌访问助手 下载网址:http://www.ggfwzs.com/ 2.Json Viewer 格式化请求接口,返回Json数据格式,可以在浏览器展示 3.Restlet client 一种类似 ...

  3. 搭建Git服务器及本机克隆提交

    前文 Git是什么? Git是目前世界上最先进的分布式版本控制系统. SVN与Git的最主要的区别? SVN是集中式版本控制系统,版本库是集中放在中央服务器的,而干活的时候,用的都是自己的电脑,所以首 ...

  4. java的几种上传文件方法

    这时:commonsmultipartresolver 的源码,可以研究一下 http://www.verysource.com/code/2337329_1/commonsmultipartreso ...

  5. java+实现文件的上传和下载

    项目截图 路径配置 将localhost:8080/up6全部替换为实际项目路径 使用mysql文件夹下的脚本创建数据表. 创建后可以在数据库中看到创建好的数据表 2.修改数据库连接 3.修改上传地址 ...

  6. Python爬虫学习==>第十一章:分析Ajax请求-抓取今日头条信息

    学习目的: 解决AJAX请求的爬虫,网页解析库的学习,MongoDB的简单应用 正式步骤 Step1:流程分析 抓取单页内容:利用requests请求目标站点,得到单个页面的html代码,返回结果: ...

  7. 论文翻译:LP-3DCNN: Unveiling Local Phase in 3D Convolutional Neural Networks

    引言 传统的3D卷积神经网络(CNN)计算成本高,内存密集,容易过度拟合,最重要的是,需要改进其特征学习能力.为了解决这些问题,我们提出了整流局部相位体积(ReLPV)模块,它是标准3D卷积层的有效替 ...

  8. eclipse 建立 web fragment project 工程

    1.鼠标右键---------new ---------Other 2.选择WEB----web fragment project 3.输入工程名,点finish

  9. [转帖]linux 下yum使用技巧

    linux 下yum使用技巧 https://www.cnblogs.com/galengao/p/5750389.html 本文来自我的github pages博客http://galengao.g ...

  10. python函数 -- 作用域,异常处理

    1.def语句和参数 python定义函数的关键词为def,格式如下: def 函数名([变元],[变元],....)          #保存在变元中的值,在函数返回后该变元就会被销毁了. 2.返回 ...