spring-aop
聊spring-aop之前,先来看一下Aspectj的使用
Aspectj
从http://www.eclipse.org/aspectj/downloads.php下载好aspectj-1.x.x.jar后,使用java -jar aspectj-1.x.x.jar命令就可以安装了。
简单编写了两个文件用来测试
切面类
1 import org.aspectj.lang.annotation.Aspect; 2 import org.aspectj.lang.annotation.Before; 3 4 @Aspect 5 public class AspectjSample { 6 @Before("execution(* *(..))")//匹配所有方法 7 public void before() { 8 System.out.println("before"); 9 } 10 }
测试类
1 public class AspectjTest { 2 public static void main(String[] args) { 3 System.out.println("main"); 4 } 5 private void privateFun() { 6 System.out.println("private fun"); 7 } 8 private static void privateStaticFun() { 9 System.out.println("private static fun"); 10 } 11 }
通过ajc -source 8 -d . -cp ../lib/aspectjrt.jar AspectjTest.java AspectjSample.java编译命令,将切面AspectjSample织入AspectTest中,将生成的class文件反编译后
切面类
1 import org.aspectj.lang.NoAspectBoundException; 2 import org.aspectj.lang.annotation.Aspect; 3 import org.aspectj.lang.annotation.Before; 4 5 @Aspect 6 public class AspectjSample { 7 public AspectjSample() { 8 } 9 10 @Before("execution(* *(..))") 11 public void before() { 12 System.out.println("before"); 13 } 14 15 public static AspectjSample aspectOf() { 16 if (ajc$perSingletonInstance == null) { 17 throw new NoAspectBoundException("AspectjSample", ajc$initFailureCause); 18 } else { 19 return ajc$perSingletonInstance; 20 } 21 } 22 23 public static boolean hasAspect() { 24 return ajc$perSingletonInstance != null; 25 } 26 27 static { 28 try { 29 ajc$postClinit(); 30 } catch (Throwable var1) { 31 ajc$initFailureCause = var1; 32 } 33 34 } 35 }
测试类
1 public class AspectjTest { 2 public AspectjTest() { 3 } 4 5 public static void main(String[] args) { 6 AspectjSample.aspectOf().before(); 7 System.out.println("main"); 8 } 9 10 private void privateFun() { 11 AspectjSample.aspectOf().before(); 12 System.out.println("private fun"); 13 } 14 15 private static void privateStaticFun() { 16 AspectjSample.aspectOf().before(); 17 System.out.println("private static fun"); 18 } 19 }
可见aspectj在字节码层面(因为源文件不会被修改,只能通过编译或类加载过程修改字节码,从而生成最终的类)修改了我们的测试类,aspectj提供了编译期和类加载其的织入方式,spring-aop又是怎样的呢?
spring-aop
在spring中使用aspectj注解可以非常方便的编写aop,我们就以aspectj注解为例来说明spring-aop(spring只是使用了Aspectj的相关注解、语法,并没有使用Aspectj的代理机制)。
切面类
1 @Order(1) 2 @Aspect 3 public class SpringAOP { 4 @Before("@annotation(age)")//使用Aspectj语法 5 public void before(Age age) { 6 System.out.println("before"); 7 } 8 }
Age注解
1 import java.lang.annotation.ElementType; 2 import java.lang.annotation.Retention; 3 import java.lang.annotation.RetentionPolicy; 4 import java.lang.annotation.Target; 5 6 @Retention(value = RetentionPolicy.RUNTIME) 7 @Target(ElementType.METHOD) 8 public @interface Age { 9 int min() default 0; 10 int max() default 150; 11 }
Service类
1 @Service 2 public class UserServiceImpl implements UserService { 3 @Resource 4 private UserMapper userMapper; 5 ...... 6 @Override 7 @Age(min = 10, max = 20) 8 public void insert(User user) { 9 userMapper.insert(user); 10 } 11 ...... 12 }
spring-aop配置文件
1 <!--开启aspectj注解,proxy-target-class表示是否使用基于类的代理模式(cglib)--> 2 <aop:aspectj-autoproxy proxy-target-class="false"/> 3 <!--切面类--> 4 <bean id="springAOP" class="com.zyong.spring.aop.SpringAOP" />
在测试类中注入UserService,调用service类的insert方法,简单插入一个数据项,会发现实际调用的是代理类的insert方法(说明测试类中注入的就是UserService代理类)。
invoke就是InvocationHandler接口中的invoke,其核心逻辑就是调用的proceed方法
1 public Object proceed() throws Throwable { 2 // We start with an index of -1 and increment early. 3 if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) { 4 return invokeJoinpoint();//调用真正的业务逻辑 5 } 6 7 Object interceptorOrInterceptionAdvice = 8 this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex); 9 if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) { 10 // Evaluate dynamic method matcher here: static part will already have 11 // been evaluated and found to match. 12 InterceptorAndDynamicMethodMatcher dm = 13 (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice; 14 if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) { 15 return dm.interceptor.invoke(this); 16 } 17 else { 18 // Dynamic matching failed. 19 // Skip this interceptor and invoke the next in the chain. 20 return proceed(); 21 } 22 } 23 else { 24 // It's an interceptor, so we just invoke it: The pointcut will have 25 // been evaluated statically before this object was constructed. 26 return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); 27 } 28 }
可以看出,spring的aop是在jdk的动态代理类中加入了责任链设计模式(拦截器),在代理方法被首次调用时,会先找到与该方法匹配的拦截器,首次调用后就可将其缓存。
最后再提一下cglib,与Aspectj、jdk动态代理都不同,cglib是在运行时动态生成被代理类的子类。
1 import net.sf.cglib.proxy.Enhancer; 2 import net.sf.cglib.proxy.MethodInterceptor; 3 import net.sf.cglib.proxy.MethodProxy; 4 import org.junit.Test; 5 6 import java.lang.reflect.Method; 7 8 public class CGLibTest { 9 private void hello() { 10 System.out.println("hello"); 11 } 12 13 @Test 14 public void test() { 15 CGLibTest instance = CGLibTest.getInstance(); 16 instance.hello(); 17 //before 18 //hello 19 //after 20 } 21 22 private static CGLibTest getInstance() { 23 Enhancer en = new Enhancer(); 24 // 设置要代理的目标类 25 en.setSuperclass(CGLibTest.class); 26 // 设置要代理的拦截器 27 en.setCallback(new CGLibAspect()); 28 // 生成代理类的实例 29 return (CGLibTest) en.create(); 30 } 31 32 private static class CGLibAspect implements MethodInterceptor { 33 34 @Override 35 public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { 36 System.out.println("before"); 37 // Object result = method.invoke(obj, args);//method是代理后的方法 38 Object result = proxy.invokeSuper(obj, args); 39 System.out.println("after"); 40 return result; 41 } 42 } 43 }
参考:https://www.ibm.com/developerworks/cn/java/j-lo-springaopcglib/
http://www.baeldung.com/aspectj