实验的目录结构

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. Android 使用date set命令修改系统时间

    测试环境:android 7.1.1 在adb shell中试图使用 date -s "yyyymmdd.[[[hh]mm]ss]"修改系统系统时间时,会提示 date: Unkn ...

  2. 如何查看MySQL connection id连接id

    每个MySQL连接,都有一个连接ID,可以通过 connection_id()查看. 连接id也可以通过以下方式查看: show processlist中id列 information_schema. ...

  3. System 源码阅读

    System 属性说明 /** * System 类包含了几个有用的字段和方法,并且不能被实例化. * * @author unascribed * @since 1.0 */ public fina ...

  4. linux性能分析之平均负载

    平均负载 1,执行 top 或者 uptime 命令 来了解系统负载 uptime 分析显示 当前时间,系统运行时间,正在登录用户数 平均负载是指单位时间内,系统处于可运行状态和不可中断状态的平均进程 ...

  5. flutter vscode 小插件

    dart flutter Awesome Flutter Snippets Bracket Pair Colorizer

  6. MySQL如何查询某个字段长度最大的记录

    转: MySQL如何查询某个字段长度最大的记录 2017年06月24日 13:12:15 翔云123456 阅读数 18348   版权声明:本文为翔云原创文章,未经博主允许不得转载. https:/ ...

  7. for循环使用

    cat > a.sh <<EOF #!/bin/bash export NODE_NAMES=(kube-test1 kube-test2 kube-test3 kube-test4 ...

  8. (C#)Appium自动化测试之卸载\重装APP

    1.先获取session,实例化driver 2.自动安装APP //安装driver.InstallApp("APP的路径"); //判断是否安装完成,返回true\false ...

  9. 重学Python - Day 07 - python基础 -> linux命令行学习 -- 常用命令 一

    常用命令和使用方法如下: man man 命令 #可以查询命令的用法 cat 和 tac cat是正序显示文件内容 tac是倒叙显示文件内容 sort 对文件内容排序 uniq 忽略文件中重复行 hi ...

  10. ctype.h头文件

    定义了一批C语言字符分类函数(C character classification functions),用于测试字符是否属于特定的字符类别,如字母字符.控制字符等等.既支持单字节(Byte)字符,也 ...