起名Java动态代理体制详解(JDK 和CGLIB,Javassist,ASM)

class文件简介及加载

  Java编写翻译器编写翻译好Java文件之后,发生.class
文件在磁盘中。那种class文件是2进制文件,内容是唯有JVM虚拟机能够分辨的机器码。JVM虚拟机读取字节码文件,取出二进制数据,加载到内存中,解析.class
文件内的音信,生成对应的 Class对象:

起名 1

class字节码文件是依照JVM虚拟机规范中明显的字节码协会规则变化的、具体class文件是何许协会类音信的,能够参考
此博文:深深精晓Java
Class
文件格式类别。或许是Java虚拟机规范

     上面通过1段代码演示手动加载
class文件字节码到系统内,转换来class对象,然后再实例化的经过:

a. 定义3个 Programmer类:

package samples;  
/** 
 * 程序猿类 
 * @author louluan 
 */  
public class Programmer {  
  
    public void code()  
    {  
        System.out.println("I'm a Programmer,Just Coding.....");  
    }  
}  

  b. 自定义1个类加载器:

package samples;  
/** 
 * 自定义一个类加载器,用于将字节码转换为class对象 
 * @author louluan 
 */  
public class MyClassLoader extends ClassLoader {  
  
    public Class<?> defineMyClass( byte[] b, int off, int len)   
    {  
        return super.defineClass(b, off, len);  
    }  
      
}  

c.
然后编写翻译成Programmer.class文件,在先后中读取字节码,然后转换到相应的class对象,再实例化:

package samples;  
  
import java.io.File;  
import java.io.FileInputStream;  
import java.io.FileNotFoundException;  
import java.io.IOException;  
import java.io.InputStream;  
import java.net.URL;  
  
public class MyTest {  
  
    public static void main(String[] args) throws IOException {  
        //读取本地的class文件内的字节码,转换成字节码数组  
        File file = new File(".");  
        InputStream  input = new FileInputStream(file.getCanonicalPath()+"\\bin\\samples\\Programmer.class");  
        byte[] result = new byte[1024];  
          
        int count = input.read(result);  
        // 使用自定义的类加载器将 byte字节码数组转换为对应的class对象  
        MyClassLoader loader = new MyClassLoader();  
        Class clazz = loader.defineMyClass( result, 0, count);  
        //测试加载是否成功,打印class 对象的名称  
        System.out.println(clazz.getCanonicalName());  
                  
               //实例化一个Programmer对象  
               Object o= clazz.newInstance();  
               try {  
                   //调用Programmer的code方法  
                    clazz.getMethod("code", null).invoke(o, null);  
                   } catch (IllegalArgumentException | InvocationTargetException  
                        | NoSuchMethodException | SecurityException e) {  
                     e.printStackTrace();  
                  }  
    }  
}  

以上代码演示了,通过字节码加载成class
对象的能力,上面看一下在代码中怎么着生成class文件的字节码。

在运转期的代码中生成2进制字节码

  由于JVM通过字节码的二进制新闻加载类的,那么,若是大家在运营期系统中,服从Java编写翻译系统组织.class文件的格式和结构,生成对应的二进制数据,然后再把这几个二进制数据加载转换到对应的类,那样,就完了了在代码中,动态创建贰个类的能力了。

起名 2

在运行时代可以遵守Java虚拟机规范对class文件的协会规则变化对应的贰进制字节码。当前有众多开源框架能够成功那个职能,如ASM,Javassist。

Java字节码生成开源框架介绍–ASM:

  ASM 是七个 Java
字节码操控框架。它亦能够2进制方式修改已有类或然动态生成类。ASM
能够直接发生贰进制 class 文件,也得以在类被加载入 Java
虚拟机此前动态改变类行为。ASM
从类文件中读入新闻后,能够转移类表现,分析类消息,甚至能够依照用户需要转变新类。

只是ASM在成立class字节码的进程中,操纵的级别是底层JVM的汇编指令级别,那须要ASM使用者要对class协会结构和JVM汇编指令有肯定的问询。

上边通过ASM 生成上面类Programmer的class字节码:

package com.samples;  
import java.io.PrintStream;  
  
public class Programmer {  
  
    public void code()  
    {  
        System.out.println("I'm a Programmer,Just Coding.....");  
    }  
}  

行使ASM框架提供了ClassWriter
接口,通过访问者情势进行动态创制class字节码,看上面包车型地铁例子:

package samples;  
  
import java.io.File;  
import java.io.FileOutputStream;  
import java.io.IOException;  
  
import org.objectweb.asm.ClassWriter;  
import org.objectweb.asm.MethodVisitor;  
import org.objectweb.asm.Opcodes;  
public class MyGenerator {  
  
    public static void main(String[] args) throws IOException {  
  
        System.out.println();  
        ClassWriter classWriter = new ClassWriter(0);  
        // 通过visit方法确定类的头部信息  
        classWriter.visit(Opcodes.V1_7,// java版本  
                Opcodes.ACC_PUBLIC,// 类修饰符  
                "Programmer", // 类的全限定名  
                null, "java/lang/Object", null);  
          
        //创建构造函数  
        MethodVisitor mv = classWriter.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);  
        mv.visitCode();  
        mv.visitVarInsn(Opcodes.ALOAD, 0);  
        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>","()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 = 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,能够看到以下音信:

起名 3

再用地方大家定义的类加载器将以此class文件加载到内部存款和储蓄器中,然后
创制class对象,并且实例化三个对象,调用code方法,会看出下面包车型客车结果:

起名 4

如上评释:在代码里生成字节码,并动态地加载成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 能够看看以下代码:

起名 5

老是只导入一条记下未免太麻烦了些,由于许多参考文献消息都是纯文本格式的,那么我们得以将洋洋条记下分段拷贝到xxx.bib里,然后集中程导弹入。

代办的中坚组成:

  代理情势上,基本上有Subject剧中人物,RealSubject剧中人物,Proxy剧中人物。在这之中:Subject剧中人物负责定义RealSubject和Proxy角色应该完成的接口;RealSubject剧中人物用来真正做到工作服务功能;Proxy剧中人物负责将自个儿的Request请求,调用realsubject
对应的request功效来兑现工作功效,本身不确实做业务。

起名 6

下面的那幅代理结构图是百里挑一的静态的代理形式:

      
当在代码阶段规定那种代理关系,Proxy类通过编译器编写翻译成class文件,当系统运行时,此class已经存在了。那种静态的代办情势即使在拜访不能访问的能源,增强现有的接口业务成效方面有十分大的独到之处,不过大批量施用那种静态代理,会使大家系统内的类的规模增大,并且不易维护;并且鉴于Proxy和RealSubject的功力
本质上是一律的,Proxy只是起到了中介的遵循,这种代理在系统中的存在,导致系统结构相比较臃肿和麻痹。

  为了消除这些标题,就有了动态地创制Proxy的想法:在运作状态中,须求代理的地方,依据Subject
和RealSubject,动态地开创一个Proxy,用完之后,就会销毁,那样就足以制止了Proxy
角色的class在系统中混杂的题材了。

上面以一个代理格局实例解说这一标题:

  
将车站的订票服务抽象出3个接口TicketService,包蕴问询,卖票,退票效能,车站类Station实现了TicketService接口,车票代售点StationProxy则达成了代办剧中人物的功效,类图如下所示。

起名 7

对应的静态的代办形式代码如下所示:

package com.foo.proxy;  
  
/** 
 * 售票服务接口实现类,车站 
 * @author louluan 
 */  
public class Station implements TicketService {  
  
    @Override  
    public void sellTicket() {  
        System.out.println("\n\t售票.....\n");  
    }  
  
    @Override  
    public void inquire() {  
        System.out.println("\n\t问询。。。。\n");  
    }  
  
    @Override  
    public void withdraw() {  
        System.out.println("\n\t退票......\n");  
    }  
  
}  

 

package com.foo.proxy;  
/** 
 * 售票服务接口  
 * @author louluan 
 */  
public interface TicketService {  
  
    //售票  
    public void sellTicket();  
      
    //问询  
    public void inquire();  
      
    //退票  
    public void withdraw();  
      
}  

 

package com.foo.proxy;  
  
/** 
 * 车票代售点 
 * @author louluan 
 * 
 */  
public class StationProxy implements TicketService {  
  
    private Station station;  
  
    public StationProxy(Station station){  
        this.station = station;  
    }  
      
    @Override  
    public void sellTicket() {  
  
        // 1.做真正业务前,提示信息  
        this.showAlertInfo("××××您正在使用车票代售点进行购票,每张票将会收取5元手续费!××××");  
        // 2.调用真实业务逻辑  
        station.sellTicket();  
        // 3.后处理  
        this.takeHandlingFee();  
        this.showAlertInfo("××××欢迎您的光临,再见!××××\n");  
  
    }  
  
    @Override  
    public void inquire() {  
        // 1做真正业务前,提示信息  
        this.showAlertInfo("××××欢迎光临本代售点,问询服务不会收取任何费用,本问询信息仅供参考,具体信息以车站真实数据为准!××××");  
        // 2.调用真实逻辑  
        station.inquire();  
        // 3。后处理  
        this.showAlertInfo("××××欢迎您的光临,再见!××××\n");  
    }  
  
    @Override  
    public void withdraw() {  
        // 1。真正业务前处理  
        this.showAlertInfo("××××欢迎光临本代售点,退票除了扣除票额的20%外,本代理处额外加收2元手续费!××××");  
        // 2.调用真正业务逻辑  
        station.withdraw();  
        // 3.后处理  
        this.takeHandlingFee();  
  
    }  
  
    /* 
     * 展示额外信息 
     */  
    private void showAlertInfo(String info) {  
        System.out.println(info);  
    }  
  
    /* 
     * 收取手续费 
     */  
    private void takeHandlingFee() {  
        System.out.println("收取手续费,打印发票。。。。。\n");  
    }  
  
}  

是因为大家以往不期待静态地有StationProxy类存在,希望在代码中,动态生成器贰进制代码,加载进来。为此,使用Javassist开源框架,在代码中动态地生成StationProxy的字节码:

package com.foo.proxy;  
  
  
import java.lang.reflect.Constructor;  
  
import javassist.*;  
public class Test {  
  
    public static void main(String[] args) throws Exception {  
       createProxy();  
    }  
      
    /* 
     * 手动创建字节码 
     */  
    private static void createProxy() throws Exception  
    {  
        ClassPool pool = ClassPool.getDefault();  
  
        CtClass cc = pool.makeClass("com.foo.proxy.StationProxy");  
          
        //设置接口  
        CtClass interface1 = pool.get("com.foo.proxy.TicketService");  
        cc.setInterfaces(new CtClass[]{interface1});  
          
        //设置Field  
        CtField field = CtField.make("private com.foo.proxy.Station station;", cc);  
          
        cc.addField(field);  
          
        CtClass stationClass = pool.get("com.foo.proxy.Station");  
        CtClass[] arrays = new CtClass[]{stationClass};  
        CtConstructor ctc = CtNewConstructor.make(arrays,null,CtNewConstructor.PASS_NONE,null,null, cc);  
        //设置构造函数内部信息  
        ctc.setBody("{this.station=$1;}");  
        cc.addConstructor(ctc);  
  
        //创建收取手续 takeHandlingFee方法  
        CtMethod takeHandlingFee = CtMethod.make("private void takeHandlingFee() {}", cc);  
        takeHandlingFee.setBody("System.out.println(\"收取手续费,打印发票。。。。。\");");  
        cc.addMethod(takeHandlingFee);  
          
        //创建showAlertInfo 方法  
        CtMethod showInfo = CtMethod.make("private void showAlertInfo(String info) {}", cc);  
        showInfo.setBody("System.out.println($1);");  
        cc.addMethod(showInfo);  
          
        //sellTicket  
        CtMethod sellTicket = CtMethod.make("public void sellTicket(){}", cc);  
        sellTicket.setBody("{this.showAlertInfo(\"××××您正在使用车票代售点进行购票,每张票将会收取5元手续费!××××\");"  
                + "station.sellTicket();"  
                + "this.takeHandlingFee();"  
                + "this.showAlertInfo(\"××××欢迎您的光临,再见!××××\");}");  
        cc.addMethod(sellTicket);  
          
        //添加inquire方法  
        CtMethod inquire = CtMethod.make("public void inquire() {}", cc);  
        inquire.setBody("{this.showAlertInfo(\"××××欢迎光临本代售点,问询服务不会收取任何费用,本问询信息仅供参考,具体信息以车站真实数据为准!××××\");"  
        + "station.inquire();"  
        + "this.showAlertInfo(\"××××欢迎您的光临,再见!××××\");}"  
        );  
        cc.addMethod(inquire);  
          
        //添加widthraw方法  
        CtMethod withdraw = CtMethod.make("public void withdraw() {}", cc);  
        withdraw.setBody("{this.showAlertInfo(\"××××欢迎光临本代售点,退票除了扣除票额的20%外,本代理处额外加收2元手续费!××××\");"  
                + "station.withdraw();"  
                + "this.takeHandlingFee();}"  
                );  
        cc.addMethod(withdraw);  
          
        //获取动态生成的class  
        Class c = cc.toClass();  
        //获取构造器  
        Constructor constructor= c.getConstructor(Station.class);  
        //通过构造器实例化  
        TicketService o = (TicketService)constructor.newInstance(new Station());  
        o.inquire();  
          
        cc.writeFile("D://test");  
    }  
      
}  

上述代码执行过后,会爆发StationProxy的字节码,并且用生成字节码加载如内部存款和储蓄器创建对象,调用inquire()方法,会取得以下结果:

起名 8

  通过地点动态变化的代码,我们发现,其落到实处优良地劳动在开立的进度中,含有太多的业务代码。大家使用上述创设Proxy代理类的主意的初衷是压缩系统代码的冗杂度,可是上述做法却高居不下了在动态创立代理类进程中的复杂度:手动地创立了太多的政工代码,并且封装性也不够,完全不有所可拓展性和通用性。假使有些代理类的1些政工逻辑卓殊复杂,上述的动态创制代理的方式是尤其不可取的!

InvocationHandler角色的原委

  仔细思忖代理情势中的代理Proxy剧中人物。Proxy剧中人物在履行代理工科作的时候,无非是在调用真正业务此前仍旧未来做一些“额外”业务。

起名 9

  有上海教室可以看看,代理类处理的逻辑非常粗略:在调用有个别方法前及方法后做壹些格外的工作。换壹种思路正是:在触及(invoke)真实剧中人物的艺术此前照旧现在做壹些附加的业务。那么,为了协会出富有通用性和简单性的代理类,能够将兼具的触发真实角色动作交给三个触及的管理器,让这一个管理器统一地保管触发。那种管理器正是Invocation
Handler。

动态代理方式的构造跟上边的静态代理形式稍微有所不一致,多引进了三个InvocationHandler剧中人物。

先解释一下InvocationHandler的功效:

  在静态代理中,代理Proxy中的方法,都钦命了调用了特定的realSubject中的对应的点子:
  在地方的静态代理形式下,Proxy所做的业务,无非是调用在分歧的request时,调用触发realSubject对应的不2诀窍;更抽象点看,Proxy所作的事情;在Java中
方法(Method)也是当做三个目的来看待了,
  动态代理工科作的基本格局正是将协调的章程效果的落到实处交给
InvocationHandler角色,外界对Proxy剧中人物中的每3个艺术的调用,Proxy剧中人物都会付出InvocationHandler来处理,而InvocationHandler则调用具体目的剧中人物的不二秘籍。如下图所示:

起名 10

在那种情势里面:代理Proxy 和RealSubject
应该实现均等的效应,那或多或少不能缺少。(小编那里说的效益,能够了然为某些类的public方法)

在面向对象的编制程序之中,即使大家想要约定Proxy
和RealSubject能够兑现平等的成效,有两种艺术:

  a.2个相比较直观的点子,就是概念一个功效接口,然后让Proxy
和RealSubject来落到实处那些接口。

     b.还有相比较生硬的艺术,正是经过持续。因为只要Proxy
继承自RealSubject,那样Proxy则有所了RealSubject的效益,

   Proxy还足以因而重写RealSubject中的方法,来贯彻多态。

中间JDK中提供的创始动态代理的体制,是以a 那种思路设计的,而cglib
则是以b思路设计的。

JDK的动态代理创设机制—-通过接口

比近日后想为RealSubject这几个类创造二个动态代理对象,JDK首要会做以下工作:

  壹. 拿走 RealSubject上的兼具接口列表;
  二. 分明要转移的代理类的类名,暗许为:com.sun.proxy.$ProxyXXXX ;
  三. 基于供给贯彻的接口消息,在代码中动态创设 该Proxy类的字节码;
  四 . 将相应的字节码转换为对应的class 对象;
  五. 开立InvocationHandler 实例handler,用来拍卖Proxy全体办法调用;
  陆. Proxy 的class对象 以创制的handler对象为参数,实例化三个proxy对象

JDK通过
java.lang.reflect.Proxy包来补助动态代理,一般意况下,大家应用上边包车型客车newProxyInstance方法

static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
          返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。

而对于InvocationHandler,咱们需求贯彻下列的invoke方法:

在调用代理对象中的每三个措施时,在代码内部,都以直接调用了InvocationHandler
的invoke方法,而invoke方法依据代理类传递给协调的method参数来区分是如何办法。

Object invoke(Object proxy,Method method,Object[] args)
          在代理实例上处理方法调用并返回结果。

讲的某个抽象,下边通过一个实例来演示一下啊:

JDK动态代理示例

  未来定义多个接口Vehicle和Rechargable,Vehicle代表交通工具类,有drive()方法;Rechargable接口表示可充电的(工具),有recharge()
方法;

概念一个兑现两个接口的类ElectricCar,类图如下:

起名 11

通过上面包车型大巴代码片段,来为ElectricCar创造动态代理类:

package com.foo.proxy;  
  
import java.lang.reflect.InvocationHandler;  
import java.lang.reflect.Proxy;  
  
public class Test {  
  
    public static void main(String[] args) {  
  
        ElectricCar car = new ElectricCar();  
        // 1.获取对应的ClassLoader  
        ClassLoader classLoader = car.getClass().getClassLoader();  
  
        // 2.获取ElectricCar 所实现的所有接口  
        Class[] interfaces = car.getClass().getInterfaces();  
        // 3.设置一个来自代理传过来的方法调用请求处理器,处理所有的代理对象上的方法调用  
        InvocationHandler handler = new InvocationHandlerImpl(car);  
        /* 
          4.根据上面提供的信息,创建代理对象 在这个过程中,  
                         a.JDK会通过根据传入的参数信息动态地在内存中创建和.class 文件等同的字节码 
                 b.然后根据相应的字节码转换成对应的class,  
                         c.然后调用newInstance()创建实例 
         */  
        Object o = Proxy.newProxyInstance(classLoader, interfaces, handler);  
        Vehicle vehicle = (Vehicle) o;  
        vehicle.drive();  
        Rechargable rechargeable = (Rechargable) o;  
        rechargeable.recharge();  
    }  
}  

 

package com.foo.proxy;  
/** 
 * 交通工具接口 
 * @author louluan 
 */  
public interface Vehicle {  
    public void drive();  
}  

 

package com.foo.proxy;  
/** 
 * 可充电设备接口 
 * @author louluan 
 */  
public interface Rechargable {  
  
    public void recharge();  
}  

 

package com.foo.proxy;  
/** 
 * 电能车类,实现Rechargable,Vehicle接口 
 * @author louluan 
 */  
public class ElectricCar implements Rechargable, Vehicle {  
  
    @Override  
    public void drive() {  
        System.out.println("Electric Car is Moving silently...");  
    }  
  
    @Override  
    public void recharge() {  
        System.out.println("Electric Car is Recharging...");  
    }  
  
}  

 

package com.foo.proxy;  
  
import java.lang.reflect.InvocationHandler;  
import java.lang.reflect.Method;  
  
public class InvocationHandlerImpl implements InvocationHandler {  
  
    private ElectricCar car;  
      
    public InvocationHandlerImpl(ElectricCar car)  
    {  
        this.car=car;  
    }  
      
    @Override  
    public Object invoke(Object paramObject, Method paramMethod,  
            Object[] paramArrayOfObject) throws Throwable {  
        System.out.println("You are going to invoke "+paramMethod.getName()+" ...");  
        paramMethod.invoke(car, null);  
        System.out.println(paramMethod.getName()+" invocation Has Been finished...");  
        return null;  
    }  
  
}  

来看一下代码执行后的结果:

起名 12

变迁动态代理类的字节码并且保留到硬盘中: 

  JDK提供了sun.misc.ProxyGenerator.generateProxyClass(String
proxyName,class[] interfaces) 底层方法来发出动态代理类的字节码:

上边定义了叁个工具类,用来将转变的动态代理类保存到硬盘中:

package com.foo.proxy;  
  
import java.io.FileOutputStream;  
import java.io.IOException;  
import java.lang.reflect.Proxy;  
import sun.misc.ProxyGenerator;  
  
public class ProxyUtils {  
  
    /* 
     * 将根据类信息 动态生成的二进制字节码保存到硬盘中, 
     * 默认的是clazz目录下 
         * params :clazz 需要生成动态代理类的类 
         * proxyName : 为动态生成的代理类的名称 
         */  
    public static void generateClassFile(Class clazz,String proxyName)  
    {  
        //根据类信息和提供的代理类名称,生成字节码  
                byte[] classFile = ProxyGenerator.generateProxyClass(proxyName, clazz.getInterfaces());   
        String paths = clazz.getResource(".").getPath();  
        System.out.println(paths);  
        FileOutputStream out = null;    
          
        try {  
            //保留到硬盘中  
            out = new FileOutputStream(paths+proxyName+".class");    
            out.write(classFile);    
            out.flush();    
        } catch (Exception e) {    
            e.printStackTrace();    
        } finally {    
            try {    
                out.close();    
            } catch (IOException e) {    
                e.printStackTrace();    
            }    
        }    
    }  
      
}  

明天咱们想将转变的代办类起名称为“ElectricCarProxy”,并保存在硬盘,应该利用以下语句:

ProxyUtils.generateClassFile(car.getClass(), "ElectricCarProxy");

如此将在ElectricCar.class 同级目录下发出
ElectricCarProxy.class文件。用反编写翻译工具如jd-gui.exe
打开,将会合到以下新闻:

import com.foo.proxy.Rechargable;  
import com.foo.proxy.Vehicle;  
import java.lang.reflect.InvocationHandler;  
import java.lang.reflect.Method;  
import java.lang.reflect.Proxy;  
import java.lang.reflect.UndeclaredThrowableException;  
/** 
 生成的动态代理类的组织模式是继承Proxy类,然后实现需要实现代理的类上的所有接口,而在实现的过程中,则是通过将所有的方法都交给了InvocationHandler来处理 
*/  
 public final class ElectricCarProxy extends Proxy  
  implements Rechargable, Vehicle  
{  
  private static Method m1;  
  private static Method m3;  
  private static Method m4;  
  private static Method m0;  
  private static Method m2;  
  
  public ElectricCarProxy(InvocationHandler paramInvocationHandler)  
    throws   
  {  
    super(paramInvocationHandler);  
  }  
  
  public final boolean equals(Object paramObject)  
    throws   
  {  
    try  
    { // 方法功能实现交给InvocationHandler处理  
      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();  
    }  
    catch (Error|RuntimeException localError)  
    {  
      throw localError;  
    }  
    catch (Throwable localThrowable)  
    {  
      throw new UndeclaredThrowableException(localThrowable);  
    }  
  }  
  
  public final void recharge()  
    throws   
  {  
    try  
    {  
  
       // 方法功能实现交给InvocationHandler处理  
  
      this.h.invoke(this, m3, null);  
      return;  
    }  
    catch (Error|RuntimeException localError)  
    {  
      throw localError;  
    }  
    catch (Throwable localThrowable)  
    {  
      throw new UndeclaredThrowableException(localThrowable);  
    }  
  }  
  
  public final void drive()  
    throws   
  {  
    try  
    {  
  
       // 方法功能实现交给InvocationHandler处理  
  
      this.h.invoke(this, m4, null);  
      return;  
    }  
    catch (Error|RuntimeException localError)  
    {  
      throw localError;  
    }  
    catch (Throwable localThrowable)  
    {  
      throw new UndeclaredThrowableException(localThrowable);  
    }  
  }  
  
  public final int hashCode()  
    throws   
  {  
    try  
    {  
  
       // 方法功能实现交给InvocationHandler处理  
  
       return ((Integer)this.h.invoke(this, m0, null)).intValue();  
    }  
    catch (Error|RuntimeException localError)  
    {  
      throw localError;  
    }  
    catch (Throwable localThrowable)  
    {  
      throw new UndeclaredThrowableException(localThrowable);  
    }  
  }  
  
  public final String toString()  
    throws   
  {  
    try  
    {  
  
       // 方法功能实现交给InvocationHandler处理  
      return (String)this.h.invoke(this, m2, null);  
    }  
    catch (Error|RuntimeException localError)  
    {  
      throw localError;  
    }  
    catch (Throwable localThrowable)  
    {  
      throw new UndeclaredThrowableException(localThrowable);  
    }  
  }  
  
  static  
  {  
    try  
    {  //为每一个需要方法对象,当调用相应的方法时,分别将方法对象作为参数传递给InvocationHandler处理  
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });  
      m3 = Class.forName("com.foo.proxy.Rechargable").getMethod("recharge", new Class[0]);  
      m4 = Class.forName("com.foo.proxy.Vehicle").getMethod("drive", new Class[0]);  
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);  
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);  
      return;  
    }  
    catch (NoSuchMethodException localNoSuchMethodException)  
    {  
      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());  
    }  
    catch (ClassNotFoundException localClassNotFoundException)  
    {  
      throw new NoClassDefFoundError(localClassNotFoundException.getMessage());  
    }  
  }  
}  

仔细观望能够看出生成的动态代理类有以下特点:

1.接续自 java.lang.reflect.Proxy,完结了 Rechargable,Vehicle
那四个ElectricCar完结的接口;

二.类中的所有办法都以final 的;

3.具有的法子效果的贯彻都合并调用了InvocationHandler的invoke()方法。

起名 13

 

cglib 生成动态代理类的编写制定—-通过类继承:

  JDK中提供的扭转动态代理类的体制有个显然的天性是:
某些类必须有落实的接口,而变更的代理类也只可以代理有些类接口定义的点子,比如:若是地方例子的ElectricCar实现了继续自八个接口的主意外,其它完毕了艺术bee()
,则在发生的动态代理类中不会有这些办法了!更极致的情形是:要是有些类未有完毕接口,那么那么些类就不能够同JDK产生动态代理了!

      幸亏大家有cglib。“CGLIB(Code Generation
Library),是3个强硬的,高质量,高品质的Code生成类库,它能够在运维期扩展Java类与实现Java接口。”

cglib 创立有些类A的动态代理类的方式是:

  1. 查找A上的富有非final的public类型的方法定义;
  二. 将那个措施的概念转换来字节码;
  三. 将整合的字节码转换来相应的代办的class对象;
  4. 落到实处MethodInterceptor接口,用来处理对代理类上享有办法的乞请(这些接口和JDK动态代理InvocationHandler的意义和剧中人物是1致的)

二个幽默的例证:定义二个Programmer类,三个Hacker类

package samples;  
/** 
 * 程序猿类 
 * @author louluan 
 */  
public class Programmer {  
  
    public void code()  
    {  
        System.out.println("I'm a Programmer,Just Coding.....");  
    }  
}  

 

package samples;  
  
import java.lang.reflect.Method;  
  
import net.sf.cglib.proxy.MethodInterceptor;  
import net.sf.cglib.proxy.MethodProxy;  
/* 
 * 实现了方法拦截器接口 
 */  
public class Hacker implements MethodInterceptor {  
    @Override  
    public Object intercept(Object obj, Method method, Object[] args,  
            MethodProxy proxy) throws Throwable {  
        System.out.println("**** I am a hacker,Let's see what the poor programmer is doing Now...");  
        proxy.invokeSuper(obj, args);  
        System.out.println("****  Oh,what a poor programmer.....");  
        return null;  
    }  
  
}  

 

package samples;  
  
import net.sf.cglib.proxy.Enhancer;  
  
public class Test {  
  
    public static void main(String[] args) {  
        Programmer progammer = new Programmer();  
          
        Hacker hacker = new Hacker();  
        //cglib 中加强器,用来创建动态代理  
        Enhancer enhancer = new Enhancer();    
                 //设置要创建动态代理的类  
        enhancer.setSuperclass(progammer.getClass());    
               // 设置回调,这里相当于是对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实行intercept()方法进行拦截  
                enhancer.setCallback(hacker);  
                Programmer proxy =(Programmer)enhancer.create();  
                proxy.code();  
          
    }  
}  

程序执行结果:

起名 14

 

让大家看看通过cglib生成的class文件内容:

package samples;  
  
import java.lang.reflect.Method;  
import net.sf.cglib.core.ReflectUtils;  
import net.sf.cglib.core.Signature;  
import net.sf.cglib.proxy.Callback;  
import net.sf.cglib.proxy.Factory;  
import net.sf.cglib.proxy.MethodInterceptor;  
import net.sf.cglib.proxy.MethodProxy;  
  
public class Programmer
EnhancerByCGLIB
fa7aa2cd extends Programmer  
  implements Factory  
{  
   //......省略  
  private MethodInterceptor CGLIB$CALLBACK_0;  // Enchaner传入的methodInterceptor  
   // ....省略  
  public final void code()  
  {  
    MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;  
    if (tmp4_1 == null)  
    {  
      tmp4_1;  
      CGLIB$BIND_CALLBACKS(this);//若callback 不为空,则调用methodInterceptor 的intercept()方法  
    }  
    if (this.CGLIB$CALLBACK_0 != null)  
      return;  
      //如果没有设置callback回调函数,则默认执行父类的方法  
      super.code();  
  }  
   //....后续省略  
}  

 

过多期刊、建立模型等急需付出诗歌的比赛都提供了LaTeX模板,下载后在模板中填充内容就好,相应的格式都以提前设定好的。

一旦要在文章中插入公式,可输入如下命令:

四Photoshop也可将pdf文件转换为eps图片,但小编利用进程中,发现PS生成的eps文件就如不再是矢量图,因为随着放大倍数的充实,图片的成色也在降低。更令人无法经受的是,图片尺寸相同,PS生成的eps文件大小是用Acrobat的二倍以上。因而小编提出用Acrobat即可。

点开 citation
的链接,将内容保留为对应的文书名。比如 BibTeX 格式的 citation
音讯,将其保存到名称叫 xxx.bib 的纯文本文件中,然后到 JabRef 里选拔 File->Import into
current database,将该文件导入当前数据库,你就会发觉数据库中多了一条记下。当然,JabRef 帮助广大 citation
音讯格式,可是既然 JabRef 选取 BibTeX 格式管理数据库,大家依然尽量下载 BibTeX 格式的 citation。

④编译

\begin{flushleft}

如上海体育地方,编号为点击顺序。即LaTex->BibTex->LaTex.。那样,文献即被引如到小说中。

   

   

例子:$p_{i}^{1}$ 
结果:pi1

起名 15

   

也足以【中文TeX套装】->【Package
Manager】,然后在弹出的窗口中精选【refresh
now(马上更新)】。

TeX是1个很好排版工具,在教育界10分盛行,特别是数学、物管理学和总计机科学界。

(二)eps图片的插入

\caption{SSD
Architecture}\label{fig:SSDArchitecture}
\end{figure}

Vista/Win7/Win8:
 C:\Users\您的用户名\AppData\Roaming\Microsoft\Word\STARTUP

前边隔行尾部的”\nonumber”是告诉编写翻译器不要对此行实行编号;

(一)eps图片的制作

   

a)用JabRef打开包涵文献的.bib文件,双击想要插入Word的文献,复制最下边壹栏Bibtexkey中的字符串,每种字符串对应一篇文献

(一)公式插入

(1)JabRef生成.bib文献库

贰可用如下方式插入长6cm宽三.四cm的名字为SSDArchitecture.eps的图样:

起名 16

那边须要提示读者的是,模板中往往带有长段的诠释,大多是对文件、公式、图片、文献等内容的选用表达。

小编恰恰接触到TEX排版,相关文化完全空白,用了两天时间学习并完结了一篇诗歌的完整排版。

在上述例子中,大家运用了”eqnarray”,类似于选取数组对公式进行排列;

起名 17

里头碰到许多没相当,着实劳碌,分享至上,现将其消除办法计算总结,共同学习。

壹、工具介绍

注意:进度中会生成bbl文件还有aux文件,借使编写翻译时有报错,可尝试删除该bbl和aux文件再开始展览编写翻译。

③推送

起名 18

①设置

3去文件目录CTEX\CTeX\ctex\doc中看看,ctex-faq.pdf能够解答大多数相逢的标题,非凡有利。

五、图片处理

2包先生的latex学习笔记

总的看,大家用CTeX套装中的Win艾德t为随想排版,用JabRef管理随想的参考文献,用Visio +
Photoshop来制作并转移散文需求的.eps图片。

 肆、公式处理

首先在您所打开的bib文件里找到您要引用的文献,点击即选拔它;

   

在\being{document}之后添加命令:\bibliographystyle{IEEEtran}(括号内为文献模板,可更改。需在您的tex文件1律目录下增加对应的bst文件。本例为IEEEtran.bst。当然,可将该bst文件添加到CTEX(安装目录)\MiKTeX\bibtex\bst目录下,那样在你富有的ctex工程里能够采取)

,出现壹串引用代码;

2用Adobe Acrobat
Professional打开该pdf文件,选用【工具】→【页面】→【剪裁】,设定好想要留下的区域(左图),双击,获得不含大片空白的pdf图片文件(右图)。

把宏包解压到
USEKugaNAME\local\texmf,然后刷新数据库,即texhash。

(2)上下标

     

   

小说的大致结构如下:

当蒙受编写难题时,查看那些注释往往比谷歌(Google)关键词要高效得多。

工具栏中Line
Numbers,即可展现行号。

例子:

壹用Win艾德t编辑排版时,插入的eps图片需求和TeX文件在同二个文本夹中,否则需求用那行代码来设定图片的储存路径

作者使用Win捌.一(x6四)操作系统,能够选择上述全数工具。尤其须求提出的是,JabRef是用Java语言写的,所以需求配备Java环境,去oracle下载相应JDK即可。

(叁)学习资料

如上为二日学习的满贯赢得,计算与此,以谢谢互连网强大到能一下子就解决了大多数题材,多谢电脑另一端素昧平生包车型地铁许多好心人。

(2)添加新的宏包

\\”即换行;

起名 19

JabRef是几个开源的参考文献管理软件,使用 Java 语言编写,所以自然具备跨平台湾特务色,通用于安装有Java协理的Windows,
Linux和Mac,软件主页http://jabref.sourceforge.net/。它可以很方便地管理下载到本机的文献,生成 BibTeX 文献数据库,供 LaTeX 或别的软件使用,能够与 Kile, Emacs, Vim,
Win艾德t 等两种软件结合使用。

c)先不用焦躁,将光标移到您散文的参考文献那一章节,点击

起名 20

起名 21

故事集中图是二个很关键的局地,相比较直接在Tex中以代码画图,先从别的工具中画好图再导入TeX中要方便急迅得多。

(一)常用绘图宏包

公式不会自行换行,只怕会当先文书档案范围,非常不佳看!笔者尝试了选择”\\”、”\newline”、”\break”都不见效,后来终于发现能够这么做:

起名 22

\begin{eqnarray}\label{eq:eq2}
\lefteqn{Response\_time_{write}(r_{i}, R_{write}^{1}, R_{write}^{2}, R_{write}^{3},
R_{hist})}\nonumber\\
\lefteqn{==F(r_{i}, R_{write}^{1}, R_{write}^{2}, R_{write}^{3}, R_{hist})}\nonumber\\
\lefteqn{= aF(r_{i}, 0, 0, 0, 0) + bF(r_{i}, R_{write}^{1}, 0, 0, 0)}\nonumber\\
\lefteqn{+ cF(r_{i}, 0, R_{write}^{2}, 0, 0)}\nonumber\\
&+ dF(r_{i}, 0,
0,
R_{write}^{3},0) + eF(r_{i}, 0, 0, 0, R_{hist}) + f
\end{eqnarray}

利用别的软件绘图eps文件插入杂谈固然方便,但借使能在TeX文件中央直机关接以代码的格局绘制图片,则会让舆论变得轻便而不宜损坏。

\begin{figure}[ht]

①安装**Bibtex4Word**

d)将解压缩后的Bibtex肆Word软件文件夹中的”bibtex四word.dot”文件,复制粘贴至Word的运营文件夹:

(1)结构

c)确认保证机器春天有管理BibTeX方式的参考文献管理工科具,推荐使用JabRef

2在Win艾德t中添加命令

Linux:

Windows:

(三)JabRef在CTeX中插入文献

\begin{equation}\label{eq:eq1}
A= b + c
\end{equation}

   

把宏包解压到CTEX\MiKTeX\tex\latex下,用命令行执行texhash。

此外,对在该工具栏里未有的标记,可点击http://www.cfsm.cn/info/symbols/symbols.htm。

   

②使用**Bibtex4Word**

起名 23

那里大家使用Win艾德t。并在工具栏Options-Preference-External
programs-External programs-Settings for Win艾德t

   

1用Microsoft Office
中的Word大概Visio画出你必要的图样,将其另存为pdf文件。注意要一副图生成四个唯有一页的pdf文件。

b)回到Word界面,先在您想要插入类似[1]一类的数字标注处,点击Bibtex四Word工具条中的

  • \caption{}为图片的标题,能够选择\caption{\label{}}的格式,使caption为label;
  • 图表的长度宽度值能够缺省,图片为原大小;若只设定长大概宽,则图片缩放时间长度度宽度比不变。

右边的

末端的(一)是自动生成的。Latex将活动依据你所编纂的公式顺序添加标号。

\graphicspath{{../eps/}}

起名 24

起名 25

是因为不佳演示,请大家温馨开始展览尝试体会一下,方法是很简短的。

(二)模板使用

a)登录http://www.ee.ic.ac.uk/hp/staff/dmb/perl/index.html

BibTeX是一种格式和二个程序, 用于协调LaTeX的参考文献处理。BibTeX 使用数据库的的点子来保管参考文献,其文献数据库文件的后缀名称为.bib。

(1)行号显示

JabRef 的文献数据库是纯文本的 bib 文件,可以将该数据库文件放在该主旨文献的目录下。比如要白手起家贰个 liver 方向相关文献的数据库,只需建立二个liver目录,在 JabRef 中精选 File->New
Database,然后 Ctrl-S,将该数据库起名
liver.bib,保存在liver目录下。然后将 File->Database
properties 中的 Database Encoding
设置为 UTF-捌(这是因为诗歌的撰稿人大概是鬼子和咱们会时不时用粤语写些注释,所以最佳用国际化的字符集), Directory
都设置成点号 “.”(表示当前目录,那样你就能够将全部目录打个包,扔到另一台 Linux/Win/Mac
机器上照常使用)。

当然对于实在生僻的标志,你也能够百度”LaTeX符号”举办查找。

安装Path to
WinEdt.exe为你安装的Win艾德t的地点即可,即CTEX(安装目录)\WinEdt\Win艾德t.exe。设置好后选拔ok-ok即可。

①LaTeX 编辑部:http://zzg34b.w3.c361.com/index.htm

接下来在Win艾德t用光标定位你就要推送的文献引用的职位;回到jabRef界面,点击工具栏中

按钮,即将该引用推送到钦命地点。如

   

起名 26

起名 27

起名 28

\includegraphics[width=6cm,height=3.4cm]{SSDArchitecture.eps}

其中\label里是该公式的竹签,将会在引用该公式时用到。

“&”本用于制表,这里能够用于固定+号的岗位。

Microsoft Office
Visio
能便宜地开创具有标准外观的图片,以便领悟、记录和剖析新闻、数据、系统和经过(选拔)。

此间我们将教师如何选用JabRef在CTeX编辑器Win艾德t里实行文献推送。

八、别的技术

   

“A = b +
c”那部分写入公式即可,不必要加$符号。运转后将显示

(三)长等式换行

3、文字处理

\\begin{flushleft}  
\*  
\\end{flushleft} 

TeX协助eps格式的矢量图,那体系型的图无论放大多少倍都会很鲜明,这也是不可胜数刊物须求散文中的图片为eps格式的来头。

CTeX是TeX中的一个本子,指的是CTeX 中文套装的简称。CTeX汉语套装是依照 Windows 下的 MiKTeX系统,集成了编辑器 Win艾德t及其它的处理软件,并增添了对汉语的总体帮衬。

2、环境要求

epic、eepic、pict2e 宏包是正统 LaTeX 的 picture 环境的逐级修正扩大抓好,可是依然不方便使用。只是因为一些与绘图无关的宏包使用到了 picture 环境的部分功力(如表格的斜线),由此偶尔能够应用 pict2e 宏包。

6、图片绘制

在Win艾德t工具栏里找到如上红框里的图标,点击后您能找到大部分的标志。

其三方的LaTeX作图的宏包很多。有的宏包功用专壹,如画饼图,有的则范围广泛功效强大。数学交流图一般最常用的是XYpic,假使要画图论图形,大家能够用 PSTricks、TikZ、MetaPost 等等各类工具;但要是并不珍爱种种结点的坐标而只想要急迅地把几十广大个点连起来,那么 GraphViz
中的 dot 就少了一些成了唯1的选项……

BibTex4Word是1个Microsoft
Word的插件,能够将.bib文件中的参考文献方便地插入到Word文书档案中,软件的利用格局和安装包能够在http://www.ee.ic.ac.uk/hp/staff/dmb/perl/找到。本文主要介绍在CTeX中插入文献,这里只是顺带一提。

\end{flushleft}

起名 29

,便可自动生成文献的有关新闻,而b)步骤中的代码串也变成了对应的数字标注。

   

   

越多的宏包及其特性看那里http://zzg34b.w3.c361.com/package/figure-2.htm

Adobe Acrobat
Professional
在此处根本用来将.pdf文件转换为.eps文件。

LaTeX 本人有2个 picture 环境,提供了赞不绝口简单的绘图作用:由坐标画斜率有限定的直线、半径有限定的圆、圆角矩形、箭头、标注,以及描点画的粗糙曲线。由于效果太弱、限制过多、使 用复杂,标准 LaTeX 的 picture 环境很少被人使用。

   

b)确定保障机器上早已设置有TeX软件,推荐使用以MiKTeX为主题的华语套装CTeX

上标和下标对Yu Gang刚接触latex的人的话可能是个难点,所以在此处尤其给出:

e)重新开动Word后,Word的工具栏准将会产出Bibtex四Word的工具条

在上海教室的下拉菜单中精大选办文献推送的目的编辑器。

起名 30

\documentclass[ ]{ }
\usepackage{ }
\being{document} %过后往上都以导言区(preamble)

\section{ }   %1级章节
\subsection{ }  %2级章节
\subsubsection{ }%3级章节,没有subsubsubsection

\end{document}  %begin开始,end结束

**(3)关于eps文件其他** 

①我们可以用CTeX集成的GSview来打开eps文件,其中,【Options】→【Show Bounding
Box】可以帮助我们更方便的确定图片的长和宽。


②eps文件本身可以用记事本打开,打开后可以看到PostScript语言的文本信息。这里可以更直观地看到BoundingBox等信息。 

**(4)设置子图** 

①有时候我们需要将多副图组织在一起,并设以统一的标题方便引用,如  

![](https://images0.cnblogs.com/blog/705129/201504/301934163187733.png) 

这时我们可以使用以下方法来实现: 

在开头加上 

\\usepackage{subfigure}


在需要插图处,按如下格式插入子图 

\\begin{figure}\[ht\]


\\begin{flushleft}


\\subfigure\[\]{


\\label{fig:FigureOne:a}


\\includegraphics\[width=4cm,height=2.4cm\]{ FigureOne
-a.eps}} 

\\hspace{0.1cm}


\\subfigure\[\]{


\\label{FigfigureTwo:b}


\\includegraphics\[width=4cm,height=2.4cm\]{ FigureTwo
-b.eps}} 

\\end{flushleft}


\\caption{FigureName}\\label{fig:FigureName}


\\end{figure}


其中\\hspace{}设定子图的间隔距离。 

在\end{document}以前添加命令:\bibliography{SSDRef}(括号内为您所要使用的bib文件,可八个,用逗号隔绝)

文献数据库的始末自然是壹篇一篇的舆论,每篇故事集在数据库中占一条记下。那么哪些添加随想呢?自然不用全体手工业添加,差不离全部的数据库在提供杂谈下载的同时都有该诗歌 citation
信息的下载,下边那张图上是电脑科学类常用数据库网址的 citation
下载链接示例。恐怕你以前不领会它们是做什么样用的,但你总能在文章下载页面的有个别地方找到它们。

⑦、管理与插入参考文献

 (2)符号

(贰)BibTeX在word中插入文献

 结果:

三将剪裁好的pdf另存为eps格式,在另存对话框的上面有3个装置选项,设置里面某个参数,将一般的PostScript设为语言级三级,字体范围设为嵌入和引用的书体,然后鲜明保存。那样就足以生成eps格式的图纸了,用latex编译出来效果相当好。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图