Spring系列之(八)Spring中的AOP

Spring中的AOP

通过配置(XML/注解)实现AOP

1. AOP相关术语

1.1 连接点

方法,Service接口中的所有方法

1.2 切入点

方法,Service接口中被增强过的方法

Tip:所有的切入点都是连接点

1.3 通知

方法,通常,具有增强功能的方法会放到一个类中,该类中所有用于增强的方法就被称为通知

分类:

  1. 前置通知
  2. 后置通知
  3. 异常通知
  4. 最终通知
  5. 环绕通知

在这里插入图片描述

1.4 目标对象

被代理对象

1.5 织入

过程,把增强方法应用到被代理对象来创建新的代理对象的过程

1.6 代理

代理对象

1.7 切面

描述通知是在切入点执行的具体什么时间点执行的,如切入点之前执行切入点之后执行切入点发生异常时执行....标红的部分就是切面

2. Spring AOP在实际项目开发中要做的事

开发阶段:

  1. 编写核心业务代码,实现业务功能,大部分开发人员都可做
  2. 抽取公共代码,制作成通知(开发最后阶段做),高级开发可做
  3. 在配置文件中,配置切面,高级开发可做

运行阶段:
Spring框架监控切入点的执行,一旦监控到切入点被运行,使用代理机制,动态创建目标对象的代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑运行。

3. Spring基于XML的AOP配置步骤

  1. 导入aop约束
    在这里插入图片描述
    参考网址:Spring Framework 中文文档

  2. 创建业务层接口实现类的Bean对象
    在这里插入图片描述

  3. 创建通知类的Bean对象
    在这里插入图片描述

  4. 使用<aop:config>标签表明开始AOP的配置

  5. 使用<aop:aspect>标签表明配置切面
    id属性:切面的唯一标识
    ref属性:指定通知类Bean的id

  6. <aop:aspect>标签内部配置通知的类型
    <aop:before>:前置通知,在切入点之前执行
    method属性:指定通知类中哪个方法用于前置通知
    pointcut属性:指定切入点表达式,用于指定对业务层中的哪些方法进行增强,也就是设置的通知是用在哪个方法上的
    <aop:after-returning>:后置通知,在切入点正常执行之后执行,后置通知和异常通知只能执行一个
    method属性:指定通知类中哪个方法用于后置通知
    pointcut属性:指定切入点表达式,用于指定对业务层中的哪些方法进行增强,也就是设置的通知是用在哪个方法上的
    <aop:after-throwing>:异常通知,在切入点执行产生异常之后执行
    method属性:指定通知类中哪个方法用于异常通知
    pointcut属性:指定切入点表达式,用于指定对业务层中的哪些方法进行增强,也就是设置的通知是用在哪个方法上的
    <aop:after>:最终通知,无论切入点是否正常执行都会在切入点之后执行
    method属性:指定通知类中哪个方法用于最终通知
    pointcut属性:指定切入点表达式,用于指定对业务层中的哪些方法进行增强,也就是设置的通知是用在哪个方法上的
    <aop:around>:环绕通知
    method属性:指定通知类中哪个方法用于环绕通知
    pointcut属性:指定切入点表达式,用于指定对业务层中的哪些方法进行增强,也就是设置的通知是用在哪个方法上的

​ 【环绕通知好像是完全自己来写各种通知】

切入点表达式

1. 关键字:execution(表达式)
2. 构成:访问修饰符 返回值 包名.包名....类名.方法名(参数列表)
3. 标准的表达式举例:

public void com.itheima.service.impl.AccountServiceImpl.saveAccount(int)
访问修饰符可以省略
void com.itheima.service.impl.AccountServiceImpl.saveAccount(int)
返回值可以使用通配符,表示任意返回值
*&nbsp;com.itheima.service.impl.AccountServiceImpl.saveAccount(int)
包名可以使用通配符,表示任意包,但是有几级包,就需要写几组 \*.
\*&nbsp;\*.\*.\*.\*.AccountServiceImpl.saveAccount(int)
包名可以使用. .表示当前包及其子包
\*&nbsp;\*. .AccountServiceImpl.saveAccount(int)
类名和方法名都可以使用\*来实现通配
\*&nbsp;\*. .\*.\*(int)
参数列表:可以直接写数据类型,基本类型写名称,引用类型写`包名.类名`的方式,可以使用通配符表示任意类型,但是必须有参数;可以使用. .表示有无参数均可,有参数可以是任意类型
\*&nbsp;\*. .\*.\*(\*)
\*&nbsp;\*. .\*.\*(. .)

4. 全通配写法
   \*&nbsp;&nbsp;*. . * . *(. .)
5. 实际开发中切入表达式的通常写法

\*&nbsp;com.itheima.service.impl.\*.\*(. .)
  1. 通用化切入点表达式(抽取出切入点表达式,使用时通过id调用)
    <aop:pointcut>标签:用于定义切入点表达式,可供调用
    id属性:表达式的唯一标识
    expression属性:表达式
    <aop:pointcut>标签的位置:位于<aop:aspect>标签内部,只能用于当前切面;位于<aop:config>标签内部,可以用于所有切面
    在这里插入图片描述
    Tip:<aop:pointcut>标签放在<aop:config>标签内部,必须放在<aop:aspect>标签之前

4. Spring基于XML的AOP实例

4.1 pom中导入坐标

在这里插入图片描述

4.2 业务层接口和实现类

IAccountService

package com.itheima.service;

/**
 * @author 商务小本本
 */
public interface IAccountService {

    void selectAccount();

    void saveAccount(int account);

    int updateAccount();

    int deleteAccount(int account);
}

AccountServiceImpl实现类

package com.itheima.service.impl;

import com.itheima.service.IAccountService;

/**
 * @author 商务小本本
 */
public class AccountServiceImpl implements IAccountService {
    public void selectAccount() {
        System.out.println("selectAccount方法");
    }

    public void saveAccount(int account) {
        System.out.println("saveAccount方法");
    }

    public int updateAccount() {
        System.out.println("updateAccount方法");
        return 0;
    }

    public int deleteAccount(int account) {
        System.out.println("deleteAccount方法");
        return 0;
    }
}

4.3 通知所在的类

package com.itheima.logger;

/**
 * @author 商务小本本
 */
public class Logger {

    void beforeAdvice(){
        System.out.println("前置通知");
    }

    void afterReturnAdvice(){
        System.out.println("后置通知");
    }

    void afterThrowAdvice(){
        System.out.println("异常通知");
    }

    void afterAdvice(){
        System.out.println("最终通知");
    }
}

4.4 配置文件

  1. 引入AOP约束
<?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"
       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">
</beans>
  1. 创建Service对象
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"></bean>
  1. 创建通知类对象
<bean id="logger" class="com.itheima.logger.Logger"></bean>
  1. 配置切面
<aop:config>
        <aop:pointcut id="pt1" expression="execution(void com.itheima.service.impl.AccountServiceImpl.saveAccount(..))"/>
        <aop:aspect id="loggerAdvice" ref="logger">
            <aop:before method="beforeAdvice" pointcut-ref="pt1"></aop:before>
            <aop:after-returning method="afterReturnAdvice" pointcut-ref="pt1"></aop:after-returning>
            <aop:after-throwing method="afterThrowAdvice" pointcut-ref="pt1"></aop:after-throwing>
            <aop:after method="afterAdvice" pointcut-ref="pt1"></aop:after>
        </aop:aspect>
    </aop:config>

4.5 测试

import com.itheima.service.IAccountService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AopTest {

    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
        IAccountService as = applicationContext.getBean("accountService", IAccountService.class);

        as.saveAccount(1);
    }
}

在这里插入图片描述

5. 环绕通知

Spring框架提供的一种可以在代码中手动控制增强方法何时执行的方式

Spring框架提供了一个接口,ProceedingJoinPoint,该接口的proceed方法相当于明确调用切入点方法,该接口可以作为环绕通知的方法参数,程序执行时,Spring框架会提供该接口的实现类供我们使用。

继续补充Spring基于XML的AOP实例的环绕通知部分

  1. 通知类中增加环绕通知,环绕通知的返回值要与切入点的返回值保持一致
Object arroundAdvice(ProceedingJoinPoint proceedingJoinPoint){
    Object result = null;
    try {
        Object[] args = proceedingJoinPoint.getArgs();
        System.out.println("前置通知");
        result = proceedingJoinPoint.proceed(args);
        System.out.println("后置通知");

        return result;
    }catch (Throwable t){
        System.out.println("异常通知");
        throw new RuntimeException(t);
    }finally {
        System.out.println("最终通知");
    }
}
  1. bean.xml中配置环绕通知
    在这里插入图片描述
  2. 测试类
import com.itheima.service.IAccountService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AopTest {

    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
        IAccountService as = applicationContext.getBean("accountService", IAccountService.class);

        as.updateAccount();
    }
}
  1. 测试结果
    在这里插入图片描述

6. Spring基于注解的AOP实例

将上面基于XML的实例改为基于注解的实例

  1. bean.xml中引入注解相关的名称空间和约束
    在这里插入图片描述

  2. bean.xml中配置包扫描(扫描注解)和注解AOP支持
    在这里插入图片描述

  3. 使用注解配置Service创建和通知了创建
    在这里插入图片描述
    在这里插入图片描述
    Tip:Logger类不属于Web三层架构的任意一层,所以使用注解@Component
    作用等同于
    在这里插入图片描述

  4. 通用化切入点表达式的注解配置
    在这里插入图片描述
    作用等同于
    在这里插入图片描述

  5. 切面的注解配置
    在这里插入图片描述
    作用等同于
    在这里插入图片描述

  6. 测试类不变,测试结果如下
    在这里插入图片描述

  7. 接下来是配置环绕通知
    在这里插入图片描述
    作用等同于
    在这里插入图片描述
    Tip:注解AOP中尽量使用环绕通知,其他通知类型的执行顺序可能会出问题

7. Spring纯注解(不存在bean.xml)的AOP

配置类中使用注解@EnableAspectJAutoProxy
作用等同于
在这里插入图片描述

posted @   刘二水  阅读(68)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示