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

posted @ 2017-08-04 15:38  holoyong  阅读(213)  评论(0编辑  收藏  举报