这样和妻子解释:Java动态代理机制详解(JDK和CGLIB( 二 )

","()V");mv.visitInsn(Opcodes.RETURN);mv.visitMaxs(1, 1);mv.visitEnd();// 定义code方法MethodVisitor methodVisitor = classWriter.visitMethod(Opcodes.ACC_PUBLIC, "code", "()V",null, null);methodVisitor.visitCode();methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out","Ljava/io/PrintStream;");methodVisitor.visitLdcInsn("I'm a Programmer,Just Coding.....");methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println","(Ljava/lang/String;)V");methodVisitor.visitInsn(Opcodes.RETURN);methodVisitor.visitMaxs(2, 2);methodVisitor.visitEnd();classWriter.visitEnd();// 使classWriter类已经完成// 将classWriter转换成字节数组写到文件里面去byte[] data = http://kandian.youth.cn/index/classWriter.toByteArray();File file = new File("D://Programmer.class");FileOutputStream fout = new FileOutputStream(file);fout.write(data);fout.close();}}上述的代码执行过后 , 用Java反编译工具(如JD_GUI)打开D盘下生成的Programmer.class , 可以看到以下信息:
这样和妻子解释:Java动态代理机制详解(JDK和CGLIB文章插图
再用上面我们定义的类加载器将这个class文件加载到内存中 , 然后 创建class对象 , 并且实例化一个对象 , 调用code方法 , 会看到下面的结果:
这样和妻子解释:Java动态代理机制详解(JDK和CGLIB文章插图
以上表明:在代码里生成字节码 , 并动态地加载成class对象、创建实例是完全可以实现的 。
Java字节码生成开源框架介绍--Javassist:
Javassist是一个开源的分析、编辑和创建Java字节码的类库 。 是由东京工业大学的数学和计算机科学系的 Shigeru Chiba (千叶 滋)所创建的 。 它已加入了开放源代码JBoss 应用服务器项目,通过使用Javassist对字节码操作为JBoss实现动态AOP框架 。 javassist是jboss的一个子项目 , 其主要的优点 , 在于简单 , 而且快速 。 直接使用java编码的形式 , 而不需要了解虚拟机指令 , 就能动态改变类的结构 , 或者动态生成类 。
下面通过Javassist创建上述的Programmer类:
import javassist.ClassPool;import javassist.CtClass;import javassist.CtMethod;import javassist.CtNewMethod;public class MyGenerator {public static void main(String[] args) throws Exception {ClassPool pool = ClassPool.getDefault();//创建Programmer类CtClass cc= pool.makeClass("com.samples.Programmer");//定义code方法CtMethod method = CtNewMethod.make("public void code(){}", cc);//插入方法代码method.insertBefore("System.out.println(\"I'm a Programmer,Just Coding.....\");");cc.addMethod(method);//保存生成的字节码cc.writeFile("d://temp");}}通过JD-gui反编译工具打开Programmer.class 可以看到以下代码:
这样和妻子解释:Java动态代理机制详解(JDK和CGLIB文章插图
代理的基本构成:代理模式上 , 基本上有Subject角色 , RealSubject角色 , Proxy角色 。 其中:Subject角色负责定义RealSubject和Proxy角色应该实现的接口;RealSubject角色用来真正完成业务服务功能;Proxy角色负责将自身的Request请求 , 调用realsubject 对应的request功能来实现业务功能 , 自己不真正做业务 。
这样和妻子解释:Java动态代理机制详解(JDK和CGLIB文章插图
上面的这幅代理结构图是典型的静态的代理模式:
当在代码阶段规定这种代理关系 , Proxy类通过编译器编译成class文件 , 当系统运行时 , 此class已经存在了 。 这种静态的代理模式固然在访问无法访问的资源 , 增强现有的接口业务功能方面有很大的优点 , 但是大量使用这种静态代理 , 会使我们系统内的类的规模增大 , 并且不易维护;并且由于Proxy和RealSubject的功能 本质上是相同的 , Proxy只是起到了中介的作用 , 这种代理在系统中的存在 , 导致系统结构比较臃肿和松散 。
为了解决这个问题 , 就有了动态地创建Proxy的想法:在运行状态中 , 需要代理的地方 , 根据Subject 和RealSubject , 动态地创建一个Proxy , 用完之后 , 就会销毁 , 这样就可以避免了Proxy 角色的class在系统中冗杂的问题了 。
下面以一个代理模式实例阐述这一问题:
将车站的售票服务抽象出一个接口TicketService,包含问询 , 卖票 , 退票功能 , 车站类Station实现了TicketService接口 , 车票代售点StationProxy则实现了代理角色的功能 , 类图如下所示 。
这样和妻子解释:Java动态代理机制详解(JDK和CGLIB文章插图
对应的静态的代理模式代码如下所示:
package com.foo.proxy;/*** 售票服务接口实现类 , 车站* @author louluan*/public class Station implements TicketService {@Overridepublic void sellTicket() {System.out.println("\n\t售票.....\n");}@Overridepublic void inquire() {System.out.println("\n\t问询 。。。。 \n");}@Overridepublic void withdraw() {System.out.println("\n\t退票......\n");}}