利用ClassFileTransformer实现aop

看了文章http://www.iteye.com/topic/1116696(这个文章非常好)中的字节码 部分,第一次接触到了java5新特性 instrumentation的ClassFileTransformer类,Instrumentation 的最大作用,就是类定义动态改变和操作。在 Java SE 5 及其后续版本当中,开发者可以在一个普通 Java 程序(带有 main 函数的 Java 类)运行时,通过 –javaagent 参数指定一个特定的 jar 文件(包含 Instrumentation 代理)来启动 Instrumentation 的代理程序。在类的字节码载入jvm前会调用ClassFileTransformer的transform方法,从而实现修改原类方法的功能,实现aop,这个的好处是不会像动态代理或者cglib技术实现aop那样会产生一个新类,也不需要原类要有接口。详细如下:

1.原类Business.java:

 

 

package model;

 

public class Business  {

 

 public boolean doSomeThing() {

        System.out.println("执行业务逻辑");

        return true;

    }

 

 public void doSomeThing2() {

        String s = "执行业务逻辑2";

        System.out.println(s);

    }

}

2.创建MyClassFileTransformer.java:

package bci;

import java.io.IOException;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;

import test.MyClassloader;

import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.NotFoundException;
import model.Business;

/**
 * 字节码转换器
 * 
 * 
 */
public class MyClassFileTransformer implements ClassFileTransformer {

    /**
     * 字节码加载到虚拟机前会进入这个方法
     */
    @Override
    public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined,
                            ProtectionDomain protectionDomain, byte[] classfileBuffer)
            throws IllegalClassFormatException {
        System.out.println(className);
        //如果加载Business类才拦截
        if (!"model/Business".equals(className)) {
            return null;
        }

        //javassist的包名是用点分割的,需要转换下
        if (className != null && className.indexOf("/") != -1) {
            className = className.replaceAll("/", ".");
        }
        try {
            //通过包名获取类文件
            CtClass cc = ClassPool.getDefault().get(className);
            //获得指定方法名的方法
            CtMethod m = cc.getDeclaredMethod("doSomeThing");
            //在方法执行前插入代码
            m.insertBefore("{ System.out.println(\"记录日志\"); }");
            return cc.toBytecode();
        } catch (NotFoundException e) {
        } catch (CannotCompileException e) {
        } catch (IOException e) {
            //忽略异常处理
        }
        return null;
    }

    /**
     * 在main函数执行前,执行的函数
     * 
     * @param options
     * @param ins
     */
    public static void premain(String options, Instrumentation ins) {
        //注册我自己的字节码转换器
        ins.addTransformer(new MyClassFileTransformer());
    }
}
3.测试类Test.java:
package bci;

import model.Business;

public class Test {

    /**
     * @param args
     */
    public static void main(String[] args) {
        new Business().doSomeThing();   
        new Business().doSomeThing2();   

    }
}
4.将bci.MyClassFileTransformer类打成jar包,名为aop.jar,同时修改改jar中的META-INF/MANIFEST.MF内容为:
Manifest-Version: 1.0
Premain-Class: bci.MyClassFileTransformer
Can-Redefine-Classes: true  
Can-Retransform-Classes: true  
Can-Set-Native-Method-Prefix: true    
说明: Premain-Cla ss用来允许 JAR 文件作为一个 Java 代理运行
5.运行Test.java, 添加jvm启动参数:-javaagent:D:\aop.jar

利用ClassFileTransformer实现aop_第1张图片
 点击run运行结果如下:

利用ClassFileTransformer实现aop_第2张图片
 

也可将bci.MyClassFileTransformer及bci.Test同时打到aop.jar包中,且将aop.jar放在d:\下,由于MyClassFileTransformer需要依赖misc.javassist-3.9.0.GA.jar包,所以同时将该包也放在d:\下,

修改aop.jar包的manifest.mf文件如下:

 

Manifest-Version: 1.0

Premain-Class: bci.MyClassFileTransformer

Class-Path: misc.javassist-3.9.0.GA.jar

Main-Class: bci.Test

注意格式(这些规则当时调式了好久。。真怪异):

1.每个属性都是回车结尾;

2.class-path当前路径是jar包所在的路径,由于aop.jar的路径是d:\aop.jar,所以其所引用的路径是d:\misc.javassist-3.9.0.GA.jar;如果依赖多个jar包,则以空格分隔;

3.如果Class-Path需要分行写,那么要注意,第一行的最后要留一个空格,下一行的开头要留一个空格,如果第三行不是Class-Path的内容了,则第二行末尾不用留空格。 

4.将Main-Class放在Class-Path后定义。

5.属性名:(空格)属性值,注意加上空格。

在d:\下运行命令:

java -javaagent:aop.jar -jar aop.jar(作为可执行jar运行)

或者:java -javaagent:aop.jar bci.Test(运行bci.Test类)

结果如下:


利用ClassFileTransformer实现aop_第3张图片

 

如果在manifest.mf文件中不设置class-parh属性,也可以利用-cp参数(依赖包)运行aop.jar:

java -javaagent:aop.jar -cp  misc.javassist-3.9.0.GA.jar bci.Test(运行bci.Test类)

注意此时不可java -javaagent:aop.jar -cp misc.javassist-3.9.0.GA.jar -jar aop.jar(作为可执行jar运行)这样运行,因为-jar 和-cp不可同时使用,-cp会被忽略,最终报misc.javassist-3.9.0.GA.jar中的类找不到错误。

你可能感兴趣的:(java,java,运维)