zoukankan      html  css  js  c++  java
  • Spring温故而知新 – Spring AOP

    AOP的相关专业术语 

    通知(Advice):定义在连接点做什么

               Spring中通知类型:前置通知,后置通知,返回通知,异常通知,环绕通知

    连接点(JoinPoint):程序执行过程中拦截的点,Sping中一般是方法

    切点(PointCut): 决定advice通知应该作用域哪个连接点

    切面(Aspect):切面就是对横切关注点的抽象

    引入(Introduction):无需修改现有类的代码,向现有类添加新的方法或属性

    织入(Weaving):把切面应用到目标对象并创建新的代理对象的过程。

    先理解两个概念:

    静态织入:通过特定的编译器在编译期间将需要增加的代码织入,即编译生成.class文件后,字节码已经被织入。

    动态织入:运行时动态将要增强的代码织入到目标类中,一般通过动态代理的技术实现,如JDK动态代理或者CGLIB动态代理

    Spring借鉴了AspectJ的切面,可以使用AspectJ来做切点解析和匹配,但是在运行时仍然是基于动态代理的AOP,并不依赖于AspectJ的编译器和织入器weaver (这点上区别于AspectJ:采用静态织入),但是编程模型几乎与编写成熟的AspectJ注解切面完全一致,这种AOP风格的好处在与能够不使用XML配置来完成。

    AspectJ切点表达式

    Spring借助AspectJ的切点表达式语言完成切面的定义,常见AspectJ指示器如下:

    execution : 匹配方法执行的连接点,这是你将会用到的Spring的最主要的切入点指示符。

    within : 限定匹配特定类型的连接点(在使用Spring AOP的时候,在匹配的类型中定义的方法的执行)。

    this : 限定匹配特定的连接点(使用Spring AOP的时候方法的执行),其中bean reference(Spring AOP 代理)是指定类型的实例。

    target : 限定匹配特定的连接点(使用Spring AOP的时候方法的执行),其中目标对象(被代理的应用对象)是指定类型的实例。

    args: 限定匹配特定的连接点(使用Spring AOP的时候方法的执行),其中参数是指定类型的实例。

    @target: 限定匹配特定的连接点(使用Spring AOP的时候方法的执行),其中正执行对象的类持有指定类型的注解。

    @args:限定匹配特定的连接点(使用Spring AOP的时候方法的执行),其中实际传入参数的运行时类型持有指定类型的注解。

    @within: 限定匹配特定的连接点,其中连接点所在类型(类)已指定注解(在使用Spring AOP的时候,所执行的方法所在类型已指定注解)。

    @annotation: 限定匹配特定的连接点(使用Spring AOP的时候方法的执行),其中连接点(方法)的主题持有指定的注解。(在使用Spring AOP的时候,所执行的方法已指定注解)

     其中只有execution指示器是执行匹配的,其他的指示器都是用来限制匹配的,具体指示器使用方式如下:

    execution指示器:

    execution(<修饰符> <返回类型> <类路径> <方法名>(<参数列表>) <异常模式> )

      <修饰符>:类型修饰符,可选,如public、protected,*表示任意修饰符

      <返回类型>:必填,如void,String等,*表示任意类型

      <类路径>:类型表达式匹配(包名.类名),可选 “包名.*”表示包下所有类,“包名..*”表示包以及子包下所有的类

      <方法名>:方法名匹配,必填,*表示可以匹配任意方法

    (参数列表):参数匹配,必填,()表示没有参数,(..)表示匹配任意个数参数,(..,String)表示匹配最后一个参数是String类型方法,前面可以是任意参数

      <异常模式>:异常列表,可选

      使用示例:

    1:匹配com.sl.aop包下所有公共类型方法

    execution(public * com.sl.aop.*.*(..))

    2:匹配com.sl.aop包下所有以Order结尾的方法

    execution(* com.sl.aop.*.*Order(..))

    3:匹配com.sl.aop包下OrderService接口下所有无参方法

    execution(* com.sl.aop.OrderService.*())

    within 指示器:

    within(<类路径>)

    使用示例:

    1: com.sl.aop包以及子包下任意连接点

    within(com.sl.aop.OrderService.*)

    2: com.sl.aop包以及子包下OrderService类型及子类型的任意连接点

    within(com.sl.aop.OrderService+)

    其他指示器:

    //实现OrderService接口的代理对象的任意连接点(这里是AOP代理对象类型匹配)
    this(com.sl.aop.OrderSerivce)
    可以与excution组合使用
    @Before("execution(* com.sl.aop.*.*(..)) && this(com.sl.aop.OrderService)")
    
    //实现OrderService接口的目标对象的任意连接点(区别于this的代理对象)
    target(com.sl.aop.OrderSerivce)
    具体this和target的区别可参考CSDN这篇文章https://blog.csdn.net/yangshangwei/article/details/77861658
    
    //匹配使用了@Service注解的类(类上加注解,接口上加注解不起作用)
    @within(org.springframework.stereotype.Service)
    
    @Before("execution(* com.sl.aop.*.*(..)) && @within(org.springframework.stereotype.Service)")
    
    //匹配使用了@ Secure注解的方法(方法上加注解)
    @annotation(cn.javass.spring.chapter6.Secure )

    使用注解配置AOP

    示例代码:

    xml配置如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
               http://www.springframework.org/schema/beans/spring-beans.xsd
               http://www.springframework.org/schema/aop
               http://www.springframework.org/schema/aop/spring-aop.xsd
               http://www.springframework.org/schema/context
               http://www.springframework.org/schema/context/spring-context.xsd" >
        
        <!-- 组件扫描,扫描含有注解的类 -->
        <context:component-scan base-package="com.sl.aop"></context:component-scan>
        <!—启用aspectj注解 -->
        <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    </beans>

    java代码:

    @Component
    @Aspect // 定义切面
    public class OrderLogger {
    
        //定义切点函数
        
        // 前置通知 :在目标方法被调用之前调用该通知
        //@Before("execution(* com.sl.aop.*.*(..))")
        //@Before("execution(public * com.sl.aop.*.*(..))")
        //@Before("execution(* com.sl.aop.OrderService.*())")
        //@Before("execution(* com.sl.aop.*.*(..)) && this(com.sl.aop.OrderService)")
        @Before("execution(* com.sl.aop.*.*(..)) && @within(org.springframework.stereotype.Service)")
        public void beforeCreateOrder() {
            System.out.println("before create order");
        }
    
    //使用args处理通知中的参数
        @Before("execution(public * com.sl.aop.*.*(..)) && args(orderNumber,..)")
        public void beforeCreateOrder(String orderNumber) {
            System.out.println("before create order:"+orderNumber);
        }
    
        // 后置通知:在目标方法返回或抛异常后调用该通知
        @After("execution(* com.sl.aop.OrderServiceImpl.createOrder(..))")
        public void afterCreateOrder() {
            System.out.println("after create order");
        }
    
        // 返回通知:在目标方法正常返回后调用
        @AfterReturning("execution(* com.sl.aop.OrderServiceImpl.createOrder(..))")
        public void createOrderSuccess() {
            System.out.println("create order success");
        }
    
        // 异常通知:在目标方法抛出异常后调用
        @AfterThrowing("execution(* com.sl.aop.OrderServiceImpl.createOrder(..))")
        public void createOrderError() {
            System.out.println("create order error");
        }
    
        // 异常通知:在目标方法抛出异常后调用
        @Around("execution(* com.sl.aop.OrderServiceImpl.createOrder(..))")
        public void createOrderError(ProceedingJoinPoint jp) throws Throwable {
            System.out.println("around before create order");
            jp.proceed();
            System.out.println("around after create order");
        }
    }
    public interface OrderService {
    
        public void createOrder();
        
        public void createOrder(String orderNumber);   
    }
    
    //@Service
    @Component("orderServiceImpl")
    public class OrderServiceImpl implements OrderService {
        
        @Override
        public void createOrder() {
            System.out.println("creating order");
        }
        
        @Override
        public void createOrder(String orderNumber) {
            System.out.println("creating order:"+orderNumber);
        }
    }

    测试代码:

    @Test
     public void TestAspectAop() {
            ApplicationContext context = 
                    new ClassPathXmlApplicationContext("aoptextbean.xml");
            OrderService orderService =    (OrderService)context.getBean("orderServiceImpl");
     
    orderService.createOrder();
    System.out.println(
    "-----------------------");
    orderService.createOrder(
    "888"); }

    结果:

    使用XML配置AOP

     使用上面的代码,切面类中删除所以aop相关注解,通过xml配置来实现前置、后置等通知,示例代码:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
               http://www.springframework.org/schema/beans/spring-beans.xsd
               http://www.springframework.org/schema/aop
               http://www.springframework.org/schema/aop/spring-aop.xsd
               http://www.springframework.org/schema/context
               http://www.springframework.org/schema/context/spring-context.xsd" >
        
        <!-- 组件扫描,扫描含有注解的类 -->
        <context:component-scan base-package="com.sl.aop"></context:component-scan>
        <!-- 使用aspectj注解 -->
        <!-- <aop:aspectj-autoproxy></aop:aspectj-autoproxy> -->
        
        <bean id="orderLogger" class="com.sl.aop.OrderLogger"></bean>
        
        <aop:config>
            <!--1: 申明一个切面   多个切面时order指定优先级-->
            <aop:aspect ref="orderLogger" order="0">
                <!-- 2:定义通知  method:指定通知名  pointcut:指定切点-->
                <aop:before method="beforeCreateOrder" pointcut="execution(* com.sl.aop.OrderService.*())" />
                <!-- <aop:before method="beforeCreateOrder" pointcut="execution(public * com.sl.aop.*.*(String)) and args(orderNumber)" /> -->
                <aop:after method="afterCreateOrder" pointcut="execution(* com.sl.aop.OrderServiceImpl.createOrder(..))" />
                <aop:after-returning method="createOrderSuccess" pointcut="execution(* com.sl.aop.OrderServiceImpl.createOrder(..))" />
                <aop:after-throwing method="createOrderError" pointcut="execution(* com.sl.aop.OrderServiceImpl.createOrder(..))" />
                <aop:around method="createOrderAround" pointcut="execution(* com.sl.aop.OrderServiceImpl.createOrder(..))" />
            </aop:aspect>
        </aop:config>
        
    </beans>
    @Component
    public class OrderLogger {
    
        //定义切点函数
        
        // 前置通知 :在目标方法被调用之前调用该通知
        public void beforeCreateOrder() {
            System.out.println("before create order");
        }
        
        //使用args处理通知中的参数
        public void beforeCreateOrder(String orderNumber) {
            System.out.println("before create order:"+orderNumber);
        }
    
        // 后置通知:在目标方法返回或抛异常后调用该通知
        public void afterCreateOrder() {
            System.out.println("after create order");
        }
    
        // 返回通知:在目标方法正常返回后调用
        public void createOrderSuccess() {
            System.out.println("create order success");
        }
    
        // 异常通知:在目标方法抛出异常后调用
        public void createOrderError() {
            System.out.println("create order error");
        }
    
        // 异常通知:在目标方法抛出异常后调用
        public void createOrderAround(ProceedingJoinPoint jp) throws Throwable {
            System.out.println("around before create order");
            jp.proceed();
            System.out.println("around after create order");
        }
    
    }

    测试代码:

    @Test
        public void TestAspectAop() {
            ApplicationContext context = 
                    new ClassPathXmlApplicationContext("aoptextbean.xml");
            OrderService orderService =    (OrderService)context.getBean("orderServiceImpl");
            orderService.createOrder();
            System.out.println("-----------------------");
            orderService.createOrder("888");
        }

    运行结果:

    最后解释一下Spring中使用xml配置AOP的标签及其属性使用方式:

    <aop:config>                                              <!--AOP配置根节点  -->
         <aop:pointcut expression="" id=""/>                   <!--定义切点   expression切点表达式,比如 expression="execution(* com.sl.aop.*.*(..))", id:定义切点id="tempId"-->
         <aop:advisor advice-ref=""/>                          <!--定义通知器  -->
         <aop:aspect  ref="" order="">                                <!--定义切面  ref:切面引用(切面beanID) order:执行顺序,多个切面可以指定优先级-->
            <aop:before method="" pointcut="" pointcut-ref="" />      <!--前置通知  method:通知方法名,poincut:切点表达式,pointcut-ref:切点引用(切点id),如pointcut-ref="tempId",可以如其他通知引用相同切点-->
            <aop:after method="" pointcut="" pointcut-ref=""/>                    <!--后置通知 -->
            <aop:after-returning method="" pointcut="" pointcut-ref=""/>         <!--返回通知 -->
            <aop:after-throwing method="" pointcut="" pointcut-ref=""/>           <!--异常通知 -->
            <aop:around method="" pointcut="" pointcut-ref=""/>                   <!--环绕通知 -->
            <aop:declare-parents types-matching="" implement-interface=""/>       <!--引入额外接口 -->
            <aop:pointcut expression="" id=""/>                                   <!--定义切点  -->
         </aop:aspect>
    </aop:config>
  • 相关阅读:
    Eclipse Kepler安装WST Server Adapter后创建Server无Tomcat解决方法
    centos下Linux C语言MD5的使用
    解析JSON字符串
    切换view的动画
    设置菜单和工具条
    视图切换的几种方法
    scrollview 例子2
    UIScrollView
    iOS:翻页效果
    软件预构的艺术源码编译
  • 原文地址:https://www.cnblogs.com/ashleyboy/p/9033392.html
Copyright © 2011-2022 走看看