688
社区成员
发帖
与我相关
我的任务
分享| 这个作业属于哪个课程 | 2023年福大-软件工程实践-W班 |
|---|---|
| 这个作业要求在哪里 | 软件工程实践寒假作业软件工程实践总结&个人技术博客 |
| 这个作业的目标 | 课程回顾与总结、个人技术总结 |
| 其他参考文献 | 《构建之法》 |
Spring Boot AOP是一种面向切面编程的技术,使用AOP可以更轻松地实现横切关注点的重用和集中管理,提高代码的可重用性和可测试性。同时提高应用程序的可扩展性和可维护性。其难点在于理解AOP的概念、学习切面和通知的编写方式,并正确应用于实际场景中。

<!-- 引入aop支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Pointcut {
String value() default "";
String argNames() default "";
}
切入点表达式包括一个execution表达式和argNames
例如:
@Pointcut("execution(* edu.fzu.service.*.*(..))")
public void pointCut() {}
JoinPoint可获取的信息
//返回目标对象,即被代理的对象
Object getTarget();
//返回切入点的参数
Object[] getArgs();
//返回切入点的Signature
Signature getSignature();
//返回切入的类型,比如method-call,field-get等等
String getKind();
通过自定义注解实现对特定方法使用切面
1.businessName字段:用于表示接口业务描述信息。
2.@Target:表示该注解可以声明在哪些目标元素之前,ElementType.METHOD表明该注解只能声明在一个类的方法前。
3.@Retention:注解类的生命周期。RetentionPolicy.RUNTIME 表示该注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在。
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface SystemLog {
String businessName();
}
1.在类上使用@Component注解。
2.在类上使用@Aspect注解 使之成为切面类。
3.@Pointcut:指哪些方法需要被执行"AOP"。
4.@annotation:带有相应注解的任意方法。
5.@Around:环绕通知。
6.ProceedingJoinPoint类: 继承了 JoinPoint。是在JoinPoint的基础上暴露出 proceed 这个方法,仅可用于@Around中。
7.joinPoint.proceed方法:执行被环绕的方法,其返回值就是被环绕方法的返回值。
8.handleBefore方法:在joinPoint.proceed方法执行前即被环绕的方法执行前执行的方法,输出请求的信息日志,与handleAfter方法共同实现“Around”效果。
9.handleAfter方法:在joinPoint.proceed方法执行后即被环绕的方法执行后执行的方法,输出方法返回,与handleBefore方法共同实现“Around”效果。
@Component
@Aspect
@Slf4j
public class LogAspect {
@Pointcut("@annotation(team.machine.machineguardbe.common.annotation.SystemLog)")
public void pt() {
}
//定义通知方法
@Around("pt()")
public Object printLog(ProceedingJoinPoint joinPoint) throws Throwable {
Object ret;
try {
handleBefore(joinPoint);
ret = joinPoint.proceed();
handleAfter(ret);
} finally {
// 结束后换行System.lineSeparator()(系统的换行符)
log.info("=======End=======" + System.lineSeparator());
}
return ret;
}
private void handleAfter(Object ret) {
// 打印出参
log.info("Response : {}", JSON.toJSONString(ret));
}
private void handleBefore(ProceedingJoinPoint joinPoint) {
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();
//获取被增强方法上的注释对象
SystemLog systemLog = getSystemLog(joinPoint);
log.info("=======Start=======");
// 打印请求 URL
log.info("URL : {}", request.getRequestURL());
// 打印描述信息
log.info("BusinessName : {}", systemLog.businessName());
// 打印 Http method
log.info("HTTP Method : {}", request.getMethod());
// 打印调用 controller 的全路径以及执行方法
log.info("Class Method : {}.{}", joinPoint.getSignature().getDeclaringTypeName(), ((MethodSignature) joinPoint.getSignature()).getName());
// 打印请求的 IP
log.info("IP : {}", request.getRemoteHost());
// 打印请求入参
log.info("Request Args : {}", JSON.toJSONString(joinPoint.getArgs()));
}
private SystemLog getSystemLog(ProceedingJoinPoint joinPoint) {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
SystemLog systemLog = methodSignature.getMethod().getAnnotation(SystemLog.class);
return systemLog;
}
}
问题描述:错误理解AOP中@After注解的作用。
在进行团队项目时需要实现对多个接口的返回中增加一个相同的字段属性,一方面需要修改的接口是其他组员开发的,我并不想修改别人的代码,另一方面是有多个接口需要进行相同的操作后在返回值中增加一个相同的字段。出于这两个原因,我决定使用AOP来实现需求。
期初我认为@After注解是在被增强的方法执行后执行,因此我在增强方法中对返回类A进行了修改,增加了所需字段使其成为返回类B并返回,但出现报错警告无法将返回类B转换为返回类A。
在查阅了AOP各注解的作用后发现了问题所在。我错误理解了@After的执行时间,@After的执行发生在增强的方法执行完成后Return之前,因此当我在@After中修改了返回类的类型时,被增强的方法在返回时会对返回值的类型进行校验或自动转换,于是便发生了类型转换错误冲突。
最后通过修改原始方法中的返回类为返回类B,但在原始方法中对需要添加的字段并不做处理,使其为空,并在@After方法体中对其进行赋值,这样就保证了方法返回类的一致性,避免了类型转换的发生。
虽说最后解决了问题,但感觉并不优雅,或许AOP并不适合用来修改方法返回类的类型。
AOP(Aspect-Oriented Programming,面向切面编程)是一种编程范式,用于在应用程序中管理横切关注点。它通过将这些关注点从核心业务逻辑中分离出来,以模块化的方式实现重用和集中管理。
在编码过程中我们应当发掘出可提取的重复的业务,使用AOP进行实现,这不仅可以减少重复的代码,同时提高了代码的可维护性,当该需求发生改变时仅需要修改一处的代码便可作用于全局,极大地减小了维护成本。
1.SpringBoot整合AOP超详细教程 ——XiongHuyi
2.Spring Boot AOP - 面向切面编程——守望时空33
3.Spring——AOP原理及流程详解——Colins~