
在 Spring Boot 开发中,有时我们希望在某些方法执行前后自动添加一段通用逻辑,例如日志记录、权限校验、缓存处理或像用户示例中那样,向 Model 对象添加特定属性。如果每次都在方法内部手动添加这些逻辑,不仅代码会变得冗余,而且难以维护。理想的解决方案是使用一个自定义注解作为标记,然后通过某种机制(如 AOP)在运行时根据这个标记自动注入所需的逻辑。
用户示例中展示的需求是,当一个类或方法被 @MyCustomAnnotation 标记时,其内部的 Model 对象应自动添加 model.addAttribute("key","value"); 这段逻辑。
Spring AOP(Aspect-Oriented Programming)是解决这类问题的核心机制。它允许我们定义“切面”(Aspect),在切面中定义“切点”(Pointcut)来匹配特定的执行点(如方法执行),并定义“通知”(Advice)来在这些切点上执行额外的逻辑。
核心步骤:
首先,我们需要定义一个简单的自定义注解,它将作为我们逻辑注入的标记。这个注解不需要包含任何业务逻辑,它只是一个元数据标记。
package com.example.demo.annotation; // 建议放在单独的包中
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定义注解,用于标记需要注入特定逻辑的类或方法。
*/
@Target({ElementType.TYPE, ElementType.METHOD}) // 允许注解作用于类和方法
@Retention(RetentionPolicy.RUNTIME) // 运行时保留,以便通过反射读取
public @interface MyCustomAnnotation {
// 可以在此添加属性,如果逻辑需要基于注解的参数
// String value() default "";
}切面是实现核心逻辑的地方。它包含一个切点定义,用于匹配被 @MyCustomAnnotation 标记的类或方法,以及一个通知,用于在匹配到时执行具体的业务逻辑。
package com.example.demo.aspect; // 建议放在单独的包中
import com.example.demo.annotation.MyCustomAnnotation;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.ui.Model;
import java.util.Arrays;
/**
* 自定义注解逻辑切面,用于在被@MyCustomAnnotation标记的方法执行前注入逻辑。
*/
@Aspect // 声明这是一个切面
@Component // 将切面作为Spring组件管理
public class CustomAnnotationLogicAspect {
/**
* 定义切点:匹配被 @MyCustomAnnotation 标记的类中的所有方法,
* 或者直接被 @MyCustomAnnotation 标记的方法。
*/
@Pointcut("@within(com.example.demo.annotation.MyCustomAnnotation) || @annotation(com.example.demo.annotation.MyCustomAnnotation)")
public void myCustomAnnotationPointcut() {}
/**
* 定义环绕通知:在目标方法执行前后执行自定义逻辑。
*
* @param joinPoint 连接点,提供对目标方法的信息和控制
* @return 目标方法的返回值
* @throws Throwable 目标方法或通知中抛出的异常
*/
@Around("myCustomAnnotationPointcut()")
public Object applyCustomLogic(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("--- 进入 CustomAnnotationLogicAspect: 准备注入逻辑 ---");
// 尝试从方法参数中查找 Model 对象,并添加属性
boolean modelModified = false;
Object[] args = joinPoint.getArgs(); // 获取目标方法的参数
if (args != null && args.length > 0) {
for (Object arg : args) {
if (arg instanceof Model) { // 如果参数是 Model 类型
Model model = (Model) arg;
model.addAttribute("keyFromAspect", "valueFromCustomAnnotation");
System.out.println(" >>> 成功向 Model 添加属性: keyFromAspect='valueFromCustomAnnotation'");
modelModified = true;
break;
}
}
}
if (!modelModified) {
System.out.println(" >>> 警告: 未在方法参数中找到 Model 对象,无法注入 Model 属性。");
}
// 继续执行目标方法
Object result = joinPoint.proceed();
System.out.println("--- 退出 CustomAnnotationLogicAspect: 逻辑注入完成 ---");
return result; // 返回目标方法的执行结果
}
}为了让 Spring 容器能够识别并应用 @Aspect 注解定义的切面,我们需要在 Spring Boot 应用的主类或配置类上添加 @EnableAspectJAutoProxy 注解。
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableAspectJAutoProxy; // 导入 AOP 启用注解
@SpringBootApplication
@EnableAspectJAutoProxy // 启用 Spring AOP 代理
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}现在,你可以在任何 Spring 管理的组件(如 Controller、Service、Repository)的类或方法上使用 @MyCustomAnnotation,相关的逻辑就会被自动注入。
package com.example.demo.controller;
import com.example.demo.annotation.MyCustomAnnotation;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@MyCustomAnnotation // 将自定义注解应用于 Controller 类
@Controller
public class ExController {
@RequestMapping(value = "/index", method = RequestMethod.GET)
public String index(Model model){
// 注意:这里不需要手动添加 model.addAttribute("key","value");
// 这段逻辑将由 CustomAnnotationLogicAspect 自动注入。
System.out.println(" >>> 正在执行 ExController.index() 方法内部逻辑。");
System.out.println(" >>> Model 是否包含 'keyFromAspect' 属性? " + model.containsAttribute("keyFromAspect"));
return "index"; // 假设存在一个名为 'index.html' 或 'index.jsp' 的视图
}
@GetMapping("/another")
public String anotherMethod(Model model) {
// 由于类被 @MyCustomAnnotation 标记,此方法也会被切面处理
System.out.println(" >>> 正在执行 ExController.anotherMethod() 方法内部逻辑。");
System.out.println(" >>> Model 是否包含 'keyFromAspect' 属性? " + model.containsAttribute("keyFromAspect"));
return "another"; // 假设存在一个名为 'another.html' 的视图
}
}当访问 /index 或 /another 路径时,控制台输出将显示切面逻辑被执行,并且 Model 对象中会包含由切面注入的 keyFromAspect 属性。
通过自定义注解结合 Spring AOP,我们能够以非侵入式的方式为 Spring Boot 应用中的方法注入通用逻辑。这种模式极大地提高了代码的模块化、可维护性和复用性。通过清晰地定义注解作为标记,并利用切面实现具体的逻辑,我们可以构建出更加优雅和健壮的应用程序。理解 AOP 的核心概念(切面、切点、通知)是掌握这一强大功能的关键。
以上就是通过自定义注解在 Spring Boot 方法中注入特定逻辑的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号