实验的目录结构

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. layui表格遇到的小操作

    表头文字显示不全 done:function(res){ tdTitle() }, /*表头文字显示不全*/ function tdTitle(){ $('th').each(function(ind ...

  2. 多线程分配线程的实现方案:CountDownLatch类

    需求:假如我们本地有4个文件需要解析,每个文件的内容为20万行.为了提高效率我们要创建4个线程进行处理.等4个线程处理完,要在文件日志表中记录处理状态. 一般的的解决方法是使用join,join用于让 ...

  3. OpenStack Blazar 架构解析与功能实践

    目录 文章目录 目录 Blazar Blazar 的安装部署 Blazar 的软件架构 Blazar 的资源模型与状态机 Blazar 的主机资源预留功能(Host Reservation) 代码实现 ...

  4. 使用构造器模式动态构建Map作为mybatis的查询条件

    import com.alibaba.fastjson.JSON; import com.fasterxml.jackson.databind.ObjectMapper; import com.fas ...

  5. k8s设置集群角色

    查看所有的node节点 [root@test1 ~]# kubectl get nodes NAME STATUS ROLES AGE VERSION test1 Ready <none> ...

  6. phpfpm和nginx设置开机自动启动

    ## 添加执行权限 chmod a+x /etc/init.d/nginx chmod a+x /etc/init.d/php-fpm ## 加入服务 chkconfig --add nginx ch ...

  7. Element Children

    The childNodes property contains all of the immediate children of the element. There is a significan ...

  8. redis缓存服务器

    1.什么是redis? Redis 是一个基于内存的高性能key-value数据库. 2.使用redis的好处? 速度快,因为数据存在内存,类似hashmap,hashmap的优势就是查找和操作的时间 ...

  9. PI膜概述

    一.概述 1.简述 聚酞亚胺薄膜又称PI薄膜(polyimide filin)是一种含有酞亚胺或丁二酞亚胺的绝缘类高分子材料.是目前工程塑料中耐热性最好的品种之一. 2.发展简史 1908年,PI聚合 ...

  10. C++学习笔记-C++与C语言的一些区别

    本文主要是整理一些C++与C的一些小的区别,也就是在使用C与C++时候需要注意的一些问题,C++是以C语言为基础的,并且完全兼容C语言的特性 注释 C语言的注释形式为 /* 注释内容 */ 而C++提 ...