Spring Aop该如何使用

科技公元 后端 2024-11-13

Spring Aop该如何使用

AOP(Aspect OrientedProgramming),即面向切面编程。本文介绍了AOP的相关概念和术语,用业务场景演示了Spring Aop的使用方式。希望本文对你轻松使用Spring Aop有所帮助。

一 什么是AOP

AOP(Aspect OrientedProgramming),面向切面编程,通过提供另一种思考程序结构的方式来补充面向对象编程(OOP)。OOP中模块化的关键单元是类,而AOP中模块化的单元是方面,即处理过程中某个步骤或阶段。

举个例子,项目中对关键操作需要记录日志,无非是简单地插表操作。但是如果在每个接口中都重复调用或实现,不仅浪费时间,还将项目变得不那么清爽。这时,面向切面编程就该出场了。

利用AOP,可以对项目中边缘业务进行隔离,降低无关业务逻辑耦合度,提高代码复用率和开发效率。一般用于日志记录、性能统计、权限管理、事务控制,异常处理等场景。

二 AOP相关术语

首先来看看AOP的一些术语:

  • 切面(Aspect) :一个关注点。Spring中以注解@Aspect标记在类上,声明一个切面类。
  • 连接点(Joinpoint) :在程序执行过程中某个特定的点,比如调用某方法。
  • 通知(Advice) :定义了切面动作和执行时机,如业务逻辑之外的事务、日志等。
  • 切点(Pointcut) :定义了执行通知的具体地点,是连接点的全集或子集;Spring中使用AspectJ切入点语法。
  • 引入(Introduction) :就是把切面用到目标类中去。在不改变现有类或方法代码的情况下,为其添加新的属性或行为。
  • 目标对象(Target Object) :被一个或者多个切面所通知的对象。Spring AOP是通过运行时代理实现的,因此目标对象是一个被代理类对象。
  • AOP代理(AOP Proxy) :AOP框架创建的对象,用来实现通知的执行。在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。
  • 织入(Weaving) :把切面连接到其它应用程序类型或者对象上,并创建一个被通知的对象。这些可以在编译时、类加载时或运行时完成。Spring AOP框架在运行时完成织入。

三 Spring AOP

Spring最核心的两个功能是Ioc和Aop,即控制反转和面向切面编程。如官网介绍,AOP在spring中应用如下:docs.spring.io/spring-fram…Spring Aop该如何使用即:AOP在Spring框架中用于:

  • 提供声明性企业服务。这类服务中最重要的是声明性事务管理。
  • 让用户实现自定义方面,用AOP补充他们对OOP的使用。

3.1 试用Spring AOP

以下代码可访问:github.com/kqcaihong/a…

3.1.1 准备工作

创建spring boot项目,引入如下依赖。xml

代码解读
复制代码
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- spring整合aspectj来实现aop --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.30</version> <scope>provided</scope> </dependency>

添加启动类及配置application.properties。java

代码解读
复制代码
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class MoreApplication { public static void main(String[] args) { SpringApplication.run(MoreApplication.class, args); } }properties
代码解读
复制代码
spring.application.name=aop-demo server.port=8010

添加User类。java

代码解读
复制代码
import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @Setter @Getter @NoArgsConstructor @AllArgsConstructor public class User { private Long id; private String name; private int age; public User(String name, int age) { this.name = name; this.age = age; } }

创建一个UserController类,模拟用户管理业务。java

代码解读
复制代码
import com.learn.more.entiry.User; import org.springframework.beans.factory.InitializingBean; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Collectors; @RequestMapping("/user") @RestController public class UserController implements InitializingBean { // 生成ID private static final AtomicLong ID_GENERATOR = new AtomicLong(0); // 模拟数据库来保存记录 private static final Map<Long, User> USER_MAP = new ConcurrentHashMap<>(); // 查询所有用户 @GetMapping("/queryAll") public List<User> queryAll() { return USER_MAP.values().stream().sorted(Comparator.comparingLong(User::getId)).collect(Collectors.toList()); } // 添加一个用户 @PostMapping("/add") public User addByParam(@RequestParam String name, @RequestParam int age) { User user = new User(name, age); user.setId(ID_GENERATOR.incrementAndGet()); USER_MAP.put(user.getId(), user); return user; } // 初始化一条记录 @Override public void afterPropertiesSet() { User bob = new User(ID_GENERATOR.incrementAndGet(), "Bob", 33); USER_MAP.put(bob.getId(), bob); } }

3.1.2 定义切面

创建AopTest类(交由spring容器管理),使用@Aspect注解声明它是一个切面类,可以在类中定义多个切面。java

代码解读
复制代码
import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.annotation.Aspect; import org.springframework.stereotype.Component; @Slf4j @Aspect @Component public class AopTest { }

3.1.3 定义切点

切点通过@Pointcut注解和切点表达式定义。Spring切面最小粒度是方法级别,而execution表达式声明了一个方法要作为切点需要满足的条件。

execution(<修饰符模式>? <返回类型模式> <方法名模式>(<参数模式>) <异常模式>?)

除了返回类型模式、方法名模式和参数模式外,其它项都是可选的。

  • *表示不限制
  • 两个点表示任意参数列表
  • 在多个表达式之间
    • 使用 ||,   or表示 或
    • 使用 &&,and表示 与
    • 使用not,!表示 非

AopTest类中增加切点:controller包下任何类中public方法java

代码解读
复制代码
// controller包下任意类中public方法,都是切点 @Pointcut("execution(public com.learn.more.controller.*.*(..))") public void pointcut() { }

3.1.4 定义通知

spring中有5类通知,对应5个用于方法上的注解:方法本身就是切面动作,注解则声明了执行时机

  • @Before:在切点方法之前执行。
  • @After:在切点方法之后执行
  • @AfterReturning:切点方法返回后执行
  • @AfterThrowing:切点方法抛异常执行
  • @Around:属于环绕增强,能控制在切点执行前和执行后,执行自定义逻辑

AopTest类中增加以下通知。java

代码解读
复制代码
@Before("pointcut()") public void beforeAdvice() { log.info("before advice..."); } @After("pointcut()") public void afterAdvice() { log.info("after advice..."); } @Around("pointcut()") public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { log.info("around before"); try { Object result = proceedingJoinPoint.proceed(); log.info("around result: {}", result); return result; } catch (Throwable t) { log.error("around error: ", t); throw t; } finally { log.info("around after"); } }

3.1.5 测试

使用postman访问GET http://localhost:8010/user/queryAllSpring Aop该如何使用运行结果如下。Spring Aop该如何使用

转载来源:https://juejin.cn/post/7375072185209438220

Apipost 私有化火热进行中

评论