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。
切面類
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的動態代理類中加入了責任鏈設計模式(攔截器),在代理方法被首次調用時,會先找到與該方法匹配的攔截器,首次調用後就可將其緩存。
參考:https://www.ibm.com/developerworks/cn/java/j-lo-springaopcglib/
spring-aop