Spring boot自定义注解

这篇具有很好参考价值的文章主要介绍了Spring boot自定义注解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Spring Boot简单实现自定义注解

1.实现自定义入参打印和方法执行时间统计(AOP实现)

  1. 定义一个注解类

@Documented 如果一个注解@B,被@Documented标注,那么被@B修饰的类,生成文档时,会显示@B。如果@B没有被@Documented标准,最终生成的文档中就不会显示@B@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface LogPrint {
    String value() default "";
}

@Target表示注解可以使用到哪些地方,可以是类,方法,或者是属性上,定义在ElementType枚举中:
public enum ElementType {
    TYPE,				/* 类、接口(包括注释类型)或枚举声明  */
    FIELD,				/* 字段声明(包括枚举常量)  */
    METHOD,				/* 方法声明  */
    PARAMETER,	 		/* 形式参数声明  */
    CONSTRUCTOR,		/* 构造方法声明  */
    LOCAL_VARIABLE,		/* 局部变量声明  */
    ANNOTATION_TYPE,	/* 注释类型声明  */
    PACKAGE,			/* 包声明  */
    TYPE_PARAMETER,		/* 类型参数声明 @since 1.8*/
    TYPE_USE			/* 任何类型声明 @since 1.8*/
}
@Retention作用是定义被它所注解的注解保留多久,一共有三种策略,定义在RetentionPolicy枚举中:
package java.lang.annotation;

public enum RetentionPolicy {

    SOURCE,	/* 注释将被编译器丢弃。*/

    CLASS,	/* 注释由编译器记录在类文件中,但不需要在运行时由VM保留。默认。*/

    RUNTIME	/*注释将由编译器记录在类文件中,并在运行时由VM保留,因此可以反射性地读取它们。*/
}
  1. 使用AOP对注解进行解析,需要定义一个切面类,包括自定义的切点方法normalPointCut(),以及连接点的处理方法normalPointAround()。连接点中的ProceedingJoinPoint可以获取被代理类的方法属性等。
@Aspect
@Component
public class LogPrintInterceptor {


    @Pointcut("@annotation([注解所在的位置如:cyh.zhujie.LogPrint])")  // @annotation(注解类型):匹配被调用的方法上有指定的注解。
    public void logPrint() { // 
    }
    
   @Around("logPrint()")
    public Object beforeMethod(ProceedingJoinPoint joinPoint) throws Throwable {
  log.info("{}方法{}传入参数为:{}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName(), joinPoint.getArgs());
        LocalTime start = LocalTime.now();
        log.info("{}方法{}开始执行时间{}:", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName(), start);
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        Object object = joinPoint.proceed(joinPoint.getArgs());
        stopWatch.stop();
        LocalTime end = LocalTime.now();
        log.info("{}方法{}结束执行时间{}:", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName(), end);
        log.info("{}方法{}共用时{}毫秒:", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName(), stopWatch.getTotalTimeMillis());
        return object;
    }


}

2.使用参数解释器对注解进行登录校验处理并返回登录信息

2.1 定义注解

@Target(ElementType.PARAMETER) // 形式参数声明
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface checkLogin {
}

2.2 实现参数解释器


@Slf4j
public class CheckInfoResolver implements HandlerMethodArgumentResolver {

    private LoginUserService loginUserService;
    
    public CheckInfoResolver(LOginUserService loginUserService){
        this.loginUserService = loginUserService;
    }
    
	// 方法supportsParameter很好理解,返回值是boolean类型,它的作用是判断Controller层中的参数,是否满足条件,满足条件则执行resolveArgument方法,不满足则跳过。
    //而resolveArgument方法呢,它只有在supportsParameter方法返回true的情况下才会被调用。用于处理一些业务,将返回值赋值给Controller层中的这个参数。

//因此呢,我们可以将HandlerMethodArgumentResolver理解为是一个参数解析器,我们可以通过写一个类实现HandlerMethodArgumentResolver接口来实现对Controller层中方法参数的修改。
    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
        return methodParameter.hasParameterAnnotation(checkLogin.class)
                && methodParameter.getParameterType().equals(LoginUser.class);
    }

    @Override
    public Object resolveArgument(MethodParameter methodParameter,
                                  ModelAndViewContainer modelAndViewContainer,
                                  NativeWebRequest nativeWebRequest,
                                  WebDataBinderFactory webDataBinderFactory) throws Exception {
        HttpServletRequest request = nativeWebRequest.getNativeRequest(HttpServletRequest.class);
        assert request != null;
        String token = request.getHeader("token");
        if (StrUtil.isEmpty(authorization)) {
            log.error("Token 为空,请检查请求头");
            throw new BaseException(500, "请登录后再操作");
        }
		LoginUser user = LoginUserService.checkLogin(token);// 校验,如果通过返回登录用户信息
        log.info(" 校验用户信息:{}", user);
        if (objectUtil.isNotNull(user)) {
            return user;
        } 
            throw new BaseException(500, "token失效,请重新登录!");
    }

}

3. 使用注解校验权限

3.1 创建注解

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface CheckPermission {
    /**
     * 是否需要登录 默认为true 设置未false只在方法上有效 比如在类上设置为true
     * 类下方法都需要登录 此时在某个方法下设置为false 则这个方法任然不需要登录
     *
     * @return
     */
    boolean value() default true;

}

3.2 继承HandlerInterceptorAdapter类

@Slf4j
public class UserPermissionInterceptor extends HandlerInterceptorAdapter {

    @Autowired
    private LoginUserService userService;

    //过滤未登录用户
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        this.hasPermission(handler, request, response);
        return true;
    }
    //判断是否登录
    private boolean isLogin(HttpServletRequest request, HttpServletResponse response) {

        String authorization = request.getHeader("token");

        if (!StrUtil.isEmpty(token)) {

           LoginUser user = LoginUserService.checkLogin(token);// 校验,如果通过返回登录用户信息

            if (ObjectUtil.isNotNull(user)) {
            		return true;
            } else {
                throw new BaseException(500, "没有登录");
            }
        }
			throw new BaseException(500, "没有登录");
    }


    /**
     * 是否有权限
     */
    private boolean hasPermission(Object handler, HttpServletRequest request, HttpServletResponse response) {
        boolean bool = true;
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            //获取类上的注解
            CheckPermission loginClass = handlerMethod.getMethod().getDeclaringClass().getAnnotation(CheckPermission.class);
            // 获取方法上的注解
            CheckPermission loginMethod = handlerMethod.getMethod().getAnnotation(CheckPermission.class);
            //如果类上加了注解进行拦截
            if (null != loginClass) {
                //如果这个方法有注解 且 设置了为不登录则通过 否则检验
                if (null != loginMethod && !loginMethod.value()) {
                    bool = true;
                } else {
                    bool = this.isLogin(request, response);
                }
            } else if (null != loginClass) {
                //如果类上没注册则检查方法
                if (null != loginMethod && loginMethod.value()) {
                    bool = this.isLogin(request, response);
                } else {
                    bool = true;
                }
            } else {
                bool = this.isLogin(request, response);
            }
        }

        if (!bool) {
            throw new BaseException(HttpStatus.HTTP_BAD_METHOD, "没有权限,请检查!");
        }

        return bool;
    }
}

4. 参数解释器和拦截器配置

@Slf4j
@Configuration
public class DefaultWebMvcConfig implements WebMvcConfigurer {

    @Value("yml文件获取")
    private String[] excludeList;


    @Lazy
    @Autowired
    private LoginUserService userService;

    
    // 注册权限拦截器
    @Bean
    public UserPermissionInterceptor userPermissionInterceptor() {
        return new UserPermissionInterceptor();
    }
	
    // 添加参数解释器
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(new CheckInfoResolver(userService));
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        List<String> pathList = new ArrayList<>();
        pathList.add("/**");

        // 接口权限功能
   registry.addInterceptor(userPermissionInterceptor()).addPathPatterns(pathList).excludePathPatterns(excludeList);
    }
}

5. 自定义注解简单实时接口请求限制

5.1 定义注解

/**
 * @author cyh
 * DATE 2023/4/26
 **/
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LimitRequest {

    /**
     * 默认请求次数
     *
     * @return
     */
    int num() default 10;

    /**
     * 默认时间 秒为单位 默认60秒内不能超过10次
     *
     * @return
     */
    long time() default 60L;

    /**
     * 限制时间 超过请求次数限制60秒 (以秒为单位)
     *
     * @return
     */
    long limitTime() default 60L;
}

5.2 实现HandlerInterceptor接口进行注解处理


/**
 * @author cyh
 * DATE 2023/4/26
 **/
@Slf4j
public class LimitRequestInterceptor implements HandlerInterceptor {

    @Resource
    RedisUtil redisUtil;

    /**
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        checkLimitNum(request, response, handler);
        return true;
    }

    private void checkLimitNum(HttpServletRequest request, HttpServletResponse response, Object handler) {
        String ip = ServletUtil.getClientIP(request);
        log.info("现在进行接口防刷次数判断:{}", ip);

        if (StrUtil.isNotBlank(ip)) {
            log.info("{}", ip);
            if (handler instanceof HandlerMethod) {

                HandlerMethod methodHandle = (HandlerMethod) handler;
                // 获取方法上的注解
                LimitRequest annotation = methodHandle.getMethod().getAnnotation(LimitRequest.class);
                // 如果获取到注解
                if (ObjUtil.isNotNull(annotation)) {
                    // 获取限制次数, 规定时间, 现在时间
                    int num = annotation.num();
                    long time = annotation.time();
                    long limitTime = annotation.limitTime();
                    // 获取该ip是否已经被锁住
                    Object lock = redisUtil.get("limitRequestLock:" + ip);
                    // 被锁住 返回抛出异常
                    if (ObjUtil.isNotNull(lock)) {
                        long expire = redisUtil.getExpire("limitRequestLock:" + ip);
                        throw new BaseException(500, "访问次数限制,请" + expire + "秒后重试");
                    }
                    // 无 则获取已经访问次数
                    Object ipRequestNum = redisUtil.get("ipRequestNum:" + ip);
                    // 访问次数为空 第一次访问
                    if (ObjUtil.isNull(ipRequestNum)) {
                        // 添加redis 访问次数,设置过期时间
                        redisUtil.set("ipRequestNum:" + ip, 1, time);
                        return;
                    } else {
                        int ipNum = Integer.parseInt(ipRequestNum.toString());
                        // 如果访问次数超过限制次数 锁住,返回
                        if (ipNum >= num) {
                            redisUtil.set("limitRequestLock:" + ip, "lock", limitTime);
                            throw new BaseException(500, "访问太频繁了,请稍后重试");
                        }
                        // 否则访问次数+1
                        redisUtil.incrByNumber("ipRequestNum:" + ip, 1d);
                        return;
                    }
                }
            }

        }

        throw new BaseException(500, "访问ip为空");
    }
}

记得实现WebMvcConfigurer 接口配置LimitRequestInterceptor

@Bean
public LimitRequestInterceptor getLimitRequestInterceptor() {
return new LimitRequestInterceptor();
}

@Override
public void addInterceptors(InterceptorRegistry registry) {

​ // 接口权限功能
​ registry.addInterceptor(getLimitRequestInterceptor());
}

6.总结

在Spring Boot中,自定义注解通常用于将某些特定的行为或操作与注解相关联。这些行为可以在运行时通过反射进行动态处理。AOP和拦截器是Spring Boot中用于处理这种类型特定行为或操作的两种常见技术。 当需要在方法执行前或执行后执行某些通用的或共享的操作时,可以使用AOP技术。 比如,记录所有方法的执行时间、对方法的输入参数进行安全控制等。使用AOP,您可以轻松地将这些通用操作与所有带有特定注解的方法相关联。AOP可以结合使用自定义注解和AspectJ语法来编写切面。 当需要拦截和处理所有请求时,如验证用户身份、监控请求、记录日志等,可以使用拦截器技术。拦截器可以拦截并处理进入应用程序和离开应用程序的HTTP请求和响应。在Spring Boot中,拦截器通常用于处理Web请求。使用自定义注解时,可以拦截并处理带有特定注解的请求。 总之,AOP和拦截器都是处理自定义注解的有效技术。使用AOP处理注解时,注重方法的处理,而使用拦截器处理注解时,注重整个请求的处理。因此,具体使用哪种技术,取决于您想要实现的功能和需要处理的场景。文章来源地址https://www.toymoban.com/news/detail-426387.html

到了这里,关于Spring boot自定义注解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • Spring Boot 中自定义数据校验注解

    Spring Boot 中自定义数据校验注解

    在 Spring Boot 中,我们可以使用 JSR-303 数据校验规范来校验表单数据的合法性。JSR-303 提供了一些常用的数据校验注解,例如 @NotNull 、 @NotBlank 、 @Size 等。但是,在实际开发中,我们可能需要自定义数据校验注解来满足特定的需求。本文将介绍如何在 Spring Boot 中自定义数据校

    2024年02月10日
    浏览(8)
  • Spring Boot 自定义注解,AOP 切面统一打印出入参请求日志

    Spring Boot 自定义注解,AOP 切面统一打印出入参请求日志

    今天主要说说如何通过自定义注解的方式,在 Spring Boot 中来实现 AOP 切面统一打印出入参日志。小伙伴们可以收藏一波。 废话不多说,进入正题! 在看看实现方法之前,我们先看下切面日志输出效果咋样: 从上图中可以看到,每个对于每个请求,开始与结束一目了然,并且

    2024年02月08日
    浏览(10)
  • Spring Boot入门(23):记录接口日志再也不难!用AOP和自定义注解给Spring Boot加上日志拦截器!

    Spring Boot入门(23):记录接口日志再也不难!用AOP和自定义注解给Spring Boot加上日志拦截器!

            在上两期中,我们着重介绍了如何集成使用 Logback 与 log4j2 日志框架的使用,今天我们讲解的主题依旧跟日志有关,不过不是使用何种开源框架,而是自己动手造。         Spring的核心之一AOP;AOP翻译过来叫面向切面编程, 核心就是这个切面. 切面表示从业务逻辑中

    2024年02月11日
    浏览(9)
  • spring-boot 请求参数校验:注解 @Validated 的使用、手动校验、自定义校验

    spring-boot中可以用@validated来校验数据,如果数据异常则会统一抛出异常,方便异常中心统一处理。 spring-boot已经引入了基础包,所以直接使用就可以。 在属性上添加校验注解: 在Controller上添加 @Validated 注解 校验未通过时,可能看到: 在 @Validated 后面紧跟着追加BindingResult,

    2023年04月16日
    浏览(18)
  • Spring Boot项目中不使用@RequestMapping相关注解,如何动态发布自定义URL路径

    Spring Boot项目中不使用@RequestMapping相关注解,如何动态发布自定义URL路径

    在Spring Boot项目开发过程中,对于接口API发布URL访问路径,一般都是在类上标识 @RestController 或者 @Controller 注解,然后在方法上标识 @RequestMapping 相关注解,比如: @PostMapping 、 @GetMapping 注解,通过设置注解属性,发布URL。在某些场景下,我觉得这样发布URL太麻烦了,不适用,

    2024年03月22日
    浏览(9)
  • Spring Boot中的@Scheduled注解:定时任务的原理与实现

    Spring Boot中的@Scheduled注解:定时任务的原理与实现

    本文将详细探讨Spring Boot中@Scheduled注解的使用,包括其原理、实现流程、步骤和代码示例。通过本文,读者将能够了解如何在Spring Boot应用中轻松创建和管理定时任务。 在Spring框架中,@Scheduled注解用于标记一个方法,使其能够在固定的时间间隔内自动执行。这个注解主要基于

    2024年02月22日
    浏览(14)
  • 【Spring】使用自定义注解方式实现AOP鉴权

    AOP,是一种面向切面编程,可以通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。 在软件开发中,鉴权(Authentication)是一项非常重要的安全措施,用于验证用户身份和权限。在应用程序中,我们通常会使用AOP(Aspect-Oriented Programming)来实现鉴权功能

    2024年02月11日
    浏览(11)
  • Spring Boot中整合MyBatis(基于xml方式&基于注解实现方式)

    Spring Boot中整合MyBatis(基于xml方式&基于注解实现方式)

    在Spring Boot中整合MyBatis时,你需要导入JDBC(不需要手动添加)、Druid的相关依赖、MySQL相关依赖。 JDBC依赖:在Spring Boot中整合MyBatis时,并不需要显式地添加JDBC的包依赖。这是因为,当你添加 mybatis-spring-boot-starter 依赖时,它已经包含了对JDBC的依赖。 mybatis-spring-boot-starter 是

    2024年02月15日
    浏览(15)
  • Spring如何使用自定义注解来实现自动管理事务?

    Spring如何使用自定义注解来实现自动管理事务?

    人可以做他(她)想做的,但不能要他(她)想要的 在两年半以前,我写了一篇博客:框架的灵魂之注解基础篇: 在那篇博客的结尾,我埋了一个坑: 如今,我练习时长达 两年半 ,终于摔锅归来! 本篇博客基于SpringBoot整合MyBatis-plus,如果有不懂这个的, 可以查看我的这篇博客

    2024年01月25日
    浏览(9)
  • 对接第三方接口鉴权(Spring Boot+Aop+注解实现Api接口签名验证)

    对接第三方接口鉴权(Spring Boot+Aop+注解实现Api接口签名验证)

    一个web系统,从接口的使用范围也可以分为对内和对外两种,对内的接口主要限于一些我们内部系统的调用,多是通过内网进行调用,往往不用考虑太复杂的鉴权操作。但是,对于对外的接口,我们就不得不重视这个问题,外部接口没有做鉴权的操作就直接发布到互联网,而

    2024年04月29日
    浏览(58)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包