问题背景
在AOP单个方法拦截的时候,遇到了拦截不成功不生效的问题,但是拦截整个类的方法是可以成功的
注意事项:
- 添加spring-boot-starter-aop依赖
- 可以复制文章的代码自己构建工程,也可以下载源码进行参考
AOP单个方法的拦截失效的解决方案
- 因为我拦截的方法是当前对象中的方法,拦截的时候需要spring增强的对象,才有切面的能力,这个跟注解@Async差不多的使用
- 错误示例如下,原本我拦截的是getResultVO(AopDTO aopDTO)方法,死活都拦截不到,因为调用这个方法的地方,是该类中当前对象,我对的是单个方法进行增强,并没有对当前类进行增强,所以对整个类增强时,是可以拦截这个方法的
//设置切点,单个的拦截方法调用的地方是当前对象
@Pointcut("execution(* com.yg.aop.controller.AopController.getResultVO(..))")
public void getPointcut() {
}
@RestController
@RequestMapping("aop")
@Slf4j
public class AopController {
public ResultVO getResultVO(AopDTO aopDTO) {
log.info("aaaaaaaaaa");
aopDTO.setName("jj");
ResultVO resultVO = ResultVO.builder()
.aopDTO(aopDTO)
.msg("你还好吗?").build();
log.info("resultVO: {}", resultVO);
return resultVO;
}
@PostMapping("test")
public String aopTest(@RequestBody AopDTO aopDTO) {
getResultVO(aopDTO);
return "success";
}
}
- 如果拦截单个方法就需要改进,简单来说只要更改这个方法的调用方式,使用其他对象来调用就可以了,下面小节附上正确的方式
AOP单个方法的拦截使用
1 创建springboot工程,引入依赖
<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.yg</groupId>
<artifactId>aop</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>aop</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
2 在启动类添加 @EnableAspectJAutoProxy 注解使AOP生效,一定不要忘哦
package com.yg.aop;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@SpringBootApplication
@EnableAspectJAutoProxy //使能切面功能注解
public class AopApplication {
public static void main(String[] args) {
SpringApplication.run(AopApplication.class, args);
}
}
3 添加controller测试类
package com.yg.aop.controller;
import com.yg.aop.dto.AopDTO;
import com.yg.aop.service.ResultServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @Author suolong
* @Date 2022/3/25 16:02
* @Version 1.5
*/
@RestController
@RequestMapping("aop")
@Slf4j
public class AopController {
//需要拦截的对象,使用注解@Autowired导入了spring的bean工厂,所以里面的方法也可以进行拦截
@Autowired
ResultServiceImpl resultService;
@PostMapping("test")
public String aopTest(@RequestBody AopDTO aopDTO) {
//调用拦截的方法
resultService.getResultVO(aopDTO);
return "success";
}
}
4 创建需要拦截的方法接口
package com.yg.aop.service;
import com.yg.aop.dto.AopDTO;
import com.yg.aop.dto.ResultVO;
/**
* @Author suolong
* @Date 2022/3/29 18:15
* @Version 1.5
*/
public interface ResultService {
ResultVO getResultVO(AopDTO aopDTO);
}
5 实现拦截的方法,在拦截的方法中我更改了入参的参数,方便查看aopDTO.setName("jj");
package com.yg.aop.service;
import com.yg.aop.dto.AopDTO;
import com.yg.aop.dto.ResultVO;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Service;
/**
* @Author suolong
* @Date 2022/3/29 15:40
* @Version 1.5
*/
@Service
@Slf4j
public class ResultServiceImpl implements ResultService{
//拦截的方法
@Override
public ResultVO getResultVO(AopDTO aopDTO) {
log.info("aaaaaaaaaa");
aopDTO.setName("jj");
ResultVO resultVO = ResultVO.builder()
.aopDTO(aopDTO)
.msg("你还好吗?").build();
log.info("resultVO: {}", resultVO);
return resultVO;
}
}
6 设置切面
package com.yg.aop.Aspect;
import com.yg.aop.dto.AopDTO;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.context.annotation.Configuration;
/**
* @Author suolong
* @Date 2022/3/25 16:18
* @Version 1.5
*/
@Configuration
@Aspect //当前类设置为切面,触发了切点方法就可以进行拦截
@Slf4j
public class AopAspect {
//设置切点方法或者类
@Pointcut("execution(* com.yg.aop.service.ResultServiceImpl.getResultVO(..))")
public void getPointcut() {
}
//对切点方法进行前置增强,就是在调用切点方法前进行做一些必要的操作,这就成为增强
@Before("getPointcut()")
public void getRes(JoinPoint joinPoint) {
log.info("Before Intercept request");
Object[] args = joinPoint.getArgs();
for (Object arg : args) {
if (arg instanceof AopDTO) {
log.info("AopDTO: {}", arg);
}
}
}
//用于配置后置通知。指定增强的方法在切入点方法之后执行,这个方式一定可以拦截到切点方法的返回值,@After拦截不到返回值
@AfterReturning(value = "getPointcut()", returning = "result")
public void getResult(JoinPoint joinPoint, Object result) {
log.info("AfterReturning Intercept request");
log.info("result: {}", result);
}
//用于配置最终通知。无论切入点方法执行时是否有异常,都会执行,只能拦截到入参
@After("getPointcut()")
public void afterTest(JoinPoint joinPoint) {
log.info("After Intercept request");
Object[] args = joinPoint.getArgs();
for (Object arg : args) {
log.info("arg: {}", arg);
}
}
}
AOP单个方法拦截测试
1 启动项目,使用postman进行测试,使用json格式
2 日志打印
- Before Intercept request进入了前置增强方法,此时入参没有改变,说明是在进入切点方法前打印的
- aaaaaaaaaa然后进入了切点方法里面
- AfterReturning Intercept request进入了@@AfterReturning后置增强方法,获取返回结果,可以看到,原本的入参name已经设置为jj
- After Intercept request进入了@After最终增强,可以看到入参的变化
作为程序员第 87 篇文章,每次写一句歌词记录一下,看看人生有几首歌的时间,wahahaha ...