0

0

java如何使用注解实现自定义拦截器 java注解拦截器实现的基础入门教程​

蓮花仙者

蓮花仙者

发布时间:2025-08-01 21:22:01

|

1015人浏览过

|

来源于php中文网

原创

定义注解:创建一个运行时保留、作用于方法的自定义注解(如@loggable),用于标记需要拦截的方法;2. 编写切面:通过@aspect和@component定义切面类,使用@pointcut指定匹配规则,结合@around等通知类型实现环绕拦截逻辑,利用反射获取方法信息并执行前置、后置及异常处理操作;3. 配置与使用:在spring boot中引入aop依赖后自动启用代理,无需显式配置@enableaspectjautoproxy,直接在目标方法上添加自定义注解即可生效;4. 优势分析:注解方式具有声明式、非侵入性特点,避免了传统xml配置或接口实现带来的代码污染,提升业务逻辑纯净度与可维护性;5. 常见问题:包括内部方法调用不触发拦截(因代理失效)、多切面执行顺序需用@order控制、性能开销在高并发场景下需评估、以及过度使用导致代码隐式性增强增加调试难度;6. 扩展应用:除日志外,还可用于权限校验(如@requirespermission)、参数验证、性能监控、幂等控制、数据脱敏、接口限流和缓存管理等横切关注点,显著提升系统模块化程度与开发效率。

java如何使用注解实现自定义拦截器 java注解拦截器实现的基础入门教程​

在Java中,利用注解实现自定义拦截器,本质上是运用了面向切面编程(AOP)的思想,通过定义一套声明式的规则,让程序在特定执行点(比如方法调用前后)自动插入预设的逻辑。这提供了一种极其优雅且非侵入性的方式来处理横切关注点,比如日志记录、权限校验、性能监控或事务管理,使得核心业务逻辑保持高度纯净,极大提升了代码的可读性、可维护性和模块化程度。

解决方案

要构建一个基于注解的自定义拦截器,我们通常会经历以下几个核心步骤:定义注解、编写切面(即拦截器逻辑)、以及配置(在Spring Boot等框架下通常是自动配置)。

首先,我们需要一个自定义注解来标记那些我们希望被拦截的方法或类。这个注解需要指定其作用范围(方法、类等)和生命周期。

立即学习Java免费学习笔记(深入)”;

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

/**
 * 自定义日志注解,用于标记需要记录方法执行日志的方法
 */
@Target(ElementType.METHOD) // 作用于方法
@Retention(RetentionPolicy.RUNTIME) // 运行时有效,以便通过反射读取
public @interface Loggable {
    String value() default ""; // 可以带一个参数,比如日志描述
}

接着,就是编写实际的拦截逻辑了。这通常通过一个切面(Aspect)来完成,我们会在其中定义“切点”(Pointcut)来匹配带有我们自定义注解的方法,并定义“通知”(Advice)来指定在切点执行前、后或环绕执行的逻辑。这里以Spring AOP为例:

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.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * 日志拦截切面
 */
@Aspect // 声明这是一个切面
@Component // 注册为Spring组件
public class LoggableAspect {

    // 定义切点,匹配所有带有 @Loggable 注解的方法
    @Pointcut("@annotation(com.example.demo.annotation.Loggable)")
    public void loggablePointcut() {
    }

    /**
     * 环绕通知:在方法执行前、后都执行逻辑
     * @param joinPoint 连接点,包含了被拦截方法的信息
     * @return 方法执行结果
     * @throws Throwable 异常
     */
    @Around("loggablePointcut()")
    public Object logMethodExecution(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        String methodName = method.getName();
        String className = method.getDeclaringClass().getSimpleName();
        Object[] args = joinPoint.getArgs();

        // 获取注解上的值
        Loggable loggable = method.getAnnotation(Loggable.class);
        String logDescription = loggable != null ? loggable.value() : "无描述";

        System.out.println(">>> 进入方法: " + className + "." + methodName + "(), 描述: " + logDescription + ", 参数: " + Arrays.toString(args));

        Object result = null;
        try {
            // 执行目标方法
            result = joinPoint.proceed();
            System.out.println("<<< 离开方法: " + className + "." + methodName + "(), 返回值: " + result);
        } catch (Throwable e) {
            System.err.println("!!! 方法异常: " + className + "." + methodName + "(), 异常信息: " + e.getMessage());
            throw e; // 重新抛出异常,让上层处理
        } finally {
            long endTime = System.currentTimeMillis();
            System.out.println("--- 方法执行耗时: " + (endTime - startTime) + "ms");
        }
        return result;
    }
}

最后,在你的Spring Boot应用中,你只需要确保你的主应用类上带有

@EnableAspectJAutoProxy
注解(在Spring Boot中,如果引入了
spring-boot-starter-aop
依赖,这个通常是自动配置的),然后就可以直接在需要拦截的方法上使用
@Loggable
注解了。

import org.springframework.stereotype.Service;
import com.example.demo.annotation.Loggable; // 假设你的注解包路径

@Service
public class MyBusinessService {

    @Loggable("处理用户订单")
    public String processOrder(String orderId, int quantity) {
        System.out.println("实际业务逻辑:正在处理订单 " + orderId + ",数量 " + quantity);
        if (quantity <= 0) {
            throw new IllegalArgumentException("数量必须大于0");
        }
        return "订单 " + orderId + " 处理成功";
    }

    @Loggable("查询商品信息")
    public String getProductInfo(String productId) {
        System.out.println("实际业务逻辑:查询商品 " + productId);
        return "商品[" + productId + "] 详情";
    }

    public void doSomethingElse() {
        System.out.println("这个方法没有注解,不会被拦截。");
    }
}

运行程序,调用

processOrder
getProductInfo
方法时,你就能看到切面中定义的日志输出了。

为什么选择注解来实现Java拦截器,它比传统方式有哪些优势?

选择注解来实现Java拦截器,在我看来,最显著的优势在于其无与伦比的“声明式”与“非侵入性”。想想看,过去我们可能需要通过XML配置一大堆Bean、代理,或者让业务类实现特定的接口,然后通过工厂模式来获取代理对象。这不仅让配置变得臃肿,更重要的是,它污染了业务代码。业务代码为了被拦截,不得不去感知拦截器的存在,这显然违背了单一职责原则。

注解则彻底改变了这一点。它就像给方法或类贴上了一个标签,告诉AOP框架:“嘿,这里需要特殊处理!”。业务代码本身不需要知道具体如何处理,也不需要引入额外的接口或基类。这种解耦使得业务逻辑保持了极高的纯净度,提高了代码的可读性和维护性。比如,一眼看到

@Loggable
,我就知道这个方法有日志切面在工作,而不需要去翻阅复杂的XML文件或者查看继承关系。

此外,注解方式也极大地简化了开发流程。在Spring Boot这类框架下,很多AOP配置都是自动化的,开发者只需要定义好注解和切面,几乎无需额外配置就能让拦截器生效。这比手动管理代理、配置拦截链条要高效太多了。它把原本复杂且重复的横切逻辑,转化成了一种直观、简洁的元数据声明。

自定义注解拦截器在实际开发中可能遇到哪些常见问题与挑战?

虽然注解拦截器非常强大,但在实际开发中,它也不是万能药,总会碰到一些让人挠头的问题。

闪念贝壳
闪念贝壳

闪念贝壳是一款AI 驱动的智能语音笔记,随时随地用语音记录你的每一个想法。

下载

一个很常见的坑是“内部方法调用不被拦截”。比如,在一个Service类中,

methodA()
调用了同一个Service实例的
methodB()
,如果
methodB()
上有注解,但
methodA()
没有,那么
methodB()
的拦截器可能不会生效。这是因为Spring AOP默认是基于动态代理实现的(JDK动态代理或CGLIB),它只对外部调用有效。当
methodA()
调用
methodB()
时,它实际上是直接调用了原始对象的方法,而不是代理对象的方法。解决这个问题,通常需要通过
AopContext.currentProxy()
获取当前代理对象再调用,或者考虑引入AspectJ的编译时/加载时织入。

另一个挑战是“拦截器执行顺序”。当多个切面都匹配同一个方法时,它们的执行顺序可能会变得复杂。Spring AOP提供

@Order
注解来指定切面的优先级,数字越小优先级越高。但如果设计不当,不同的切面之间可能会产生意想不到的交互,甚至导致死循环或逻辑错误。这要求开发者在设计多个切面时,必须清晰地规划好它们的职责和执行顺序。

还有就是“性能开销”。虽然现代JVM和AOP框架对反射和代理的优化已经非常出色,但在极端高并发或对性能要求极高的场景下,过多的拦截器层层嵌套,依然可能带来额外的性能损耗。这通常不是一个大问题,但值得在性能敏感的模块中进行考量和测试。

最后,过度依赖AOP可能导致“代码的隐式性”增加,也就是所谓的“魔法”代码。虽然注解让代码看起来很干净,但如果滥用,开发者可能会忘记某个注解背后隐藏着复杂的逻辑,导致调试困难。当出现问题时,栈追踪会变得更长,更难定位到真正的业务逻辑错误。这要求我们在使用AOP时保持克制,只将真正属于横切关注点的逻辑放入其中。

除了基础的日志记录,自定义注解拦截器还能在哪些业务场景中发挥作用?

自定义注解拦截器的应用场景远不止日志记录那么简单,它几乎可以用于所有需要“横切”到业务逻辑的非功能性需求。

一个非常典型的应用是权限控制。我们可以定义一个

@RequiresPermission("user:create")
这样的注解,然后编写一个切面,在方法执行前检查当前用户是否拥有所需的权限。如果没有,就抛出未授权异常。这比在每个业务方法内部写
if (hasPermission())
要优雅得多,且易于统一管理。

参数校验也是一个很好的例子。设想你有一个方法接收一个用户ID,你希望确保这个ID不是空的或者符合某种格式。你可以定义一个

@ValidateUserId
注解,并在拦截器中实现校验逻辑。这样,业务方法就不需要再重复这些校验代码了。

性能监控同样非常适合。定义一个

@MonitorPerformance
注解,拦截器可以在方法执行前后记录时间戳,计算方法的执行耗时,甚至将数据发送到监控系统,帮助我们发现性能瓶颈。

在分布式系统中,幂等性处理也常常借助于注解拦截器。例如,对于一个提交订单的接口,可以定义

@Idempotent
注解,拦截器在方法执行前检查请求的唯一标识符(如请求ID),确保同一个请求不会被重复处理,有效防止重复提交。

此外,还有数据脱敏/加密(如

@SensitiveData
自动对返回结果中的敏感字段进行脱敏)、API限流(如
@RateLimit(permits = 10)
控制方法每秒调用次数)、以及缓存管理(虽然Spring自带了
@Cacheable
等,但其底层原理也是AOP,我们可以根据自己的需求定制更复杂的缓存策略)等。这些场景都受益于注解拦截器提供的声明式、非侵入性特性,极大地提升了开发效率和系统健壮性。

热门AI工具

更多
DeepSeek
DeepSeek

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

豆包大模型
豆包大模型

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

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

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

文心一言
文心一言

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

讯飞写作
讯飞写作

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

即梦AI
即梦AI

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

ChatGPT
ChatGPT

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

相关专题

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

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

161

2025.08.06

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

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

89

2026.01.26

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

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

139

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应用程序等。

409

2023.10.12

Java Spring Boot开发
Java Spring Boot开发

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

73

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 应用的流行工具。

151

2025.12.22

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

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

271

2025.12.24

Spring Boot企业级开发与MyBatis Plus实战
Spring Boot企业级开发与MyBatis Plus实战

本专题面向 Java 后端开发者,系统讲解如何基于 Spring Boot 与 MyBatis Plus 构建高效、规范的企业级应用。内容涵盖项目架构设计、数据访问层封装、通用 CRUD 实现、分页与条件查询、代码生成器以及常见性能优化方案。通过完整实战案例,帮助开发者提升后端开发效率,减少重复代码,快速交付稳定可维护的业务系统。

34

2026.02.11

TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

26

2026.03.13

热门下载

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

精品课程

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

共23课时 | 4.4万人学习

C# 教程
C# 教程

共94课时 | 11.3万人学习

Java 教程
Java 教程

共578课时 | 82.1万人学习

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

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