1. 程式人生 > >spring-aop

spring-aop

private 我們 數據 pri evaluate users ica led 設計

聊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