SpringBoot之AOP具体实现详解
程序员文章站
2024-03-19 16:42:46
...
SpringBoot之AOP具体实现详解
- //本文作者:wangkexin
Spring AOP 之 SpringBoot集成
上一篇文章《Spring AOP 面向切面概念入门》对AOP作了简要的介绍,包含一些专业术语的解释。
本文基于SpringBoot编写了一个简单的Spring AOPDemo。
准备工作(pom文件添加如下依赖)
maven依赖添加如下
<!--引入SpringBoot的Web模块-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--引入AOP依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
本次使用的 maven 的 modelVersion 4.0.0, springBoot 版本是 2.1.7.RELEASE, jdk 1.8 的项目环境
注意:在完成了引入AOP依赖包后,不需要去做其他配置。AOP的默认配置属性中,spring.aop.auto属性默认是开启的,也就是说只要引入了AOP依赖后,默认已经增加了@EnableAspectJAutoProxy,不需要在程序主类中增加@EnableAspectJAutoProxy来启用。
application.yml的配置(可有可无,默认是开启)
列出来看一下是什么配置
spring:
aop:
proxy-target-class: true
auto: true
web请求入口:实现具体的业务
这里实现的是日志信息的搜集
@RestController
public class TestController {
@GetMapping("/test")
@LogTestLog(value = "测试") //使用这个注解就相当于切点了
@ResponseBody
public String test(@RequestParam("name") String name, @RequestParam("age") Integer age) throws Exception {
return name;
}
}
定义一个注解
这个注解是是相当于切入点
@Target({ElementType.PARAMETER, ElementType.METHOD}) //作用在参数和方法上
@Retention(RetentionPolicy.RUNTIME) //运行时注解
@Documented//表明这个注解应该被 javadoc工具记录
public @interface LogTestLog {
String value() default "";
}
切面的具体实现
定义切面类:在类上添加@Aspect 和@Component 注解即可将一个类定义为切面类。
@Aspect 注解 使之成为切面类
@Component 注解 把切面类加入到IOC容器中
@Aspect
@Slf4j
@Component
public class LogAscpectTest {
/**
* 切面扫描的是 使用这个注解的方法
* 可以定义多个切入点
*/
@Pointcut("@annotation(com.java8.aopday06.LogTestLog)")
public void webLog () {
}
/**
* execution:用来匹配执行方法的连接点
* A:@Pointcut("execution(* com.aijava.springcode.service..*.*(..))")
* 第一个*表示匹配任意的方法返回值,..(两个点)表示零个或多个,上面的第一个..表示service包及其子包,第二个*表示所有类,第三个*表示所有方法,第二个..表示方法的任意参数个数
* B:@Pointcut("within(com.aijava.springcode.service.*)")
* within限定匹配方法的连接点,上面的就是表示匹配service包下的任意连接点
* C:@Pointcut("this(com.aijava.springcode.service.UserService)")
* this用来限定AOP代理必须是指定类型的实例,如上,指定了一个特定的实例,就是UserService
* D:@Pointcut("bean(userService)")
* bean也是非常常用的,bean可以指定IOC容器中的bean的名称
* @within(org.springframework.stereotype.Service),拦截带有 @Service 注解的类的所有方法
* @annotation(org.springframework.web.bind.annotation.RequestMapping),拦截带有@RquestMapping的注解方法
*/
@Pointcut("execution(* com.java8.aopday06..*.*(..))")//把指定包路径下所有方法都进行匹配
private void pointCut() {
}
/**
* 环绕通知--- 测试记录所有的日志信息
* @param joinPoint
* @return
*/
@Around("webLog()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-mm-dd HH:mm:ss");
String format = simpleDateFormat.format(new Date(System.currentTimeMillis()));
log.info("调用方法之前答应()进入该方法的时间信息:{}", format);
// 表示开始调用设置切点的方法
Object proceed = joinPoint.proceed();
long time = System.currentTimeMillis() - startTime;
log.info("方法执行的时间: {}", time);
try {
//记录日记信息
saveLog (joinPoint, time);
}catch (Exception e) {
log.error("出现异常: {}", e.toString());
e.printStackTrace();
}
return proceed;
}
/**
* 返回通知这是为了测试拿到被匹配所有切面的业务的返回值
* @param joinPoint
* @return
*/
@AfterReturning(pointcut = "pointCut()", returning = "result")
public void afterReturning (JoinPoint point, Object result) {
MethodSignature methodSignature = (MethodSignature) point.getSignature();
Class returnType = methodSignature.getReturnType();
log.info("方法的返回值类型: {}, 方法的返回值:{}", returnType, result);
}
private void saveLog(ProceedingJoinPoint joinPoint, long time) {
MethodSignature signature = (MethodSignature)joinPoint.getSignature();
Method method = signature.getMethod();
// 获取注解上的描述
LogTestLog annotation = method.getAnnotation(LogTestLog.class);
String value = annotation.value();
// 获取的请求的类名字, 方法名,
String name = method.getName();
//添加注解的方法名称: test
log.info("添加注解的方法名称: {}", name);
String className = joinPoint.getTarget().getClass().getName();
// 添加注解的方法类的名称:com.java8.aopday06.TestController
log.info("添加注解的方法类的名称:{}", className);
Object[] args = joinPoint.getArgs();
List<Object> objects = Arrays.asList(args);
for (int j = 0; j < objects.size(); j++) {
// 参数信息: 22 ---- 可以吧数据放入到容器中
log.info("参数信息: {} ", objects.get(j));
}
Parameter[] parameters = method.getParameters();
List<Parameter> parameterList = Arrays.asList(parameters);
for (int i = 0; i < parameterList.size(); i++) {
//parameters : class java.lang.String, name ---- 参数的类型以及名字
log.info("parameters : {}, {}", parameterList.get(i).getType(), parameterList.get(i).getName());
}
HttpServletRequest httpServletRequest = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
String requestURI = httpServletRequest.getRequestURI();
StringBuffer requestURL = httpServletRequest.getRequestURL();
log.info("请求的 urI: {}, 请求的 url: {}", requestURI, requestURL);
String remoteAddr = httpServletRequest.getRemoteAddr();
String localAddr = httpServletRequest.getLocalAddr();
log.info("本地ip : {}", localAddr);
String remoteHost = httpServletRequest.getRemoteHost();
log.info("远程 host: {}", remoteHost);
int remotePort = httpServletRequest.getLocalPort();
log.info("本地 post: {}", remotePort);
}
}
启动调用服务返回结果
http://localhost:8081/test?name=45&age=2
如果大家在遇到问题可以随时联系我
下一篇: 原生python实现knn分类算法