当前位置: 首页 > article >正文

Spring面向切面编程

目录

1.AOP概述及Spring AOP实现原理

AOP概述 

AOP的应用场景 

 AOP的作用

Spring AOP概述

 Spring AOP的实现原理

 Spring AOP中Advice的分类

 2. 通过xml配置实现AOP

实现步骤:

 新增模块:

导入相关依赖:

新增实体类User 

 新增业务类UserService

定义切面类LogAspect 

 添加Spring核心配置文件

AOP配置说明 

注册切面类

配置切面

Spring配置文件中完成切面配置

编写测试类,验证效果

运行结果:

当前的目录结构:

​编辑 

3. 通过注解实现AOP

Spring AOP注解

 通过以下注解来定义通知 

用例设计(采用注解):

 实现步骤:

新增模块:

导入相关依赖: 

新增实体类User 

新增业务类UserService 

定义切面类LogAspect 

如何获取连接点信息 

@AfterReturning 

@Around

Spring配置文件中配置AspectJ自动代理 

编写测试类 

 运行结果:

当前的目录结构如下: 


1.AOP概述及Spring AOP实现原理

AOP概述 

AOP (Aspect Oriented Programming) 面向 切面编程
AOP 是一种编程的 思想
AOP 是对 OOP ( Object Oriented Programming ,面向对象 编程 ) 补充和 完善
利用 " 横切 " 技术 ,剖解开封装的对象内部,并将那些 与业务无关 ,但却 影响多 个类的 公共行为 封装到一个 可重用模块 ,并将其命名 Aspect 切面
作用 AOP 可以减少系统的重复代码,降低模块之间的耦合度,并有利于提升项目的可操作性和 可维护性

AOP的应用场景 

日志记录 :定义切面在方法执行前、执行后或异常抛出时记录日志
事务处理 :定义切面在方法开始前开启事务,在方法执行完毕后提交事务,在方法出现异常时回滚事务。
安全控制 :定义切面在方法执行前进行权限判断,如果用户没有权限,则抛出异常或转向到错误页面,以防止未经授权的访问。
缓存控制 :定义切面在方法执行前查询缓存中是否有数据,如果有则返回,否则执行方法并将方法返回值存入缓存中。
异常处理 :定义切面在方法执行过程中,如果出现异常,则进行 异常处理

 AOP的作用

不改变原有代码前提下,增加新功能

横切关注点:跨越应用程序多个模块。与业务逻辑无关处设置需关注部分,就是横切关注点。如日志,安全,缓存,事务等等

切面(ASPECT):横切关注点被模块化的特殊对象。即,它是一个类。

通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。

目标(Target):被通知对象。

代理(Proxy):向目标对象应用通知之后创建的对象。

切入点(PointCut):切面通知 执行的 “地点”的定义。

连接点(JointPoint):与切入点匹配的执行点。

Spring AOP概述

Spring AOPSpring框架中的一个模块,是基于AOP思想通过整合AspectJ框架实现的面向切面编程技术

AspectJ是一个面向切面编程的具体实现框架,定义了AOP的语法

 Spring AOP的实现原理

Spring AOP是基于动态代理技术实现的

在程序运行过程中,SpringAOP内部会基于JDK动态代理技术和cglib动态代理技术为目标对象生成代理对象

程序调用目标对象方法时,不是直接调用目标对象方法,而是先调用代理对象的方法,再调用目标对象的方法,从而对目标方法进行增强

 

 Spring AOP中Advice的分类

Spring AOP 通过 Advice 定义横切逻辑
支持 5 种类型的 Advice:

 

 2. 通过xml配置实现AOP

基于 Spring AOP 技术 在项目中配置 切面 实现 日志记录 的功能。
日志记录功能的具体要求如下:

1)当应用程序调用业务逻辑层组件方法之前,在控制台中打印出“开始调用方法

2)当应用程序调用业务逻辑层组件方法之后,在控制台中打印出“结束调用方法

3)当应用程序调用业务逻辑层组件方法出现了异常,在控制台中打印出“调用方法出现异常

实现步骤:

新建项目模块
整合 Spring AOP 框架 依赖
定义 切面类以及通知 方法
Spring 配置文件中完成 切面配置
运行测试类完成 切面测试

 新增模块:

打开上节的项目 spring6
新增模块 spring- aop -xml

 

导入相关依赖:

修改模块 spring- aop -xml pom.xml ,导入依赖
引入 spring-context 依赖 后,根据依赖的传递性,会自动引入 spring- aop 依赖
引入 Spring AspectJ 框架的整合依赖 spring-aspects ,因为我们要使用 AspectJ 框架的语法来进行配置切面

 xml配置如下:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.flowerfog</groupId>
        <artifactId>Spring6</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <artifactId>spring-aop-xml</artifactId>
    <packaging>war</packaging>
    <name>spring-aop-xml Maven Webapp</name>
    <url>http://maven.apache.org</url>
    <dependencies>
        <!--spring context依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>6.0.2</version>
        </dependency>
        <!--spring和AspectJ整合依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>6.0.2</version>
        </dependency>
        <!--junit5测试-->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>5.3.1</version>
        </dependency>
        <!--lombok插件-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.30</version>
        </dependency>
    </dependencies>
    <build>
        <finalName>spring-aop-xml</finalName>
    </build>
</project>

新增实体类User 

package org.flowerfog.pojo;

import lombok.Data;

@Data
public class User {
    private Integer id;
    private String name;
    private String account;
    private String password;
    private Integer status;
}

 新增业务类UserService

package org.flowerfog.Service;


import org.flowerfog.pojo.User;

public class UserService {
    public void add(User user) {
        System.out.println("新增用户" + user);
    }

    public void removeById(Integer id) {
        System.out.println("删除Id=" + id + "的用户");
    }
}

定义切面类LogAspect 

package org.flowerfog.comm;

public class LogAspect {
    // 前置通知
    public void before(){
        System.out.println("开始调用方法");
    }
    // 后置通知
    public void after(){
        System.out.println("结束调用方法");
    }
    // 异常通知
    public void exception(Exception ex){
        System.out.println("调用方法出席异常"+ex.getMessage());
    }
}

 添加Spring核心配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

</beans>

AOP配置说明 

注册切面类

配置切面

标签元素

作用

<aop:config>

AOP配置顶层节点,一个配置文件中可以包含多个<aop:config>节点

<aop:aspect>

配置切面该元素会将Spring容器中的Bean转换成切面Bean

<aop:pointcut>

配置切点,通过id属性指定切点唯一标识,通过expression属性指定切点表达式

 

 

Spring配置文件中完成切面配置

<?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
       https://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--注册Bean-->
    <bean id="userService" class="org.flowerfog.Service.UserService" />
    <bean id="logAspect" class="org.flowerfog.comm.LogAspect" />
    <!--AOP配置,需导入aop约束-->
    <aop:config>
        <!--配置切点-->
        <aop:pointcut id="pointcut" expression="execution(* org.flowerfog.Service.*.*(..))"/>
        <!--配置切面Bean-->
        <aop:aspect ref="logAspect">
            <aop:before method="before" pointcut-ref="pointcut" />
            <aop:after method="after" pointcut-ref="pointcut" />
            <aop:after-throwing method="exception" pointcut-ref="pointcut" throwing="ex" />
        </aop:aspect>

    </aop:config>
</beans>

编写测试类,验证效果


import org.flowerfog.Service.UserService;
import org.flowerfog.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


public class UserServiceTest {
    @Test
    public void add() {
        // 加载Spring核心配置文件
        ApplicationContext ctx=new ClassPathXmlApplicationContext("spring.xml");
        // 获取bean对象
        UserService userService = (UserService) ctx.getBean("userService");
        User user = new User();
        user.setName("张三");
        user.setAccount("111");
        user.setPassword("123456");
        user.setStatus(0);
        userService.add(user);
    }
}


运行结果:

当前的目录结构:

 

3. 通过注解实现AOP

Spring 基于注解配置 AOP 需要 启用 AspectJ 自动代理 功能
启用 该功能后, Spring 框架会自动扫描应用程序中所有被 AOP 注解标记的类 , 并自动创建 AOP 代理 对象
Spring 配置文件中添加如下配置:

Spring AOP注解

@Aspect定义切面

切面类需注册到Spring容器@Aspect注解才能生效

@Pointcut定义切点

切面类中定义一个空方法并使用@Pointcut注解来定义切点

然后在@Pointcut注解中定义切点表达式用来匹配切入的目标类和方法

空方法的方法名就是切点的唯一标识id

 通过以下注解来定义通知 

 

用例设计(采用注解):

基于 Spring AOP 技术 在项目中配置 切面 实现 日志记录 的功能。
日志记录功能的具体要求如下:

1)当应用程序调用业务逻辑层组件方法之前,在控制台中打印出“开始调用方法

2)当应用程序调用业务逻辑层组件方法之后,在控制台中打印出“结束调用方法

3)当应用程序调用业务逻辑层组件方法出现了异常,在控制台中打印出“调用方法出现异常

 

 实现步骤:

新建项目模块

整合Spring AOP框架依赖

定义切面类以及通知方法

添加注解完成切面配置

运行测试类完成切面测试

新增模块:

新增模块 spring- aop -annotation

导入相关依赖: 

修改模块的 pom.xml ,导入依赖
引入 spring-context 依赖 后,根据依赖的传递性,会自动引入 spring- aop 依赖
引入 Spring AspectJ 框架的整合依赖 spring-aspects ,因为我们要使用 AspectJ 框架的语法来进行配置切面

 

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.flowerfog</groupId>
        <artifactId>Spring6</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <artifactId>spring-aop-annotation</artifactId>
    <packaging>war</packaging>
    <name>spring-aop-annotation Maven Webapp</name>
    <url>https://maven.apache.org</url>
    <dependencies>
        <!--spring context依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>6.0.2</version>
        </dependency>
        <!--spring和AspectJ整合依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>6.0.2</version>
        </dependency>
        <!--junit5测试-->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>5.3.1</version>
        </dependency>
        <!--lombok插件-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.30</version>
        </dependency>
    </dependencies>
    <build>
        <finalName>spring-aop-annotation</finalName>
    </build>
</project>

新增实体类User 

package org.flowerfog.pojo;

import lombok.Data;

@Data
public class User {
    private Integer id;
    private String name;
    private String account;
    private String password;
    private Integer status;
}

新增业务类UserService 

package org.flowerfog.Service;


import org.flowerfog.pojo.User;

public class UserService {
    public void add(User user) {
        System.out.println("新增用户" + user);
    }

    public void removeById(Integer id) {
        System.out.println("删除Id=" + id + "的用户");
    }
}

定义切面类LogAspect 

package org.flowerfog.comm;

import org.aspectj.lang.annotation.*;

// 定义切面
@Aspect
public class LogAspect {
    // 定义切点,其id为pointcut
    @Pointcut("execution(* org.flowerfog.Service.*.*(..))")
    public void pointcut(){

    }
    // 前置通知
    @Before("pointcut()")
    public void before(){
        System.out.println("开始调用方法");
    }
    // 后置通知
    @After("pointcut()")
    public void after(){
        System.out.println("结束调用方法");
    }
    // 异常通知
    @AfterThrowing(value = "pointcut()",throwing = "ex")
    public void exception(Exception ex){
        System.out.println("调用方法出现异常"+ex.getMessage());
    }
}

如何获取连接点信息 

通过切面对象方法的形参能获取连接点信息
数据类型为 JoinPoint

 

环绕通知可使用增强类型 ProceedingJoinPoint ,其 proceed() 执行连接点拦截到的方法

@AfterReturning 

在切面类中添加返回通知的代码

 LogAspect当前代码如下:

package org.flowerfog.comm;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.aspectj.lang.reflect.SourceLocation;
import org.springframework.util.ObjectUtils;

import java.lang.reflect.Method;

// 定义切面
@Aspect
public class LogAspect {
    // 定义切点,其id为pointcut
    @Pointcut("execution(* org.flowerfog.Service.*.*(..))")
    public void pointcut(){

    }
    // 前置通知
    @Before("pointcut()")
    public void before(){
        System.out.println("开始调用方法");
    }
    // 后置通知
    @After("pointcut()")
    public void after(){
        System.out.println("结束调用方法");
    }
    // 异常通知
    @AfterThrowing(value = "pointcut()",throwing = "ex")
    public void exception(Exception ex){
        System.out.println("调用方法出现异常"+ex.getMessage());
    }

    // 返回通知
    @AfterReturning("pointcut()")
    public void afterReturn(JoinPoint joinPoint){
        MethodSignature signature=(MethodSignature)joinPoint.getSignature(); // 获得目标对象的签名,及当前拦截到的方法
        Method method=signature.getMethod();

        String longString = joinPoint.toLongString();// 获得连接点的扩展全命名
        Object[] args = joinPoint.getArgs();// 返回连接点的参数数组
        Object target = joinPoint.getTarget();// 获得目标对象
        Object aThis = joinPoint.getThis();// 获得正在执行的目标对象
        JoinPoint.StaticPart staticPart = joinPoint.getStaticPart();// 获得连接点的静态部分
        SourceLocation sourceLocation = joinPoint.getSourceLocation();// 获得连接点对应的方法源地址
        String shortString = joinPoint.toShortString();// 获得连接点的缩写命名
        String kind = joinPoint.getKind();// 返回连接点的类型

        System.out.println("method:"+method+"\r\nlongString:"+longString);
        if (!ObjectUtils.isEmpty(args)) {
            for (Object arg : args) {
                System.out.println(arg);
            }
        }
        System.out.println("调用方法ok");
    }

}

@Around

在切面类中添加环绕通知的代码

 最终LogAspect代码如下:

package org.flowerfog.comm;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.aspectj.lang.reflect.SourceLocation;
import org.springframework.util.ObjectUtils;

import java.lang.reflect.Method;

// 定义切面
@Aspect
public class LogAspect {
    // 定义切点,其id为pointcut
    @Pointcut("execution(* org.flowerfog.Service.*.*(..))")
    public void pointcut(){

    }
    // 前置通知
    @Before("pointcut()")
    public void before(){
        System.out.println("开始调用方法");
    }
    // 后置通知
    @After("pointcut()")
    public void after(){
        System.out.println("结束调用方法");
    }
    // 异常通知
    @AfterThrowing(value = "pointcut()",throwing = "ex")
    public void exception(Exception ex){
        System.out.println("调用方法出现异常"+ex.getMessage());
    }

    // 返回通知
    @AfterReturning("pointcut()")
    public void afterReturn(JoinPoint joinPoint){
        MethodSignature signature=(MethodSignature)joinPoint.getSignature(); // 获得目标对象的签名,及当前拦截到的方法
        Method method=signature.getMethod();

        String longString = joinPoint.toLongString();// 获得连接点的扩展全命名
        Object[] args = joinPoint.getArgs();// 返回连接点的参数数组
        Object target = joinPoint.getTarget();// 获得目标对象
        Object aThis = joinPoint.getThis();// 获得正在执行的目标对象
        JoinPoint.StaticPart staticPart = joinPoint.getStaticPart();// 获得连接点的静态部分
        SourceLocation sourceLocation = joinPoint.getSourceLocation();// 获得连接点对应的方法源地址
        String shortString = joinPoint.toShortString();// 获得连接点的缩写命名
        String kind = joinPoint.getKind();// 返回连接点的类型

        System.out.println("method:"+method+"\r\nlongString:"+longString);
        if (!ObjectUtils.isEmpty(args)) {
            for (Object arg : args) {
                System.out.println(arg);
            }
        }
        System.out.println("调用方法ok");
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        //开始时间
        long beginTime = System.currentTimeMillis();
        //执行目标方法
        Object result = joinPoint.proceed();
        //执行时长(毫秒)
        long time = System.currentTimeMillis()-beginTime;

        System.out.println("执行时长(毫秒):"+time);
        
        return  result;
    }

}

Spring配置文件中配置AspectJ自动代理 

<?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
       https://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--注册Bean-->
    <bean id="userService" class="org.flowerfog.Service.UserService" />
    <bean id="logAspect" class="org.flowerfog.comm.LogAspect" />
    <!--启用AspectJ自动代理-->
    <aop:aspectj-autoproxy />
</beans>

编写测试类 


import org.flowerfog.Service.UserService;
import org.flowerfog.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


public class UserServiceTest {
    @Test
    public void add() {
        // 加载Spring核心配置文件
        ApplicationContext ctx=new ClassPathXmlApplicationContext("spring.xml");
        // 获取bean对象
        UserService userService = (UserService) ctx.getBean("userService");
        User user = new User();
        user.setName("李四");
        user.setAccount("222");
        user.setPassword("123456");
        user.setStatus(0);
        userService.add(user);
    }
}

 运行结果:

当前的目录结构如下: 

至此, AOP演示结束。。。。。。。。


http://www.kler.cn/a/388091.html

相关文章:

  • 贪心算法入门(二)
  • Prometheus面试内容整理-Prometheus 的架构和工作原理
  • python装饰器的使用以及私有化
  • 【C++】类与对象的基础概念
  • 【秋招笔试-支持在线评测】11.13花子秋招(已改编)-三语言题解
  • 黄色校正电容102j100
  • 第4章 分离数据和指令-Claude开发应用教程
  • 2024最新软件测试面试热点问题
  • Oh My Posh安装
  • 07 P1164 小A点菜
  • Docker在CentOS上的安装与配置
  • 初识Electron 进程通信
  • PGMP-串串01概述
  • 数据分析:微生物功能差异分析之Maaslin2
  • 5分钟科普:AI网关是什么?应用场景是什么?有没有开源的选择?
  • 【JAVA】java 企业微信信息推送
  • 8+ 典型分析场景,25+ 标杆案例,Apache Doris 和 SelectDB 精选案例集(2024版)电子版上线
  • Python酷库之旅-第三方库Pandas(204)
  • layui 文件上传前检查文件大小,后面再点上传出现重复提交的问题
  • 【图】图学习
  • 20241106软考架构-------软考案例12答案
  • SQL,力扣题目262,行程和用户
  • 9_api_intro_imagerecognition_ocr2word
  • Vue 的 keep-alive
  • CSRF 跨站请求伪造的实现原理和预防措施
  • Windows 使用批处理脚本快速释放被占用的端口