• Spring 面向切面编程 第1关:使用前后置通知统计所有方法的执行时间


    目录

    任务描述

    相关知识

    AOP基本概念

    切点完整表达式

    五种通知

    实例解析

    编程要求

    测试说明

    参考代码


    任务描述

    Spring AOPSpring框架的核心技术之一,不幸的是,AOP 中许多概念不是特别直观,因此,本关卡将使用代码案例的方式讲解 AOP相关概念。

    本关任务:使用前后置通知统计出博客系统中博客类中每个业务方法的执行时长。

    相关知识

    AOP基本概念

    • Aspect切面): 切面由切点 (Pointcut) 和增强/通知 (Advice) 组成,它既包括了横切逻辑的定义、也包括了连接点的定义;

    • Joint point(连接点):能够被拦截的地方:Spring AOP 是基于动态代理的,所以是方法拦截的。每个成员方法都可以称之为连接点;

    • Pointcut(切点):具体定位的连接点,上面也说了,每个方法都可以称之为连接点,我们具体定位到某一个方法就成为切点;

    • Advice(通知/增强):表示添加到切点的一段逻辑代码,并定位连接点的方位信息。简单来说就定义了是干什么的,具体是在哪干;

    • Spring AOP提供了5Advice类型给我们,分别是:前置(Before)、后置(After)、返回(AfterReturning)、异常(AfterThrowing)、环绕(Around);

    • Target(目标对象):织入Advice的目标对象;

    • Weaving(织入):将增强/通知添加到目标类的具体连接点上的过程。

    切点完整表达式

    切点的完整表达式如下:

    execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)throws-pattern?)

    参数参数说明
    modifiers-pattern修饰符 可选 public private protected
    declaring-type-pattern方法的声明类型
    name-patterm方法名称类型,例 set* 则表示以set开头的所有的方法名称
    param-pattern参数匹配:(..) 表示任意多个参数,每个参数任意多个类型,(*,String) 表示两个参数,第一个是任意类型,第二个是String
    throws-pattern异常的匹配模式
    ret-type-pattern返回类型 必选 * 代表任意类型

    五种通知

    前置通知:在连接点前面执行,前置通知不会影响连接点的执行;

    后置通知:在连接点执行完成后执行,不管是正常执行完成,还是抛出异常,都会执行返回通知中的内容;

    异常返回通知:在连接点抛出异常后执行;

    正常返回通知:在连接点正常执行完成后执行,如果连接点抛出异常,则不会执行;

    环绕通知:最为强大的通知,它能够让你编写的逻辑将被通知的目标方法完全包裹起来。实际上就像在一个通知方法中同时编写前置通知和后置通知。定义通知的时候在通知方法中添加了入参ProceedingJoinPoint,这个参数是必须写的。因为需要在通知中使用ProceedingJoinPoint.proceed()调用目标方法。

    1. @Around("切点")
    2. public void around(ProceedingJoinPoint point) throws InterruptedException {
    3. //目标方法执行前
    4. System.out.println(" 环绕通知前");
    5. try {
    6. //执行目标方法
    7. point.proceed();
    8. } catch (Throwable throwable) {
    9. throwable.printStackT\frace();
    10. }
    11. //目标方法执行后
    12. System.out.println(" 环绕通知后");
    13. }

    实例解析

    看完AOP基本概念是不是感觉有点晦涩难懂?下面就让我们以实例讲解一下,比如企业招聘的时候,面试官只需要做面试打分相关的核心功能即可,简历筛选和入职相关步骤交给人力资源就可以了,又比如博客系统中用户(User类)有个修改用户名和密码的核心功能,但是在修改用户密码之前,我们会校验该用户是否具有权限,检验权限的地方有很多,我们把这一功能独立出来当成切面类,而User类只需实现修改用户名和密码方法即可,下面就让我们看一下具体是怎么实现的。

    • 创建目标类(待增强的目标对象):
    1. package Educoder;
    2. import org.springframework.stereotype.Component;
    3. @Component("User")
    4. public class User {
    5. public void updateNameAndPw() {
    6. System.out.println("用户-修改用户名和密码");
    7. }
    8. }
    • 创建切面类(包含切点execution(* Educoder.User.updateNameAndPw())和通知/增强before()方法):
    1. package Educoder;
    2. import org.aspectj.lang.annotation.*;
    3. import org.springframework.stereotype.Component;
    4. @Component("Authority")
    5. @Aspect
    6. public class Authority {
    7. //前置通知,括号内切点表达式的意思就是`Educoder`包下`User`类下的`updateNameAndPw()`方法
    8. @Before("execution(* Educoder.User.updateNameAndPw())")
    9. public void before() {
    10. System.out.println("权限校验");
    11. }
    12. }
    • applicationContext.xml中配置自动注入:
    1. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    2. xmlns:context="http://www.springframework.org/schema/context"
    3. xmlns:aop="http://www.springframework.org/schema/aop"
    4. xsi:schemaLocation="http://www.springframework.org/schema/beans
    5. http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context
    6. http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    • 编写测试代码:
    1. package Educoder;
    2. import org.springframework.context.ApplicationContext;
    3. import org.springframework.context.support.ClassPathXmlApplicationContext;
    4. public class Test {
    5. public static void main(String[] args) {
    6. //创建Spring的IOC容器对象
    7. ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
    8. //从IoC的容器中获取Bean实例
    9. Interviewer spr1 = applicationContext.getBean("User", Interviewer.class);
    10. //调用updateNameAndPw()方法
    11. spr1.updateNameAndPw();
    12. }
    13. }
    • 输出结果如下: 权限校验 用户-修改用户名和密码

    编程要求

    请仔细阅读右侧代码,根据方法内的提示,在Begin - End区域内进行代码补充,把目标类(BlogService)中的所有方法的执行时间统计出来,目标类在右侧代码文件区域可见。 后台IoC容器对象会自动获取目标类实例并调用目标类中所有方法。

    测试说明

    补充完代码后,点击测评,平台会对你编写的代码进行测试,当你的结果与预期输出一致时,即为通过。 预期输出:

    当前时间2019.1.1 00:00:00

    业务功能一

    当前时间2019.1.1 00:01:00,执行耗时60000

    当前时间2019.1.1 00:00:00

    业务功能二

    当前时间2019.1.1 00:01:00,执行耗时60000


    开始你的任务吧,祝你成功!

    参考代码

    1. package Educoder;
    2. import org.aspectj.lang.ProceedingJoinPoint;
    3. import org.aspectj.lang.annotation.*;
    4. import org.springframework.stereotype.Component;
    5. import java.text.ParseException;
    6. import java.text.SimpleDateFormat;
    7. @Component("BlogAdvice")
    8. @Aspect
    9. public class BlogAdvice {
    10. //定义切点
    11. @Pointcut("execution(* Educoder.BlogService.service*(..))")
    12. public void My() {
    13. }
    14. //定义前置通知,输出当前时间2019.1.1 00:00:00
    15. @Before("My()")
    16. public void before() {
    17. System.out.println("当前时间2019.1.1 00:00:00");
    18. }
    19. //定义后置通知,输出当前时间2019.1.1 00:01:00,执行耗时60000
    20. @After("My()")
    21. public void after() {
    22. System.out.println("当前时间2019.1.1 00:01:00,执行耗时60000");
    23. }
    24. }

     

  • 相关阅读:
    这就叫速度,并发编程深度解析实战七天杀上 GitHub 榜首
    Javascript知识【基础语法】
    基于滑模预测控制的海底采矿车轨迹跟踪算法
    QT tcp通信
    Vue父组件 和 子组件 相互调用修改参数的问题-解决
    FME之PythonCaller遍历(循环)列表(数组)
    《QDebug 2022年6月》
    配置与管理Samba服务器
    408 | 【计网】第二章 物理层 回顾
    Debian | Vscode 安装与配置 C 环境
  • 原文地址:https://blog.csdn.net/ycq0_9/article/details/127677300