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

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连再起飞吧

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Session缓存、Hibernate处理对象的状态了解

Session接口 Session接口是Hibernate向应用程序提供的操纵数据库的最主要的接口&#xff0c;它提供了基本的保存&#xff0c;更新&#xff0c;删除和查询的方法。 Session是有一个缓存, 又叫Hibernate的一级缓存 session缓存是由一系列的Java集合构成的。当一个对象被加入到…

SpringCloud集成SkyWalking链路追踪并收集日志2

博主介绍&#xff1a;✌全网粉丝5W&#xff0c;全栈开发工程师&#xff0c;从事多年软件开发&#xff0c;在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战&#xff0c;博主也曾写过优秀论文&#xff0c;查重率极低&#xff0c;在这方面有丰富的经验…

AI 文献综述工具

find sources that support this statement:

微信小程序 uniapp+vue.js医疗在线问诊挂号系统4oy17

预约挂号系统的逐渐发展&#xff0c;进一步方便了广大用户&#xff0c;使其可以更方便、快捷地预约挂号&#xff0c;并且也有效地防止号贩子“倒号”&#xff0c;使用户预约挂号更公平&#xff0c;然而现有预约挂号系统或多或少有所欠缺 小程序前端框架&#xff1a;uniapp 小程…

Tomcat无法成功启动——双击startup.bat闪退的解决办法

一、首先查看端口是否被占用了&#xff0c;一般Tomcat的默认端口是8080&#xff0c;可以在管理员命令行通过“netstat -ano|findstr "8080”"的命令查看当前是否有进程占用了端口。 1.如果端口占用了&#xff1a; 则根据PID&#xff08;进程id号&#xff09;来查这个…

kimichat使用技巧:用语音对话聊天

kimichat之前是只能用文字聊天的&#xff0c;不过最近推出了语音新功能&#xff0c;也可以用语音畅快的对话聊天了。 这个功能目前支持手机app版本&#xff0c;所以首先要在手机上下载安装kimi智能助手。已经安装的&#xff0c;要点击检查更新&#xff0c;更新到最新的版本。 …

期货开户交易绝境时保持理智

三国时代&#xff0c;诸葛亮以空城计骗过司马懿的数十万大军不战而退&#xff0c;也都是沉得住气。有智慧的人&#xff0c;越是紧急危难的时候&#xff0c;越是冷静沉著&#xff0c;唯有在镇静中才能想出应付事变的方法。所谓“饭未煮熟&#xff0c;不要妄自一开&#xff1b;蛋…

SGI_STL空间配置器源码剖析(六)deallocate函数

deallocate函数是内存释放函数。源码及注释如下&#xff1a; /* __p may not be 0 */static void deallocate(void* __p, size_t __n) // __p指向要回收的内存起始地址&#xff0c;__n表示其大小{if (__n > (size_t) _MAX_BYTES)// 大于128字节&#xff0c;普通方式开辟和回…

git am XXX.patch 文件内容解析

git am XXX.patch 文件内容解析 打补丁的两种方式&#xff1a; 1.patch XXX.patch 2.git am XXX.patch 例如&#xff1a; diff --git a/drivers/crypto/se/ce.c b/drivers/crypto/se/ce.c index e6f68286d4ce6..de1bcb46fbe6b 100644 --- a/drivers/crypto/se/ce.cb/drive…

Zookeeper中的节点类型和实现持久化的两种方式

进入zookeeper的bin目录&#xff0c;执行./zkServer.sh start ../conf/zoo.cfg启动&#xff01; Zookeeper内部的数据模型 类似于数据结构中的树&#xff0c;同时也很像文件系统的目录&#xff0c; 节点的类型 持久节点&#xff1a;create /znode 创建出的节点&#xff0c…

等保测评2.0——网络安全等级保护测评的初步了解

一、什么是网络安全等级保护测评&#xff1f; 二、网络安全等级保护&#xff0c;保护的是什么&#xff1f; 等级保护对象&#xff1a;网络安全等级保护工作直接作用的对象。&#xff08;注&#xff1a;主要包括信息系统、通信网络设施和数据资源等&#xff09; 计算机信息系统…

mac上如何安装python3

mac上如何安装python3&#xff1f; 安装homebrew 在终端执行命令 /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" 执行完成后&#xff0c;homebrew和pip等工具就自动安装好了。 接下来安装python3.在终端…

将数学表达式对分子分母先因式分解再约分化简simplify()

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 将数学表达式 对分子分母 先因式分解 再约分化简 simplify() [太阳]选择题 请问以下输出结果正确的是&#xff1a; from sympy import simplify from sympy.abc import x, y A (x**2 2*x 1)…

数据中心的网络架构设计,打造高效、安全的数字底座

数据中心的网络架构设计 一、数据中心网络架构设计原则 网络,作为数据中心的核心支柱,其结构精妙,由众多二层接入设备与少量三层设备共同编织而成。过去,数据中心网络规模有限,仅凭数十台设备的简单互连便能实现信息的畅通无阻。然而,随着技术与应用需求的飞速增长,数据…

目前深圳嵌入式单片机就业环境如何?

深圳作为中国的科技创新中心之一&#xff0c;嵌入式行业的就业环境相对较好。我这里有一套嵌入式入门教程&#xff0c;不仅包含了详细的视频讲解&#xff0c;项目实战。如果你渴望学习嵌入式&#xff0c;不妨点个关注&#xff0c;给个评论222&#xff0c;私信22&#xff0c;我在…

广州南沙番禺联想SR530服务器主板传感器故障维修

今日分享一例广州市南沙区联想ThinkSystem SR530服务器sensor sysbrd vol故障问题维修案例&#xff1b; 服务器型号是&#xff1a;Lenovo thinksystem sr530 g6服务器 服务器所在位置&#xff1a;广东省广州市南沙区 服务器故障问题&#xff1a;机房异常停电&#xff0c;来电后…

基于ssm微信小程序的医院挂号预约系统

采用技术 基于ssm微信小程序的医院挂号预约系统的设计与实现~ 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringMVCMyBatis 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 页面展示效果 用户管理 医院管理 医生管理 公告资讯管理 科室信息管…

Nginx讲解 很详细了!!!

hello&#xff0c;你好鸭&#xff0c;我是Ethan&#xff0c;很高兴你能来阅读&#xff0c;昵称是希望自己能不断精进&#xff0c;向着优秀程序员前行!&#x1f4aa;&#x1f4aa;&#x1f4aa; 目前博客主要更新Java系列、数据库、项目案例、计算机基础等知识点。感谢你的阅读和…

蓝桥杯嵌入式(G431)备赛——最后一晚查漏补缺

蓝桥杯嵌入式&#xff08;G431&#xff09;备赛笔记——初始化cubeMX 蓝桥杯嵌入式&#xff08;G431&#xff09;备赛笔记——LED 蓝桥杯嵌入式&#xff08;G431&#xff09;备赛笔记——按键模块设计 蓝桥杯嵌入式&#xff08;G431&#xff09;备赛笔记——LCD按键 蓝桥杯…

paddle实现手写数字模型(一)

参考文档&#xff1a;paddle官网文档环境&#xff1a;Python 3.12.2 &#xff0c;pip 24.0 &#xff0c;paddlepaddle 2.6.0 python -m pip install paddlepaddle2.6.0 -i https://pypi.tuna.tsinghua.edu.cn/simple调试代码如下&#xff1a; LeNet.py import paddle import p…