
Spring中最为重要的一个特性之一为AOP。它使得我们开发过程中一些棘手的情况下做了很好的处理(在不改动原本代码逻辑的前提下,前后增加额外的逻辑处理),让我们尽可能的少修改原本的代码块,使得我们的程序出错的可能性大降低。这里我就介绍一个不用框架而完成的一个AOP。
ASM代码实现
需要增强类
public class Montos {
public void say() {
System.out.println("montos is a handsome boy");
}
}
字节码的观察类
public class MyClassVisitor extends ClassVisitor implements Opcodes {
public MyClassVisitor(ClassVisitor cv) {
super(ASM5, cv);
}
@Override
public void visit(int version, int access, String name, String signature,String superName, String[] interfaces) {
cv.visit(version, access, name, signature, superName,interfaces);
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature,String[] exceptions) {
MethodVisitor mv = cv.visitMethod(access, name, desc, signature,exceptions);
//不增强构造方法
if (!name.equals("<init>") && mv != null) {
mv = new MyMethodVisitor(mv);
}
return mv;
}
/**
* 对类内方法的观察
*/
class MyMethodVisitor extends MethodVisitor implements Opcodes {
public MyMethodVisitor(MethodVisitor mv) {
super(Opcodes.ASM5, mv);
}
/**
* 开始访问某一个方法的 Code 区时被调用 --- 类似于AOP的前置调用
*/
@Override
public void visitCode() {
super.visitCode();
mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
mv.visitLdcInsn("show time begin");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
}
/**
* 判断了当前指令是否为无参数的“return”指令,如果是就在它的前面添加一些指令,也就是将 AOP的后置逻辑
*/
@Override
public void visitInsn(int opcode) {
if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN) || opcode == Opcodes.ATHROW) {
// 方法在返回之前,打印 "show time end"
mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
mv.visitLdcInsn("show time end");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
}
mv.visitInsn(opcode);
}
}
}
字节码生成类
public class Generator {
public static void main(String[] args) throws Exception {
// 读取字节码
ClassReader classReader = new ClassReader("com/montos/asm/Montos");
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
// 处理字节码
ClassVisitor classVisitor = new MyClassVisitor(classWriter);
classReader.accept(classVisitor, ClassReader.SKIP_DEBUG);
byte[] data = classWriter.toByteArray();
// 输出
File f = new File("绝对路径/target/classes/com/montos/asm/Montos.class");
FileOutputStream fout = new FileOutputStream(f);
fout.write(data);
fout.close();
System.out.println("now generator cc success!!!!!");
}
}
测试类
public class TestMain {
public static void main(String[] args) {
Montos montos = new Montos();
montos.say();
}
}
结果我们能发现,在第一次运行测试类的时候是按照源码里面编写的那样进行输出。当我们对基类进行加强,然后再运行测试类的时候,发现我们前后增加的语句输出了。
Javassist代码实现
需要增强类
public class Montos {
public void say() {
System.out.println("montos is a handsome boy");
}
}
增强实现类
public class JavassistTest {
public static void main(String[] args) throws NotFoundException,
CannotCompileException, IllegalAccessException, InstantiationException,
IOException {
ClassPool cp = ClassPool.getDefault();
CtClass cc = cp.get("com.montos.javassist.Montos");
CtMethod m = cc.getDeclaredMethod("say");
m.insertBefore("{ System.out.println(\"show time begin\"); }");
m.insertAfter("{ System.out.println(\"show time end\"); }");
Class c = cc.toClass();
cc.writeFile("绝对路径/target/classes/com/montos/javassist");
Montos h = (Montos) c.newInstance();
h.say();
}
}
通过上面的
Demo,我们也能发现,我们也是完成了对于一个类的增强实现。
总结
上面是通过两种简单的方式阐述了我们日常代码中的
AOP实现方式。第一种是通过ASM,第二种是通过Javassist实现。ASM是在指令层次上操作字节码的,Javassist则强调通过源代码层次操作字节码 。两者都是从底层字节码作为最终解决方法的。只不过Javassist则更偏向于我们使用编程语言的人接受。
祝小伙伴新的一年顺乐顺乐顺乐~