package demo.aop; public interface ActionI { public void doSth(); }
package demo.aop; public class ActionImpl implements ActionI { @Override public void doSth() { System.out.println("doing sth"); } }
1.通过装饰器方法
package demo.aop; public class ActionDecorator implements ActionI { private ActionI action; public ActionDecorator(ActionI action) { this.action = action; } @Override public void doSth() { System.out.println("doSth invoke start"); action.doSth(); System.out.println("doSth invoke end"); } public static void main(String[] args) { ActionI action = new ActionDecorator(new ActionImpl()); action.doSth(); } }
输出: doSth invoke start doing sth doSth invoke end
使用装饰器的时候,如果要切入多个方法的时候,装饰器内就需要手懂编写同样多个方法。这种方法是显然是不够动态和灵活的。
2.使用Proxy代理
package demo.aop; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class ActionInvocationHandler implements InvocationHandler { private ActionI action; public ActionInvocationHandler(ActionI action){ this.action = action; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Log.info(method.getName()+" start"); method.invoke(action, args); Log.info(method.getName()+" end"); return null; } public static void main(String[] args){ ActionI action = (ActionI)Proxy.newProxyInstance(ActionI.class.getClassLoader(), new Class[] { ActionI.class }, new ActionInvocationHandler(new ActionImpl())); action.doSth(); } }
输出 doSth start doing sth doSth end
不足之处在于:代理是面向接口的,代理的对象必须是对一个接口的实现。代理是通过反射机制实现的,效率不高。
相较以上两种方法,改变java方法的更直接的方法是修改字节码
Java 规范详细说明了 class 文件的格式,直接编辑字节码确实可以改变 Java 类的行为。
3.hook
启动时往 Java 虚拟机中挂上一个用户定义的 hook 程序,可以在装入特定类的时候改变特定类的字节码,从而改变该类的行为。但是其缺点也是明显的:
Instrument 包是在整个虚拟机上挂了一个钩子程序,每次装入一个新类的时候,都必须执行一遍这段程序,即使这个类不需要改变。
直接改变字节码事实上类似于直接改写 class 文件,无论是调用 ClassFileTransformer. transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer),还是 Instrument.redefineClasses(ClassDefinition[] definitions),都必须提供新 Java 类的字节码。也就是说,同直接改写 class 文件一样,使用 Instrument 也必须了解想改造的方法相对类首部的偏移量,才能在适当的位置上插入新的代码。
尽管 Instrument 可以改造类,但事实上,Instrument 更适用于监控和控制虚拟机的行为。
4.asm
Java 字节码操控框架,能动态生成类或者增强既有类的功能。