aop实现方式有三种,分别是ajc编译器,agent类加载,和代理方式。spring使用的是代理方式
ajc编译器实现aop需要使用maven插件
org.codehaus.mojo aspectj-maven-plugin 1.14.0 1.8 1.8 1.8 true ignore UTF-8 true false compile
下载完插件后使用maven重新编译一下才可以使用,本地idea点击maven的compile。示例代码
定义切面
@Aspect
public class MyAspet {@Before("execution(* com.example.springtest.aop.aspet.MyService.foo())")public void before(){System.out.println("before...");}
}
定义目标类
public class MyService {public void foo(){System.out.println("foo......");}
}
测试与结果
public static void main(String[] args) {new MyService().foo();
}
ajc编译器使用并不广泛,示例中的切面和目标类并没有交给spring管理。它的一个小优势是可以处理静态方法,无论是静态还是非静态的方法都可以使用ajc编译器达到面向切面编程的效果。原因是在编译阶段更改.class源码。增强后的目标类代码▼
agent实现方式是在类加载时进行增强的。使用agent类加载方式实现aop需要在VM参数加上
-javaagent:E:\software\apache-maven-3.5.0\conf\rep\org\aspectj\aspectjweaver\1.9.7\aspectjweaver-1.9.7.jar
代码可以使用上边例子的代码,可以达到一样的效果
代码示例
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;public class JdkProxyDemo {interface Foo{void foo();}static class Targent implements Foo{@Overridepublic void foo() {System.out.println("foo");}}public static void main(String[] args) {Targent targent = new Targent();ClassLoader classLoader = JdkProxyDemo.class.getClassLoader();Foo o = (Foo) Proxy.newProxyInstance(classLoader, new Class[]{Foo.class}, new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("before............");Object invoke = method.invoke(targent, args);System.out.println("after............");return invoke;}});o.foo();}
}
newProxyInstance方法参数
public static Object newProxyInstance(ClassLoader loader,Class>[] interfaces,InvocationHandler h)
ClassLoader loader | 类加载器,因为代理类没有源码,所以加载类时需要类加载器 |
Class>[] interfaces | 被代理的接口 |
InvocationHandler h | 抽象出来的接口,使得代理方法能在调用方实现 |
InvocationHandler接口方法参数
public Object invoke(Object proxy, Method method, Object[] args)throws Throwable;
Object proxy | 代理对象 |
Method method | 目标方法 |
Object[] args | 目标参数 |
注意点
通过arthas的jad查看生成的代理类如下
final class $Proxy0
extends Proxy
implements JdkProxyDemo.Foo {private static Method m1;private static Method m2;private static Method m3;private static Method m0;public $Proxy0(InvocationHandler invocationHandler) {super(invocationHandler);}static {try {m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);m3 = Class.forName("com.example.springtest.aop.jdk.JdkProxyDemo$Foo").getMethod("foo", new Class[0]);m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);return;}catch (NoSuchMethodException noSuchMethodException) {throw new NoSuchMethodError(noSuchMethodException.getMessage());}catch (ClassNotFoundException classNotFoundException) {throw new NoClassDefFoundError(classNotFoundException.getMessage());}}public final boolean equals(Object object) {try {return (Boolean)this.h.invoke(this, m1, new Object[]{object});}catch (Error | RuntimeException throwable) {throw throwable;}catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}public final String toString() {try {return (String)this.h.invoke(this, m2, null);}catch (Error | RuntimeException throwable) {throw throwable;}catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}public final int hashCode() {try {return (Integer)this.h.invoke(this, m0, null);}catch (Error | RuntimeException throwable) {throw throwable;}catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}public final void foo() {try {this.h.invoke(this, m3, null);return;}catch (Error | RuntimeException throwable) {throw throwable;}catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}
}
代码示例
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;public class CglibProxyDemo {static class Target{public void foo(){System.out.println("foo");}}public static void main(String[] args) {Target target = new Target();Target o = (Target) Enhancer.create(Target.class, new MethodInterceptor() {@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy)throws Throwable {System.out.println("before...");Object invoke = method.invoke(target, args);System.out.println("after...");return invoke;}});o.foo();}
}
MethodInterceptor接口的intercept()方法参数说明
Object intercept(Object var1, Method var2, Object[] var3, MethodProxy var4)
throws Throwable;
执行目标方法除了Method.invoke()还可以使用intercept()方法的第4个参数MethodProxy执行目标方法,并且使用MethodProxy不会使用反射,理论上效率会高一些
methodProxy.invoke(target,args);
target是目标视力,args是目标方法参数
methodProxy.invokeSuper(o,args);
o是代理对象自己,args是目标方法参数
MethodProxy的FastClass机制
Cglib动态代理执行代理方法效率之所以比JDK高是因为Cglib采用了FastClass机制,他为代理类和被代理类各生成了一个class(jdk动态代理优化则是一个方法生成一个代理类),这个class会为代理类与被代理类的方法分类index。这个index作为方法参数,FastClass可以直接定位到要调用的方法进行调用,这样省去了反射调用,所以效率比JDK动态代理快。
注意点