0

0

利用Spring AOP与自定义注解实现方法逻辑扩展

聖光之護

聖光之護

发布时间:2025-09-22 10:57:41

|

958人浏览过

|

来源于php中文网

原创

利用Spring AOP与自定义注解实现方法逻辑扩展

本文深入探讨了如何在Spring Boot应用中,通过自定义注解结合Spring AOP(面向切面编程)来优雅地为特定方法或类注入额外逻辑。我们将学习如何定义一个自定义注解,创建相应的切面来拦截被该注解标记的目标,并在方法执行前后动态地添加业务逻辑,例如向Spring MVC的Model对象中添加属性,从而实现代码的解耦与复用。

1. 引言:自定义注解与逻辑增强的需求

在spring boot开发中,我们经常遇到需要在多个方法或类中重复执行某段横切逻辑(如日志记录、权限校验、事务管理或数据预处理)。如果直接将这些逻辑硬编码到每个方法中,会导致代码冗余、可维护性差。理想情况下,我们希望能够通过一个简单的标记(自定义注解)来声明性地应用这些逻辑,而无需修改原始方法体。例如,当一个controller类被特定注解标记时,其内部的某些处理方法能够自动地向model中添加预设数据。这正是spring aop与自定义注解的完美结合点。

2. 核心概念:Spring AOP简介

Spring AOP(Aspect-Oriented Programming,面向切面编程)是一种编程范式,旨在将横切关注点(如日志、事务、安全等)从核心业务逻辑中分离出来。它允许我们在不修改核心业务代码的情况下,通过“切面”来增强或修改现有代码的行为。

Spring AOP的关键概念包括:

  • 切面(Aspect):一个模块化的单元,封装了横切关注点。它通常是一个带有@Aspect注解的类。
  • 连接点(Join Point):程序执行过程中可以插入切面的点,例如方法调用、异常抛出等。在Spring AOP中,通常是方法执行。
  • 通知(Advice):切面在特定连接点执行的动作。包括:
    • @Before:在目标方法执行之前执行。
    • @After:在目标方法执行之后(无论成功或失败)执行。
    • @AfterReturning:在目标方法成功执行并返回结果之后执行。
    • @AfterThrowing:在目标方法抛出异常之后执行。
    • @Around:包围目标方法的执行,可以在方法调用前后自定义行为,甚至阻止方法执行或改变返回值。
  • 切点(Pointcut):定义了通知将在哪些连接点执行的表达式。它通过匹配规则(如方法签名、注解等)来定位目标。

3. 步骤一:定义自定义注解

首先,我们需要创建一个自定义注解,用作我们逻辑增强的标记。

package com.example.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 自定义注解,用于标记需要额外逻辑增强的Controller或方法
 */
@Target(ElementType.TYPE) // 目标是类、接口(包括注解类型)或枚举声明
@Retention(RetentionPolicy.RUNTIME) // 注解在运行时可用,可以通过反射读取
public @interface MyCustomAnnotation {
    // 可以在这里定义注解的属性,例如一个描述信息
    String value() default "Default custom logic";
}

注解元数据解释:

  • @Target(ElementType.TYPE):指定该注解可以应用于类、接口(包括注解类型)或枚举声明。如果需要标记方法,可以改为ElementType.METHOD或{ElementType.TYPE, ElementType.METHOD}。根据原始问题,注解是放在Controller类上的,所以ElementType.TYPE是合适的。
  • @Retention(RetentionPolicy.RUNTIME):指定该注解在运行时可见,这意味着我们可以通过反射机制在运行时读取到该注解信息,这是AOP实现的基础。

4. 步骤二:创建切面(Aspect)

接下来,我们将创建一个Spring AOP切面,它将定义切点(哪些方法需要被拦截)和通知(拦截后执行什么逻辑)。

package com.example.aspect;

import com.example.annotation.MyCustomAnnotation;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import org.springframework.ui.Model;

/**
 * 自定义注解的切面,用于实现逻辑增强
 */
@Aspect // 声明这是一个切面
@Component // 将切面作为Spring组件管理
public class CustomAnnotationAspect {

    /**
     * 定义一个环绕通知(@Around),拦截被MyCustomAnnotation注解的类中所有公共方法。
     *
     * 切点表达式解释:
     * - @within(com.example.annotation.MyCustomAnnotation):匹配所有被MyCustomAnnotation注解的类。
     * - execution(public * *(..)):匹配该类中所有的公共方法。
     *
     * @param joinPoint 连接点,提供对被拦截方法的信息和控制
     * @return 目标方法的返回值
     * @throws Throwable 目标方法或切面逻辑可能抛出的异常
     */
    @Around("@within(com.example.annotation.MyCustomAnnotation) && execution(public * *(..))")
    public Object applyCustomLogic(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("--- 进入自定义逻辑增强切面 ---");

        // 1. 在方法执行前添加逻辑
        System.out.println("切面:准备为方法 " + joinPoint.getSignature().getName() + " 添加Model属性...");

        // 获取方法参数,查找Model对象
        Object[] args = joinPoint.getArgs();
        for (Object arg : args) {
            if (arg instanceof Model) {
                Model model = (Model) arg;
                // 根据需求添加Model属性
                model.addAttribute("customKey", "customValueFromAspect");
                System.out.println("切面:成功向Model中添加属性 'customKey:customValueFromAspect'");
                break; // 假设每个方法最多只有一个Model参数
            }
        }

        // 2. 执行原始目标方法
        Object result = joinPoint.proceed(); // 调用目标方法

        // 3. 在方法执行后添加逻辑(可选)
        System.out.println("切面:方法 " + joinPoint.getSignature().getName() + " 执行完毕。");
        System.out.println("--- 退出自定义逻辑增强切面 ---");

        return result; // 返回目标方法的执行结果
    }
}

切点(Pointcut)定义与通知(Advice)类型:

Magician
Magician

Figma插件,AI生成图标、图片和UX文案

下载
  • @Aspect 和 @Component:将该类声明为一个Spring管理的切面。
  • @Around:我们选择了环绕通知,因为它提供了最大的灵活性,可以在目标方法执行前后执行逻辑,甚至控制目标方法是否执行。
  • @within(com.example.annotation.MyCustomAnnotation):这是一个注解匹配切点表达式。它表示匹配所有被com.example.annotation.MyCustomAnnotation注解标记的类。
  • execution(public * *(..)):这是一个方法执行匹配切点表达式。它表示匹配所有公共方法。
  • 结合 &&,整个切点表达式的含义是:拦截所有被MyCustomAnnotation注解的类中的所有公共方法的执行。
  • ProceedingJoinPoint:在@Around通知中,必须使用ProceedingJoinPoint,它允许我们调用proceed()方法来执行目标方法。

实现逻辑注入:获取并操作Model对象:applyCustomLogic方法内部,我们通过joinPoint.getArgs()获取到目标方法的所有参数。然后遍历这些参数,判断是否存在org.springframework.ui.Model类型的实例。如果找到,就将其强制转换为Model类型,并调用addAttribute()方法注入我们需要的键值对

5. 步骤三:启用AOP支持

为了让Spring Boot应用能够识别并应用我们定义的切面,我们需要在主应用类或配置类上添加@EnableAspectJAutoProxy注解。

package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@SpringBootApplication
@EnableAspectJAutoProxy // 启用Spring AOP的自动代理功能
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

6. 完整示例

现在,我们创建一个Controller来演示如何使用自定义注解。

package com.example.controller;

import com.example.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;

/**
 * 示例Controller,被MyCustomAnnotation标记
 */
@MyCustomAnnotation // 应用自定义注解
@Controller
public class ExController {

    @RequestMapping(value = "/index", method = RequestMethod.GET)
    public String index(Model model){
        System.out.println("Controller:进入index方法。");
        // 这里不需要手动添加model.addAttribute("customKey","customValueFromAspect");
        // 它会由切面自动添加

        // 验证切面是否已添加属性
        if (model.containsAttribute("customKey")) {
            System.out.println("Controller:Model中已包含 'customKey',值为:" + model.getAttribute("customKey"));
        } else {
            System.out.println("Controller:Model中未包含 'customKey'。");
        }

        model.addAttribute("message", "Hello from ExController!");
        return "index"; // 返回视图名称
    }

    @GetMapping("/hello")
    public String hello(Model model) {
        System.out.println("Controller:进入hello方法。");
        // 这个方法也会被切面拦截,因为MyCustomAnnotation在类级别
        if (model.containsAttribute("customKey")) {
            System.out.println("Controller:Model中已包含 'customKey',值为:" + model.getAttribute("customKey"));
        }
        model.addAttribute("greeting", "Greetings from another method!");
        return "hello";
    }

    @GetMapping("/no-model")
    public String noModel() {
        System.out.println("Controller:进入noModel方法,无Model参数。");
        // 这个方法也会被切面拦截,但切面不会找到Model参数,所以不会添加属性
        return "no-model";
    }
}

当请求/index或/hello时,CustomAnnotationAspect会拦截这些方法的执行,并在它们内部逻辑执行前,自动向Model对象中添加"customKey":"customValueFromAspect"属性。而/no-model方法虽然也被拦截,但由于其参数中不包含Model对象,切面不会执行model.addAttribute()操作。

7. 注意事项与最佳实践

  • 通知类型的选择
    • @Before:适合做前置校验、日志记录等。
    • @AfterReturning:适合处理方法成功返回后的结果。
    • @AfterThrowing:适合处理方法抛出异常后的逻辑,如异常日志。
    • @Around:最强大,可以完全控制目标方法的执行,包括参数修改、返回值修改、阻止执行等,但使用不当也可能引入复杂性。
  • 参数与返回值处理:在@Around通知中,可以通过joinPoint.getArgs()获取参数,通过joinPoint.proceed(newArgs)修改参数后执行目标方法。返回值则直接通过return result;返回。
  • 异常处理:切面内部的逻辑也可能抛出异常。在@Around通知中,需要捕获joinPoint.proceed()可能抛出的Throwable,并决定如何处理(例如重新抛出、包装或处理掉)。
  • 性能考量:虽然AOP功能强大,但过度使用或复杂的切点表达式可能会对性能产生轻微影响。在性能敏感的场景下,应谨慎设计切面。
  • AOP代理机制:Spring AOP默认使用JDK动态代理(针对接口)或CGLIB代理(针对类)。如果目标类没有实现接口,Spring会使用CGLIB代理。确保你的类和方法是public的,否则AOP可能无法生效。
  • 自调用问题:同一个对象内部的方法调用(即this.methodA()调用this.methodB())不会被AOP代理拦截,因为它们不是通过代理对象调用的。这被称为“自调用问题”。

8. 总结

通过自定义注解结合Spring AOP,我们成功地实现了一种声明式的方式来增强Spring Boot应用中的方法逻辑。这种模式极大地提高了代码的模块化、可维护性和复用性,使得横切关注点与核心业务逻辑清晰分离。无论是进行权限管理、日志记录、性能监控,还是像本例中动态添加Model属性,Spring AOP都提供了一个优雅且强大的解决方案。掌握这一技术,将使你的Spring Boot应用开发更加高效和专业。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
spring框架介绍
spring框架介绍

本专题整合了spring框架相关内容,想了解更多详细内容,请阅读专题下面的文章。

115

2025.08.06

Java Spring Security 与认证授权
Java Spring Security 与认证授权

本专题系统讲解 Java Spring Security 框架在认证与授权中的应用,涵盖用户身份验证、权限控制、JWT与OAuth2实现、跨站请求伪造(CSRF)防护、会话管理与安全漏洞防范。通过实际项目案例,帮助学习者掌握如何 使用 Spring Security 实现高安全性认证与授权机制,提升 Web 应用的安全性与用户数据保护。

30

2026.01.26

spring boot框架优点
spring boot框架优点

spring boot框架的优点有简化配置、快速开发、内嵌服务器、微服务支持、自动化测试和生态系统支持。本专题为大家提供spring boot相关的文章、下载、课程内容,供大家免费下载体验。

135

2023.09.05

spring框架有哪些
spring框架有哪些

spring框架有Spring Core、Spring MVC、Spring Data、Spring Security、Spring AOP和Spring Boot。详细介绍:1、Spring Core,通过将对象的创建和依赖关系的管理交给容器来实现,从而降低了组件之间的耦合度;2、Spring MVC,提供基于模型-视图-控制器的架构,用于开发灵活和可扩展的Web应用程序等。

390

2023.10.12

Java Spring Boot开发
Java Spring Boot开发

本专题围绕 Java 主流开发框架 Spring Boot 展开,系统讲解依赖注入、配置管理、数据访问、RESTful API、微服务架构与安全认证等核心知识,并通过电商平台、博客系统与企业管理系统等项目实战,帮助学员掌握使用 Spring Boot 快速开发高效、稳定的企业级应用。

70

2025.08.19

Java Spring Boot 4更新教程_Java Spring Boot 4有哪些新特性
Java Spring Boot 4更新教程_Java Spring Boot 4有哪些新特性

Spring Boot 是一个基于 Spring 框架的 Java 开发框架,它通过 约定优于配置的原则,大幅简化了 Spring 应用的初始搭建、配置和开发过程,让开发者可以快速构建独立的、生产级别的 Spring 应用,无需繁琐的样板配置,通常集成嵌入式服务器(如 Tomcat),提供“开箱即用”的体验,是构建微服务和 Web 应用的流行工具。

34

2025.12.22

Java Spring Boot 微服务实战
Java Spring Boot 微服务实战

本专题深入讲解 Java Spring Boot 在微服务架构中的应用,内容涵盖服务注册与发现、REST API开发、配置中心、负载均衡、熔断与限流、日志与监控。通过实际项目案例(如电商订单系统),帮助开发者掌握 从单体应用迁移到高可用微服务系统的完整流程与实战能力。

156

2025.12.24

硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

1132

2023.10.19

clawdbot ai使用教程 保姆级clawdbot部署安装手册
clawdbot ai使用教程 保姆级clawdbot部署安装手册

Clawdbot是一个“有灵魂”的AI助手,可以帮用户清空收件箱、发送电子邮件、管理日历、办理航班值机等等,并且可以接入用户常用的任何聊天APP,所有的操作均可通过WhatsApp、Telegram等平台完成,用户只需通过对话,就能操控设备自动执行各类任务。

19

2026.01.29

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 3万人学习

C# 教程
C# 教程

共94课时 | 7.9万人学习

Java 教程
Java 教程

共578课时 | 52.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号