• Spring学习笔记-面向切面(AOP)-04


    什么是面向切面编程

    先大概了解一下部分术语

    横切关注点:软件开发中,散布于多出的功能称为横切关注点(cross-cutting concern),简单的可以描述为可以影响应用多处的功能,比如日志、安全。

    切面:切面能帮我们模块化横切关注点,如图所示,三个不同的模块,每个模块都是为特定业务服务,但是这些模块都需要类似的辅助功能,例如安全、事务管理。面向切面,可以使我们在一个地方定义通用功能,以声明的方式定义这个功能要以何种方式在何处使用,无需修改受影响的类,横切关注点可以被模块化为一个特殊的类,这些类被称为切面。

    AOP带来的好处:

      1、每个关注点集中于一个地方,而不是分散到多处代码。

      2、服务模块更简洁,因为它们只包含主要关注点(或核心功能)的代码,次要关注点的代码被转移到切面中了。

    AOP术语

     常见术语:通知(advice)、切点(pointcut)、连接点(point)

    1、通知(advice):切面的所做的工作被称为通知(什么时候做)

      通知定义了切面是什么以及何时使用,描述切面要完成的工作和何时执行这个工作。

      Spring切面可以应用5种类型的通知:

      1、前置通知(Before):在目标方法被调用之前调用通知;

      2、后置通知(After):在目标方法被调用之后调用通知;

      3、返回通知(After-returning):在目标方法成功执行之后调用通知;

      4、异常通知(After-throwing):在目标方法抛出异常后调用通知;

      5、环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义行为。

    2、连接点(Join point):插入切面的时机,在应用执行“过程中”能够插入切面的一个点(做的时机)

       应用可能有无数的时机应用通知,这些时机被称为连接点。这个点可以使调用方法时,抛出异常时,甚至是修改一个字段时,切面代码可以利用这些连接点插入到应用的正常流程中。

    3、切点(Poincut):插入切面的位置(在什么地方做)

      明确要织入的一个或多个连接点,通常使用明确的类和方法名称,或正则表达式匹配类和方法名,来指定切点。

    4、切面(Aspect):通知和切点的结合。(在什么时候什么地方做)

      通知和切点共同定义了切面的全部内容-----它是什么,在什么时候何处完成其功能。

    5、引入(Introduction):不修改仙有泪的情况下,向现有类引入新功能

      引入允许我们向现有类添加新方法或属性。

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

      切面在指定的连接点被织入到目标对象中,在目标对象的生命周期中有多个点可织入:

      1、编译期:切面在目标类编译时被织入,这种方式需要特殊的编译器,AspectJ的织入编译器就是以这种方式织入。

      2、类加载期:切面在目标类加载到JVM中被织入,此方式需要特殊的类加载器(ClassLoader),其可以在目标类被引入应用之前增强该类的字节码。AspectJ 5的加载时织入(LTW)就是这种方式织入的。

      3、运行期:切面在应用运行时的某个时刻被织入,一般情况下,织入切面时,AOP容器会为目标对象动态的创建一个代理对象,Spring AOP就是以这种方式织入的。

     通过切点来选择连接点

    Spring AOP中可以使用AspectJ切点表达式语言定义切点,如下为Spring AOP所支持的AspectJ切点指示器:

    编写切点

    首先写一个接口用于演示如何定义切点,如下接口可以代表任何类型的现场表演,如舞台剧、电影等。

    public interface Performance {
        public void perform();
    }

    假设我们向编写上面接口的perform()方法触发通知,如下展示了一个接口表达式

    execution()指示器选择Performance的perform方法,方法表达式以“ * ” 号开始,表示我们不关心方法的返回值类型,可以理解为一个通配符。

    *号之后指定了全限定的类名和方法名,对于方法的参数列表使用2个点好(..)表示切点选择任意参数签名的perform()方法。

     

    现在加入我们需要配置的切点仅匹配concert包,可使用within()指示器来限制匹配,如下图。

    使用“&&”操作符连接execution()和within()指示器,形成“与”(AND)的关系,(也可以使用||(或)、!(非)标识操作,也可以使用and、or、not来代替)

    在切点中选择bean

     除了表4.1所列的指示器外,Spring还引入了一个新的bean()指示器,它允许我们在切点表达式中使用bean的ID来标识bean。bean()使用bean ID或bean名称作为参数来限制切点只匹配特定的bean。例如,考虑如下的切点:在这里,我们希望在执行Performance的perform()方法时应用通知,但限定bean的ID为woodstock。在某些场景下,限定切点为指定的bean或许很有意义,但我们还可以使用非操作为除了特定ID以外的其他bean应用通知:在此场景下,切面的通知会被编织到所有ID不为woodstock的bean中。

    使用注解创建切面

    使用我们定义的Performance接口,作为切面中切点的目标对象,然后开始使用AspecJ注解定义切面

    定义切面

    如下Audience类使用AspectJ注解定义了一个切面

    @Aspect
    public class Audience {
        //前置通知 
        @Before("execution(** Performance.perform(..))")
        public void silenceCellPhones(){
            System.out.println("表演开始,观众就坐(silenceCellPhones)");
        }
        //前置通知
        @Before("execution(** Performance.perform(..))")
        public void takeSeats(){
            System.out.println("表演开始,手机调至静音(takeSeats)");
        }
        //返回通知
        @AfterReturning("execution(** Performance.perform(..))")
        public void applause(){
            System.out.println("表演结束,并且很精彩,观众鼓掌喝彩(applause)");
        }
        //异常通知
        @AfterThrowing("execution(** Performance.perform)")
        public void demandRefund(){
            System.out.println("表演失败,观众要求退款(demandRefund)");
        }
    }

    上面有多个重复的切点,可以使用@Pointcut注解进行优化,performance方法内容不重要,在这里的作用实际上只是一个标识,供@Pointcut注解依附,供其他通知注解使用,避免重复使用相同的切点表达式。

    @Aspect
    public class Audience2 {    
        @Pointcut("execution(** Performance.perform(..))")
        public void performance(){};
        //前置通知 
        @Before("performance()")
        public void silenceCellPhones(){
            System.out.println("表演开始,观众就坐(silenceCellPhones)");
        }
        //前置通知
        @Before("performance()")
        public void takeSeats(){
            System.out.println("表演开始,手机调至静音(takeSeats)");
        }
        //返回通知
        @AfterReturning("performance()")
        public void applause(){
            System.out.println("表演结束,并且很精彩,观众鼓掌喝彩(applause)");
        }
        //异常通知
        @AfterThrowing("execution(** Performance.perform)")
        public void demandRefund(){
            System.out.println("表演失败,观众要求退款(demandRefund)");
        }
    }

    现在定义了切点,但是如果就此而已那么Audience只会是Spring容器的一个bean,并不会视为一个切面,不会被Spring解析,也不会创建将其转换为切面的代理,需要配置以启用。

    JavaConfig方式:可在配置类级别上使用@EnableAspectJ-AutoProxy注解启用自动代理。

    //JavaConfig配置类
    @Configurable        
    @EnableAspectJAutoProxy    //启用自动代理
    @ComponentScan  //组件扫描
    public class ConcertConfig {
        @Bean
        public Audience audience(){
            return new Audience();
        }
    }

    XML方式:使用Spring  aop命名空间中的<aop:aspectj-autoproxy>元素。

    <!--启用AspectJ自动代理-->
    <aop:aspectj-autoproxy />
    <!--声明Audience bean-->
    <bean class= "concert.Audience"/>

     环绕通知

      能让你所编写的逻辑将被通知的目标方法完全包装起来,就像同时在一个通知方法中编写前置和后置通知。如下代码,@Around注解表明watchPerformance()方法会作为performance()切点的环绕通知,其中通知方法接受一个ProceedingJoinPoint类型的参数,这个参数不许要提供,因为要在通知中通过它调用被通知的方法,在通知方法完成工作之后,需要调用ProceedingJoinPoint的oriceed()方法,否则这个通知会阻塞被通知方法(目标方法)的调用。

    处理通知中的参数

    目前为止,所有的切面都没有任何参数,除了环绕通知,在如下通知代码中,切点表达式匹配带参数的目标方法,其中&& args(trackNumber)表示传递给playTrack()方法的int类型参数也会传递到通知方法中,参数中的trackNumberca也与通知方法签名中的参数相匹配,此

  • 相关阅读:
    C和指针学习笔记--第五章
    C和指针学习笔记--第四章
    C和指针学习笔记--第三章
    ipables常用命令
    linux网络设计与实现-----第一章
    iptables
    cJSON学习
    Makefile-更新函数库文件
    Makefile隐晦规则
    【Spark学习笔记】01-Spark简介
  • 原文地址:https://www.cnblogs.com/vice/p/9079737.html
Copyright © 2020-2023  润新知