zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    屏蔽鼠标右键/F1帮助
    vs2010如何连接到mysql数据库
    经典的数据库设计贴吧
    js子窗口刷新父窗口
    数据库三大范式
    写给毕业生
    SQL Server权限数据库设计
    .NET
    ASP.NET MVC框架(第一部分) 【转】
    什么是SQL注入法攻击 .
  • 原文地址:https://www.cnblogs.com/holoyong/p/7285028.html
Copyright © 2011-2022 走看看