SpringBoot-自定义注解AOP实现及拦截器示例

news2025/1/19 7:50:09

SpringBoot-自定义注解AOP实现及拦截器示例

一、四大元注解

当你在编写自定义注解时@Target、@Retention、@Documented 和 @Inherited 是四个你可能会用到的元注解,它们可以帮助你更好地定义和使用注解

1、@Target

@Target 注解用于指定注解可以应用的程序元素类型。它的值是一个 ElementType 数组,表示该注解可以应用到哪些地方,包括类、接口、方法、字段等。常用的 ElementType 类型包括:

  • ElementType.TYPE:类、接口、枚举
  • ElementType.METHOD:方法
  • ElementType.FIELD:字段
  • ElementType.PARAMETER:方法参数
  • ElementType.CONSTRUCTOR:构造方法
  • ElementType.LOCAL_VARIABLE:局部变量
  • ElementType.ANNOTATION_TYPE:注解类型
  • ElementType.PACKAGE:包

例如,如果你希望你的注解只能应用在方法上,你可以这样定义:

@Target(ElementType.METHOD)

2、@Retention

@Retention 注解用于指定注解的保留策略,即注解在编译时、运行时或者在类文件中都保留。它的值是一个 RetentionPolicy 枚举,包括:

  • RetentionPolicy.SOURCE注解仅保留在源代码中,在编译时丢弃
  • RetentionPolicy.CLASS注解保留到类文件中,在运行时丢弃(默认值)
  • RetentionPolicy.RUNTIME:注解保留到运行时,可以通过反射获取

例如,如果你希望你的注解在运行时可用,你可以这样定义:

@Retention(RetentionPolicy.RUNTIME)

3、@Documented

@Documented 注解表示该注解应该被 javadoc 工具记录,因此可以在生成的文档中看到该注解及其说明。它没有任何属性,只需将其放在注解声明之前即可。

例如:

@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    // 注解的定义
}

在生成的文档中,你将能够看到使用了 MyAnnotation 的地方以及相关说明。

4、@Inherited

@Inherited 注解表示该注解可以被子类继承。当一个类使用了被 @Inherited 注解的注解时,其子类也会继承该注解。需要注意的是,@Inherited 只对类的继承有效,对接口、方法、字段等不起作用。

例如:

@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    // 注解的定义
}

在这个例子中,如果一个父类被 @MyAnnotation 注解修饰,在子类中同样会被继承。

这些元注解可以帮助你更灵活地定义和使用自定义注解,根据具体需求选择合适的元注解来控制注解的行为。

二、自定义注解实现

1、创建注解

自定义注解:CustomAnnotation

package com.kdz.demo.annotation;

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 CustomAnnotation {
    String value1() default "";
    String value2() default "";
}

2、AOP实现

创建AOP切面类CustomAspect,指定自定义注解为切入点

指定自定义注解为切入点,定义了切入点表达式和前后通知

package com.kdz.demo.Aspect;

import com.kdz.demo.annotation.CustomAnnotation;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Slf4j
@Aspect
@Component
public class CustomAspect {

    private static final Logger logger = LoggerFactory.getLogger(CustomAspect.class);

    //指定自定义注解为切入点
    @Pointcut("@annotation(customAnnotation)")
    public void customAnnotationPointcut(CustomAnnotation customAnnotation) {}

    @Before("@annotation(customAnnotation)")
    public void beforeAdvice(CustomAnnotation customAnnotation) {
        System.out.println("Before advice: " + customAnnotation.value1() + " - " + customAnnotation.value2());
    }

    @After("@annotation(customAnnotation)")
    public void afterAdvice(CustomAnnotation customAnnotation) {
        System.out.println("After advice: " + customAnnotation.value1() + " - " + customAnnotation.value2());
    }

    @Around("@annotation(customAnnotation)")
    public Object aroundAdvice(ProceedingJoinPoint joinPoint, CustomAnnotation customAnnotation) throws Throwable {
        long startTime = System.currentTimeMillis();

        Object result = joinPoint.proceed();

        long endTime = System.currentTimeMillis();
        long executionTime = endTime - startTime;

        logger.info("Method: " + joinPoint.getSignature().getName());
        logger.info("Execution Time: " + executionTime + "ms");
        Object[] args = joinPoint.getArgs();
        for (int i = 0; i < args.length; i++) {
            logger.info("Parameter " + (i + 1) + ": " + args[i]);
        }
        logger.info("Annotation: " + customAnnotation.value1() + " - " + customAnnotation.value2());

        return result;
    }
}

在这个更新后的切面类中,我们使用了@Around通知来围绕目标方法的执行。在方法执行前记录了开始时间,在方法执行后记录了结束时间,并计算了方法的执行时间。我们还获取了方法的参数,并将这些信息都记录在日志中。

3、使用自定义注解

package com.kdz.demo.service;

import com.kdz.demo.annotation.CustomAnnotation;
import org.springframework.stereotype.Service;

@Service
public class DemoService {

    @CustomAnnotation(value1 = "湖人总冠军", value2 = "666LBJ666")
    public void doSomething() {
        System.out.println("Doing something...");
    }
}

4、测试

在这里插入图片描述

package com.kdz.demo;

import com.kdz.demo.service.DemoService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class UserDefinedAnnotationApplicationTests {

    @Autowired
    DemoService demoService;
    @Test
    void contextLoads() {
        demoService.doSomething();
    }
}

在这里插入图片描述

三、配合MVC拦截器判断是否携带token

1、创建注解

自定义注解:TokenRequired

package com.kdz.demo.annotation;

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 TokenRequired {
}

2、AOP实现

创建AOP切面类TokenRequiredAspect,指定自定义注解为切入点

指定自定义注解为切入点,定义了切入点表达式前置通知

package com.kdz.demo.Aspect;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class TokenRequiredAspect {

    private static final Logger logger = LoggerFactory.getLogger(TokenRequiredAspect.class);

    @Before("@annotation(com.kdz.demo.annotation.TokenRequired)")
    public void beforeTokenRequiredMethod() {
        logger.info("Executing method with TokenRequired annotation...");
    }
}

3、使用自定义注解

package com.kdz.demo.controller;

import com.kdz.demo.annotation.TokenRequired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
public class DemoController {

    @GetMapping("/hello")
    @TokenRequired
    public String hello() {
        return "Hello, World!";
    }
}

4、配置启动类

package com.kdz.demo;

import com.kdz.demo.config.AuthenticationHandlerInterceptor;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@SpringBootApplication
public class UserDefinedAnnotationApplication implements WebMvcConfigurer {

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

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new AuthenticationHandlerInterceptor());
    }

}

​ 我们的应用程序的启动类 UserDefinedAnnotationApplication 上添加了 @SpringBootApplication 注解,并实现了 WebMvcConfigurer 接口,并重写了 addInterceptors 方法,在这个方法中注册了我们的拦截器 AuthenticationHandlerInterceptor。这样,拦截器就会被正确地加载和注册到应用程序中。

5、拦截器实现

拦截器判断是否使用注解及是否携带特定token

package com.kdz.demo.config;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.kdz.demo.annotation.TokenRequired;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;


@Component
public class AuthenticationHandlerInterceptor implements HandlerInterceptor {

    private static final Logger logger = LoggerFactory.getLogger(AuthenticationHandlerInterceptor.class);

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        logger.debug("进入拦截器, URL: {}", request.getServletPath());

        if (!(handler instanceof HandlerMethod)) {
            return true;
        }

        HandlerMethod handlerMethod = (HandlerMethod) handler;

        // 检查请求的方法是否添加了 TokenRequired 注解
        if (!handlerMethod.hasMethodAnnotation(TokenRequired.class)) {
            logger.debug("接口未添加 TokenRequired 注解,直接放行");
            return true;
        }

        // 获取请求中的 token
        String token = request.getHeader("Authorization");

        // 进行 token 验证
        if (token != null && token.equals("Bearer LBJ666")) {
            return true; // Token 验证通过,允许请求通过
        } else {
            // Token 验证失败,返回未授权错误
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
            return false;
        }
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    }
}

6、测试效果

1)未使用自定义注解TokenRequired

在这里插入图片描述

在这里插入图片描述

2)使用注解

使用正确token

在这里插入图片描述

在这里插入图片描述

使用错误token

在这里插入图片描述

在这里插入图片描述
SpringBoot-自定义注解AOP实现及拦截器示例 到此完结,笔者归纳、创作不易,大佬们给个3连再起飞吧

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1608225.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

响应式修改 页面字体字号【大 中 小 】

浅浅记录下&#xff0c;工作中遇到的问题&#xff0c;修改页面文本字号。 <p class"change_fontSize">[ 字号 <a href"javascript:doZoom(18)">大</a><a href"javascript:doZoom(16)">中</a><a href"ja…

Java精品项目--第8期基于SpringBoot的宠物用品商城的设计分析与实现

项目使用技术栈 SpringBootThymeleafMyBatisMySQLAopJavajdk1.8 项目介绍 项目截图

嘉立创EDA基础

一&#xff0c;原理图部分 &#xff08;1&#xff09;路径设置 打开嘉立创以后&#xff0c;在右上角点击设置 可以看到下图 左边栏所有工程为工程路径&#xff0c;常用库为库路径 &#xff08;2&#xff09;模式设置 同样点击设置&#xff0c;可以看到下面界面 下图为在线系…

【读点论文】YOLOX: Exceeding YOLO Series in 2021,无锚框单阶段目标检测方案,解耦检测头的分类和回归分支,优化标签分配策略

YOLOX: Exceeding YOLO Series in 2021 Abstract 在本报告中&#xff0c;我们介绍了YOLO系列的一些经验改进&#xff0c;形成了一种新的高性能探测器—YOLOX。我们将YOLO检测器切换到无锚方式&#xff0c;并进行其他先进的检测技术&#xff0c;即去耦头和领先的标签分配策略S…

【Java EE】依赖注入DI详解

文章目录 &#x1f334;什么是依赖注入&#x1f340;依赖注入的三种方法&#x1f338;属性注入(Field Injection)&#x1f338;构造方法注入&#x1f338;Setter注入&#x1f338;三种注入优缺点分析 &#x1f333;Autowired存在的问题&#x1f332;解决Autowired对应多个对象问…

Compose Canvas

文章目录 Compose Canvas概述Canvas属性drawPoints 绘制点drawPoints属性使用 drawLine 绘制线drawLine属性使用 drawRect 绘制矩形drawRect属性使用 drawRoundRect 绘制圆角矩形drawRoundRect属性使用 drawCircle 绘制圆drawCircle属性使用 drawOval 绘制椭圆drawOval属性使用…

【宝德PI300T G2智能小站开发教程(三)】centos配置系统开发源

目录 一.前言 二.配置dns服务器 三.测试连通性 四.设置更新源文件 一.前言 openeular系统的宝德板子没有更新的国内源,要如何配置? 二.配置dns服务器 vi /etc/resolv.conf 添加如下内容: nameserver 8.8.8.8 nameserver 114.114.114.114 三.测试连通性 ping www.ba…

Python教学入门:流程控制

条件语句&#xff08;if 语句&#xff09;&#xff1a; 条件语句用于根据条件的真假执行不同的代码块。 x 10if x > 0: # 如果 x 大于 0print("x 是正数") # 输出&#xff1a;x 是正数 elif x 0: # 如果 x 等于 0print("x 是零") else: # 如果以…

【C语言】每日一题,快速提升(7)!

&#x1f525;博客主页&#x1f525;&#xff1a;【 坊钰_CSDN博客 】 欢迎各位点赞&#x1f44d;评论✍收藏⭐ 题目&#xff1a;X图形 示例&#xff1a; 输入&#xff1a; 5 //几行数输出&#xff1a; …

【GlobalMapper精品教程】074:从Lidar点云创建3D地形模型

本文基于地形点云数据,基于泊松方法、贪婪三角形测量方法和阿尔法形状创建3d地形模型。 文章目录 一、加载地形点云数据二、创建三维地形模型1. 泊松方法2. 贪婪三角形测量方法3. 阿尔法形状注意事项一、加载地形点云数据 加载配套案例数据包中的data074.rar中的地形点云数据…

【C语言】【数据结构】项目实践——贪吃蛇游戏(超详细)

前言 本篇博客我们来实现一个小游戏项目——贪吃蛇&#xff0c;相信肯定很多人都玩过&#xff0c;那么整个贪吃蛇是怎么实现出来的那&#xff0c;这个项目用到了很多方面的知识&#xff1a;C语言函数、枚举、结构体、动态内存管理、预处理指令、链表、Win32 API等。我们就通过这…

《QT实用小工具·三十二》九宫格炫酷主界面

1、概述 源码放在文章末尾 项目实现了九宫格炫酷主界面&#xff0c;下面是项目demo演示&#xff1a; 项目部分代码如下&#xff1a; #pragma execution_character_set("utf-8")#include "frmmain.h" #include "ui_frmmain.h"frmMain::frmMain…

DeepWalk论文精读

介绍 图神经网络的开山之作 DeepWalk&#xff1a;一种用于学习网络中顶点的潜在表示的新方法&#xff0c;使用随机行走中获得的局部信息&#xff0c;通过将序列视为句子&#xff0c;节点视为单词 通过随机游走可以采样出一个序列&#xff0c;序列好比一句话&#xff0c;节点…

postgresql数据库pg_dirtyread插件闪回技术 —— 筑梦之路

闪回查询&#xff08;Flashback Query&#xff09;是一种在数据库中执行时间点查询的技术。它允许查询数据库中过去某个时间点的数据状态&#xff0c;并返回相应的查询结果。通常闪回查询分为表级以及行级的闪回查询。PostgreSQL数据库由于MVCC的机制&#xff0c;对于DML的操作…

初学python记录:力扣1883. 准时抵达会议现场的最小跳过休息次数

题目&#xff1a; 给你一个整数 hoursBefore &#xff0c;表示你要前往会议所剩下的可用小时数。要想成功抵达会议现场&#xff0c;你必须途经 n 条道路。道路的长度用一个长度为 n 的整数数组 dist 表示&#xff0c;其中 dist[i] 表示第 i 条道路的长度&#xff08;单位&…

怎么配置python

右键点击“计算机”&#xff0c;选择“属性”。 在左侧栏找到“高级系统设置”。 点击“环境变量”。 在系统变量中&#xff0c;双击“Path”。 在字符串的末尾&#xff0c;加一个分号; 然后再输入你安装python的路径&#xff0c;如图所示&#xff1a; 点击“确定”&#xff0…

使用Docker搭建一主二从的redis集群

文章目录 一、根据基础镜像构建三个docker容器二、构建master机三、配置slave机四、测试 本文使用 主机指代 物理机、 master机指代“一主二从”中的 一主&#xff0c; slave机指代“一主二从”中的 二从 一、根据基础镜像构建三个docker容器 根据本文第一章&#xff08…

有哪些公认好用且免费的云渲染网渲平台?渲染100邀请码1a12

现在云渲染是越来越火了&#xff0c;无论是在建筑设计、影视动画还是效果图行业都有它的身影&#xff0c;云渲染能缩短制作周期&#xff0c;提高工作效率&#xff0c;那么市面上有哪些公认好用且免费的云渲染平台呢&#xff1f;这次我们来了解下。 首先&#xff0c;我们来看看有…

使用名称空间共享集群

&#x1f4d5;作者简介&#xff1a; 过去日记&#xff0c;致力于Java、GoLang,Rust等多种编程语言&#xff0c;热爱技术&#xff0c;喜欢游戏的博主。 &#x1f4d8;相关专栏Rust初阶教程、go语言基础系列、spring教程等&#xff0c;大家有兴趣的可以看一看 &#x1f4d9;Jav…