Spring AOP初步总结(一)

学习AOP有段时间了,一直没空总结一下,导致有些知识点都遗忘了,之后会把以前学过的Spring核心相关的知识点总结一轮...

先大体介绍下Spring AOP的特点(均摘自"Spring in action第四版"):

  Spring支持了AOP,另外还有很多实现了AOP的技术,例如AspectJ,它补充了Spring AOP框架的功能,他们之间有着大量的协作,而且Spring AOP中大量借鉴了AspectJ项目,Spring AOP相对粗粒度,而AspectJ提供更强大更细粒度的控制,以及更丰富的AOP工具集,但需要额外的语法学习;

  Spring借鉴了AspectJ的切面,以提供注解驱动的AOP,编程模型几乎与编写成熟的AspectJ注解切面完全一致。这种AOP风格的好处在于能够不使用XML来完成功能。Spring AOP构建在动态代理之上,因此,Spring对AOP的支持局限于方法拦截;如果你对AOP的需求超过了建党方法调用(如构造器或属性拦截),那么你需要AspectJ来实现切面;

  Spring提供了4种类型的AOP支持:

    基于代理的经典Spring AOP;

    纯POJO切面;

    @AspectJ注解驱动的切面;

    注入式AspectJ切面(适用于Spring各版本);

复习下AOP相关的术语(概念):

  1.通知(Advice):

    切面的工作被称为通知,通知定义了切面是什么以及何时使用;

    分5类:

前置通知(Before) 在目标方法被调用之前调用通知功能;
后置通知(After) 在目标方法完成之后调用通知,此时不会关心方法的输出是什么;
返回通知(After-returning) 在目标方法成功执行之后调用通知;
异常通知(After-throwing) 在目标方法抛出异常后调用通知;
环绕通知(Around)
通知包裹了被通知的方法,在被通知的方法调用之前和调用之后
执行自定义的行为。

  2.连接点(Join point)

    应用通知的时机被称为连接点;连接点是在应用执行过程中能够插入切面的一个点。这个点可以是调用方法时、抛出异常时、甚至修改一个字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为;

  3.切点(Poincut)

    切点定义了切面的在何处实施;通常使用明确的类和方法名称,或是利用正则表达式定义所匹配的类和方法名来指定;

  4.切面Aspect

    是通知(何时,做什么)和切点(何处)的结合;

  5.引入(Introduction)

    允许我们向现有的类添加新方法或新属性;

  6.织入(Weaving)

    把切面应用到目标对象并创建新的代理对象的过程

    3种织入时机:

编译期 在目标类编译时被织入,AspectJ的织入编译器就是以这种方式织入的
类加载期 在目标类加载到JVM中时被织入,AspectJ5的加载时织入(load-time weaving,LTW)支持
运行期 在运行的某个时刻被织入,一般情况下AOP容器会为目标对象动态创建一个代理对象,Spring AOP就支持这种方式

    

Spring AOP是基于动态代理的:

  通过在代理类中包裹切面,Spring在运行期把切面织入到Spring管理的bean中。如下图所
示,代理类封装了目标类,并拦截被通知方法的调用,再把调用转发给真正的目标bean。当
代理拦截到方法调用时,在调用目标bean方法之前,会执行切面逻辑。

  

编写切点(若干图例了解切点表达式):

   

  

    我们使用execution()指示器选择Performance的perform()方法。方法表达式以“*”号
开始,表明了我们不关心方法返回值的类型。然后,我们指定了全限定类名和方法名。对于
方法参数列表,我们使用两个点号(..)表明切点要选择任意的perform()方法,无论该
方法的入参是什么。

  

  我们使用了“&&”操作符把execution()和within()指示器连接在一起形成与(and)关系(切点必须匹配所有的指示器)。类似地,我们可以使用“||”操作符来标识或(or)关系,而使用“!”操作符来标识非(not)操作。因为“&”在XML中有特殊含义,所以在Spring的XML配置里面描述切点时,我们可以使用and来代替“&&”。同样,or和not可以分别用来代替“||”和“!”。

  

  在这里,我们希望在执行Performance的perform()方法时应用通知,但限定bean的ID为woodstock。

下面正式开始创建切面:

import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class Audience {
    @Before("execution(** concert.Performance.perform(..))")
    public void silenceCellPhones() {
        System.out.println("Silencing cell phones");
     }

    @Before("execution(** concert.Performance.perform(..))")
    public void takeSeats() {
        System.out.println("Taking seats");
     }

      @AfterReturning("execution(** concert.Performance.perform(..))")
    public void applause() {
        System.out.println("CLAP CLAP CLAP...");
     }

    @AfterThrowing("execution(** concert.Performance.perform(..))")
    public void demandRefund() {
        System.out.println("Demanding a refund");
     }
}

  Audience类使用@AspectJ注解进行了标注。该注解表明Audience不仅仅是一个POJO,还是一个切面。Audience类中的方法都使用注解来定义切面的具体行为。Audience有四个方法,定义了一个观众在观看演出时可能会做的事情。在演出之前,观众要就坐(takeSeats())并将手机调至静音状态(silenceCellPhones();

  AspectJ提供了五个注解来定义通知

这样,一个切面就定义好了,但我们四个注解使用的表达式都一样,可以用@Pointcut注解提供表达式的引用:

  1.提供一个空方法,在上面增加注解:@Pointcut("execution(** concert.Performance.perform(..))");

  2.其它注解引用,例如: @Before("上面定义的空方法名()");

光这样还不够,下面要装配Audience类成为一个bean:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@EnableAspectJAutoProxy //气筒AspectJ自动代理
@ComponentScan
public class ConcertConfig { //bean的配置类

    @Bean
    public Audience audience() { //配置Audience类成为一个Bean
        return new Audience();
    }
}

以上就实现了前置后置分离通知的切面;

接下在介绍另一种:环绕通知的写法(逻辑不复杂的时候建议用这种方式,更加直观,一旦逻辑复杂,可读性会很差):

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Around;
import org.aspectj.lang.Aspect;
import org.aspectj.lang.Pointcut;

@Aspect
public void Audience {
    @Pointcut("execution(** concert.Performance.perform(..))")
    public void performance(){}

    @Around("performance()")
    public void watchPerformance(PerceedingJoinPoint jp){
        try {
            System.out.println("Silencing cell phones");
            System.out.println("Taking seats");
            jp.proceed();                   //调用被通知的方法(可以多次调用)
            System.out.println("CLAP CLAP CLAP...");
        } catch (Throwable e) {
            System.out.println("Demanding a refund");
        }
    }
}

以上总结的是不带参数构造切面的情况

接下来介绍如果在通知上符加参数的情况:

import java.util.HashMap;
import java.util.Map;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class TrackCounter {
    private Map<Integer, Interger> trackCounts =
        new HashMap<Integer, Integer>(); 

    @Pointcut(
            "execution(* soundsystem.CompactDisc.playTrack(int))" +
            "&& args(trackNumber)")
    public void trackPlayed(int trackNumber) {}

    @Before("trackPlayed(trackNumber)")
    public void countTrack(int trackNumber){
        int currentCount = getPlayCOunt(trackNumber);
        trackCounts.put(trackNumber, currentCount + 1);
    }

    public int getPlayCount(int trackNumber) {
        return trackCOunts.containKey(trackNumber) ?
            trackCounts.get(trackNumber) : 0;
    }
}

图例来解释表达式:

原文地址:https://www.cnblogs.com/Joey44/p/10025188.html

时间: 2024-10-31 16:57:30

Spring AOP初步总结(一)的相关文章

Spring AOP初步总结(三)

最近遇到一个新需求:用户多次点击提交订单发生多次扣款,一开始准备配置数据库事务,但后来发现这种方法白白浪费很多资源,就改为利用接口上的切面对请求做拦截,并将当前登陆的用户存进Redis缓存,废话不说了直接上代码: AOP的应用(模拟请求拦截器): /** * @author YHW * @ClassName: ApiMemberAspect * @Description: * @date 2019/3/21 8:54 */ @Aspect @Configuration public aspect

深入理解Spring AOP之二代理对象生成

深入理解Spring AOP之二代理对象生成 spring代理对象 上一篇博客中讲到了Spring的一些基本概念和初步讲了实现方法,当中提到了动态代理技术,包含JDK动态代理技术和Cglib动态代理 动态代理这部分我有过一篇博客介绍:动态代理,想深入了解的朋友能够看一看,再回想一下,Spring中怎样区分採用JDK动态代理和CGlib动态代理: 假设目标对象的实现类实现了接口.Spring AOP 将会採用 JDK 动态代理来生成 AOP 代理类: 假设目标对象的实现类没有实现接口,Spring

关于 Spring AOP (AspectJ) 你该知晓的一切

[版权申明]未经博主同意,谢绝转载!(请尊重原创,博主保留追究权) http://blog.csdn.net/javazejian/article/details/54629058 出自[zejian的博客] 关联文章: 关于Spring IOC (DI-依赖注入)你需要知道的一切 关于 Spring AOP (AspectJ) 你该知晓的一切 本篇是年后第一篇博文,由于博主用了不少时间在构思这篇博文,加上最近比较忙,所以这篇文件写得比较久,也分了不同的时间段在写,已尽最大能力去连贯博文中的内容

关于 Spring AOP (AspectJ) 该知晓的一切

关联文章: 关于Spring IOC (DI-依赖注入)你需要知道的一切 关于 Spring AOP (AspectJ) 你该知晓的一切 本篇是年后第一篇博文,由于博主用了不少时间在构思这篇博文,加上最近比较忙,所以这篇文件写得比较久,也分了不同的时间段在写,已尽最大能力去连贯博文中的内容,尽力呈现出简单易懂的文字含义,如文中有错误请留言,谢谢. OOP的新生机 OOP新生机前夕 神一样的AspectJ-AOP的领跑者 AspectJ的织入方式及其原理概要 基于Aspect Spring AOP

Spring AOP中的JDK和CGLib动态代理哪个效率更高?

一.背景 今天有小伙伴面试的时候被问到:Spring AOP中JDK 和 CGLib动态代理哪个效率更高? 二.基本概念 首先,我们知道Spring AOP的底层实现有两种方式:一种是JDK动态代理,另一种是CGLib的方式. 自Java 1.3以后,Java提供了动态代理技术,允许开发者在运行期创建接口的代理实例,后来这项技术被用到了Spring的很多地方. JDK动态代理主要涉及java.lang.reflect包下边的两个类:Proxy和InvocationHandler.其中,Invoc

Spring AOP——Spring 中面向切面编程

前面两篇文章记录了 Spring IOC 的相关知识,本文记录 Spring 中的另一特性 AOP 相关知识. 部分参考资料: <Spring实战(第4版)> <轻量级 JavaEE 企业应用实战(第四版)> Spring 官方文档 W3CSchool Spring教程 易百教程 Spring教程 一.AOP--另一种编程思想 1.1 什么是 AOP AOP (Aspect Orient Programming),直译过来就是 面向切面编程.AOP 是一种编程思想,是面向对象编程(

Spring 框架学些(二)Spring AOP

关于AOP AOP,面向切面编程是OOP之后出现的概念(大概). 面向对象基本上就是针对类来设计代码,类中定义方法,逻辑中调用不同的类中不同的方法构成业务. 切面 而面向切面中的切面到底是什么.在业务逻辑中,我们会分很多不同的模块,也有不同的类,而这些类的一些方法中,有一些共性功能.比如认证.日志.限流等功能,在各个模块都需要,那每一个功能可以被认为是一个切面. 类似一个三明治,面包.火腿.菜叶.番茄等等每一层都是一个功能模块,一刀切下去这个切面贯穿整个三明治各层,这一刀就形成一个切面,而这个切

细说Spring——AOP详解(动态代理实现AOP)

前言 嗯,我应该是有一段实现没有写过博客了,在写完了细说Spring——AOP详解(AOP概览)之后,我发现我不知道该怎么写AOP这一部分,所以就把写博客这件事给放下了,但是这件事情又不想就这么放弃,所以今天我仔细思考了一下,决定还是要克服困难,我仔细的想了一下怎么讲解AOP实现这一部分,然后我决定由浅入深的讲解动态代理,然后用动态代理实现一个简单的AOP,感觉这样能够让人对AOP的原理有一个比较深刻的认识,希望能帮到大家.而且最近学习又组建了ACM比赛的队伍,虽然已经要大三了,按理来说应该一心

Spring框架之Spring AOP

一.基于注解管理的AOP 1.Spring配置文件 <!-- 配置自动扫描包,自动扫描Bean组件,切面类 --> <context:component-scan base-package="com.zhoujian.spring.anno,com.zhoujian.spring.test"> <!-- <context:include-filter type="annotation" expression="org.a