SpringBoot自定义注解

news/2024/12/2 10:21:55/

SpringBoot自定义注解

1. 创建一个注解的基本元素

修饰符:访问修饰符必须为public,不写默认为pubic;
关键字:关键字为@interface;
注解名称:注解名称为自定义注解的名称
注解类型元素:注解类型元素是注解中内容,根据需要标志参数,例如上面的注解的value;
规则总结如下:

  1. Annotation型定义为@interface, 所有的Annotation会自动继承java.lang.Annotation这一接口,并且不能再去继承别的类或是接口.
  2. 参数成员只能用public或默认(default)这两个访问权修饰
  3. 参数成员只能用基本类型byte,short,char,int,long,float,double,boolean八种基本数据类型和String、Enum、Class、annotations等数据类型,以及这一些类型的数组.
  4. 要获取类方法和字段的注解信息,必须通过Java的反射技术来获取 Annotation对象,因为你除此之外没有别的获取注解对象的方法
  5. 注解也可以没有定义成员, 不过这样注解就没啥用了

2. 元注解(@Target、@Retention、@Inherited、@Documented)

我们上面的创建的注解XinLinLog上面还有几个注解(@Target、@Retention、@Inherited、@Documented),这四个注解就是元注解,元注解的作用就是负责注解其他注解。Java5.0定义了4个标准的元注解类型,它们被用来提供对其它 注解类型作标志操作(可以理解为最小的注解,基础注解)
@Target:用于描述注解的使用范围,该注解可以使用在什么地方
在这里插入图片描述
@Retention:表明该注解的生命周期
在这里插入图片描述
@Inherited:是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。
@Documented:表明该注解标记的元素可以被Javadoc 或类似的工具文档化

3. 如何自定义注解

注解其实就是一种标记,可以在程序代码中的关键节点(类、方法、变量、参数、包)上打上这些标记,然后程序在编译时或运行时可以检测这些标记从而执行一些特殊操作,因此自定义注解使用的基本流程为:

  1. 第一步,定义注解 – 相当于定义标记
  2. 第二步,配置注解 – 把标记打在需要用到的程序代码中
  3. 第三步,解析注解 – 在编译期或运行时检测到标记,并进行特殊操作

4. 示例代码

创建一个maven项目,导入下列依赖:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><artifactId>spring-boot-starter-parent</artifactId><groupId>org.springframework.boot</groupId><version>2.7.0</version></parent><groupId>com.young</groupId><artifactId>Annotation02</artifactId><version>1.0-SNAPSHOT</version><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.83</version></dependency></dependencies><properties><maven.compiler.source>11</maven.compiler.source><maven.compiler.target>11</maven.compiler.target></properties></project>

目录结构如下:
在这里插入图片描述
application.yml:

server:port: 8089

Annotation02App.class

package com.young;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class Annotation02App {public static void main(String[] args) {SpringApplication.run(Annotation02App.class,args);}
}

4.1 创建一个ArgIntercept注解,用于类、接口、枚举的方法

package com.young.annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target({ElementType.METHOD,ElementType.TYPE})//注解范围为类,接口,枚举的方法上
@Retention(RetentionPolicy.RUNTIME) //被虚拟机保存,可用反射机制读取
public @interface ArgIntercept {boolean required()default true;
}

创建Person.class

package com.young.entity;import com.young.annotation.ArgIntercept;public class Person {private String name;private String mobile;private Integer age;private String sex;public Person(){}public Person(String name,String mobile,Integer age,String sex){this.name=name;this.mobile=mobile;this.age=age;this.sex=sex;}public void setName(String name){this.name=name;}public void setMobile(String mobile){this.mobile=mobile;}public void setAge(Integer age){this.age=age;}public void setSex(String sex){this.sex=sex;}public String getName(){return this.name;}@ArgInterceptpublic String getMobile(){return this.mobile;}@ArgIntercept(required = false)public Integer getAge(){return this.age;}public String getSex(){return this.sex;}
}

创建DemoController.java,用于测试

package com.young.controller;import com.young.annotation.ArgIntercept;
import com.young.entity.Person;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;import java.lang.reflect.Method;@RestController
public class DemoController {@GetMapping("/testMethod1")public String testMethod1()throws Exception{Person person=new Person("cxy","134****8118",22,"男");Method[] methods = Person.class.getMethods();String res="";for (Method m : methods) {String methodName=m.getName();if (!methodName.contains("get")||methodName.equals("getClass")){continue;}ArgIntercept declaredAnnotation = m.getDeclaredAnnotation(ArgIntercept.class);//当ArgIntercept注解值为true时,跳过if (declaredAnnotation!=null&&declaredAnnotation.required()){continue;}//只有没有ArgIntercept或者ArgIntercept的required为false时,才拼接字符串String temp=String.valueOf(m.invoke(person))+" ";res=res+temp;}return res;}
}

启动项目,访问http://localhost:8089/testMethod01
在这里插入图片描述

4.2 创建一个ClassIntercept,用于对象属性注解

创建一个User.java

package com.young.entity;import lombok.Data;import java.time.LocalDateTime;
import java.util.Date;@Data
public class User {private Integer id;private String username;private String password;private LocalDateTime loginTime;
}

创建LoginIntercept注解

package com.young.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 LoginIntercept {boolean required()default true;
}

ControllerConfiguration

package com.young.config;import com.young.annotation.LoginIntercept;
import com.young.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;import javax.annotation.Resource;@Configuration
public class ControllerConfiguration implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry){registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**");}
}

LoginInterceptor.java

package com.young.interceptor;import com.young.annotation.LoginIntercept;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request,HttpServletResponse response,Object handler)throws Exception{if (!(handler instanceof HandlerMethod)){return true;}HandlerMethod method=(HandlerMethod) handler;//判断是否有添加LoginIntercept注解LoginIntercept loginIntercept=method.getMethodAnnotation(LoginIntercept.class);if (loginIntercept==null||!loginIntercept.required()){//没有注解或注解的required为false,直接放行return true;}//鉴权String token = request.getHeader("token");if (token==null||!"token".equals(token)){//校验失败return false;}return true;}
}

修改DemoController.java

package com.young.controller;import com.young.annotation.ArgIntercept;
import com.young.annotation.LoginIntercept;
import com.young.entity.Person;
import com.young.entity.User;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;import java.lang.reflect.Method;
import java.time.LocalDateTime;@RestController
public class DemoController {@GetMapping("/testMethod1")public String testMethod1()throws Exception{Person person=new Person("cxy","134****8118",22,"男");Method[] methods = Person.class.getMethods();String res="";for (Method m : methods) {String methodName=m.getName();if (!methodName.contains("get")||methodName.equals("getClass")){continue;}ArgIntercept declaredAnnotation = m.getDeclaredAnnotation(ArgIntercept.class);//当ArgIntercept注解值为true时,跳过if (declaredAnnotation!=null&&declaredAnnotation.required()){continue;}//只有没有ArgIntercept或者ArgIntercept的required为false时,才拼接字符串String temp=String.valueOf(m.invoke(person))+" ";res=res+temp;}return res;}@GetMapping("/testMethod2")@LoginIntercept(required = false)public User testMethod2(){User user=new User();user.setUsername("not require login");user.setId(1);user.setPassword("123456");user.setLoginTime(LocalDateTime.now());return user;}@GetMapping("/testMethod3")@LoginInterceptpublic User testMethod3(){User user=new User();user.setUsername("require login");user.setId(2);user.setPassword("1234567");user.setLoginTime(LocalDateTime.now());return user;}
}

运行项目
测试testMethod2接口,放行成功,
不带token测试testMethod3
在这里插入图片描述
携带错误token访问testMethod3
在这里插入图片描述
携带正确token
在这里插入图片描述

4.3 创建一个RoleIntercept,用于权限校验

在数据库中创建一个m_user表,结构如下:
在这里插入图片描述
数据如下:
在这里插入图片描述
修改pom.xml,添加下面两个依赖

 <dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.3</version></dependency>

修改User.java

package com.young.entity;import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;import java.time.LocalDateTime;
import java.util.Date;@Data
@TableName(value = "m_user")
public class User {@TableIdprivate Integer id;private String username;private String password;private LocalDateTime loginTime;private String role;
}

UserVO.java

package com.young.vo;import lombok.Data;import java.io.Serializable;@Data
public class UserVO implements Serializable {private String username;private String password;
}

修改application.yml

server:port: 8089
spring:datasource:username: rootpassword: 3fa4d180driver: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/young?useSSL=false&serverTimezone=UTC

UserMapper.java

package com.young.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.young.entity.User;
import org.apache.ibatis.annotations.Mapper;@Mapper
public interface UserMapper extends BaseMapper<User> {
}

UserService.java

package com.young.service;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.young.entity.User;
import com.young.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class UserService {@Autowiredprivate UserMapper userMapper;public User login(String username,String password){LambdaQueryWrapper<User>queryWrapper=new LambdaQueryWrapper<>();queryWrapper.eq(User::getUsername,username).eq(User::getPassword,password);return userMapper.selectOne(queryWrapper);}
}

创建AdminIntercept注解

package com.young.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 AdminIntercept {boolean required()default true;
}

创建AdminInterceptor.java拦截器

package com.young.interceptor;import com.young.annotation.AdminIntercept;
import com.young.entity.User;
import com.young.service.UserService;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;public class AdminInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response,Object handler)throws Exception{if (!(handler instanceof HandlerMethod)){return true;}HandlerMethod method=(HandlerMethod) handler;//判断是否有adminIntercept注解AdminIntercept adminIntercept = method.getMethodAnnotation(AdminIntercept.class);if (adminIntercept==null||!adminIntercept.required()){//没有注解或注解的required为false,直接放行return true;}//获取会话中的用户User user=(User)request.getSession().getAttribute("user");//判断用户权限if (user==null){System.out.println("用户未登录");return false;}if(user.getRole()==null||!"admin".equals(user.getRole())){System.out.println("用户没有admin权限");return false;}return true;}
}

修改ControllerConfiguration

package com.young.config;import com.young.annotation.LoginIntercept;
import com.young.interceptor.AdminInterceptor;
import com.young.interceptor.LoginInterceptor;
import org.springframework.boot.autoconfigure.kafka.KafkaProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;import javax.annotation.Resource;@Configuration
public class ControllerConfiguration implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry){registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**");registry.addInterceptor(new AdminInterceptor()).addPathPatterns("/user/**");}
}

创建UserController.java

package com.young.controller;import com.young.annotation.AdminIntercept;
import com.young.entity.User;
import com.young.service.UserService;
import com.young.vo.UserVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserService userService;@PostMapping("/login")public String login(@RequestBody UserVO userVO, HttpServletRequest request){User user = userService.login(userVO.getUsername(), userVO.getPassword());if (user!=null){HttpSession session = request.getSession();session.setAttribute("user",user);return "登录成功";}return "登录失败";}@GetMapping("/info")@AdminInterceptpublic User info(HttpServletRequest request){User user = (User)request.getSession().getAttribute("user");return user;}
}

运行项目,测试
未登录测试/user/info
在这里插入图片描述
在这里插入图片描述
登录不是admin的用户
在这里插入图片描述
访问/user/info
在这里插入图片描述
在这里插入图片描述
登录有admin权限的用户
在这里插入图片描述
访问/user/info
在这里插入图片描述

4.4 使用自定义注解,整合Redis实现限流

pom.xml添加下列依赖

   <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>

修改application.yml

server:port: 8089
spring:datasource:username: rootpassword: 3fa4d180driver: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/young?useSSL=false&serverTimezone=UTCredis:host: 127.0.0.1port: 6379jedis:max-idle: 8pool:max-active: 8min-idle: 0max-wait: 3000timeout: 5000

LimitIntercept.java

package com.young.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 LimitIntercept {boolean required()default true;//设置默认5秒内最多点击3次 int maxCount()default  3; //默认最多点击3次int waitTime()default 5; //默认时长5秒
}

修改ControllerConfiguration.java

package com.young.config;import com.young.annotation.LoginIntercept;
import com.young.interceptor.AdminInterceptor;
import com.young.interceptor.LimitInterceptor;
import com.young.interceptor.LoginInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.kafka.KafkaProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;import javax.annotation.Resource;@Configuration
public class ControllerConfiguration implements WebMvcConfigurer {@Autowiredprivate StringRedisTemplate stringRedisTemplate;@Overridepublic void addInterceptors(InterceptorRegistry registry){registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**");registry.addInterceptor(new AdminInterceptor()).addPathPatterns("/user/**");registry.addInterceptor(new LimitInterceptor(stringRedisTemplate));}
}

RedisConstant.java

package com.young.constants;public class RedisConstant {public final static String LIMIT_KEY="limit";
}

添加LimitInterceptor.java

package com.young.interceptor;import com.young.annotation.LimitIntercept;
import com.young.constants.RedisConstant;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.invoke.MethodHandle;
import java.util.concurrent.TimeUnit;public class LimitInterceptor implements HandlerInterceptor {private StringRedisTemplate stringRedisTemplate;public LimitInterceptor(StringRedisTemplate stringRedisTemplate){this.stringRedisTemplate=stringRedisTemplate;}@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response,Object handler)throws Exception{if (!(handler instanceof HandlerMethod)){return false;}HandlerMethod method=(HandlerMethod) handler;LimitIntercept limitIntercept = ((HandlerMethod) handler).getMethodAnnotation(LimitIntercept.class);if (limitIntercept==null||!limitIntercept.required()){return true;}int maxCount=limitIntercept.maxCount();int waitTime=limitIntercept.waitTime();//当未过期时if (stringRedisTemplate.hasKey(RedisConstant.LIMIT_KEY)){Integer count = Integer.valueOf(stringRedisTemplate.opsForValue().get(RedisConstant.LIMIT_KEY));if (count<=0){System.out.println("限流了=============");return false;}//减少次数stringRedisTemplate.opsForValue().decrement(RedisConstant.LIMIT_KEY);return true;}//设置到redis中stringRedisTemplate.opsForValue().set(RedisConstant.LIMIT_KEY,maxCount+"",waitTime, TimeUnit.SECONDS);return true;}
}

添加LimitController.java

package com.young.controller;import com.young.annotation.LimitIntercept;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/limit")
public class LimitController {@GetMapping("/test1")@LimitInterceptpublic String test1(){return "test1";}@GetMapping("/test2")@LimitIntercept(maxCount = 1,waitTime = 10)public String test2(){return "test2";}
}

运行,测试
5秒内访问/limit/test1超过3次,开始限流
在这里插入图片描述
在这里插入图片描述
10秒内访问/limit/test2超过1次,开始限流
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述#### 5. 参考文章
SpringBoot自定义注解
springboot项目中自定义注解的使用总结、java自定义注解实战(常用注解DEMO)


http://www.ppmy.cn/news/59215.html

相关文章

c#笔记-数组

数组 声明数组 数组是一种可以声明多个同类型变量的数据结构&#xff0c;能替你声明多个变量。 并且其中的值可以通过索引动态访问&#xff0c;可以搭配循环批量处理这些值。 数组类型的写法是&#xff0c;在目标类型后加上一对中括号。 数组值没有字面量&#xff0c;需要构…

音频环回实验

音频环回实验 一、WM8978简介 WM8978是一个低功耗、高质量的立体声多媒体数字信号编译码器&#xff0c;它结合了一个高质量的立体声音DAC和ADC&#xff0c;带有灵活的音频线输入、麦克风输入和音频输出处理 WM8978内部有58个寄存器。每一个寄存器的地址位为7位&#xff0c;数…

22.碳交易机制下考虑需求响应的综合能源系统优化运行

说明书 MATLAB代码&#xff1a;碳交易机制下考虑需求响应的综合能源系统优化运行 注意&#xff1a;另外还有含义柔性负荷、蓄冷式空调、共享储能以及碳捕集的综合能源系统优化运行代码&#xff0c;欢迎咨询 关键词&#xff1a;需求响应 碳交易机制 日前优化调度 综合能源系统…

关于低代码开发,你是真的了解了吗?

在低代码开发已是大势所趋的今天&#xff0c;不少企业都切身感受到了低代码开发带来的便利。低代码开发平台的优势在当下数字化浪潮中&#xff0c;为企业提供了定制专属的数字化解决方案。 低代码本身没有太强的行业属性&#xff0c;这也让低代码开发平台能够更加灵活地适应不同…

pikachu靶场-Unsafe Filedownload

不安全的文件下载 文件下载功能在很多web系统上都会出现&#xff0c;一般我们当点击下载链接&#xff0c;便会向后台发送一个下载请求&#xff0c;一般这个请求会包含一个需要下载的文件名称&#xff0c;后台在收到请求后 会开始执行下载代码&#xff0c;将该文件名对应的文件…

2 行代码开启 SAST,将代码漏洞定位到具体行数

&#x1f4a1; 如何在流水线中集成与应用 SAST&#xff0c;实现自动化代码安全扫描 &#xff1f; 近日&#xff0c;在「DevSecOps软件安全开发实践」课程上&#xff0c;极狐(GitLab) 高级专业服务交付工程师欧阳希、极狐(GitLab) 后端工程师黄松&#xff0c;分享了静态安全扫描…

Android - 动画

一、概念 属性动画PeopertyAnimation&#xff1a;view的属性根据执行的动画发生真实的改变。通过不断设置 View 的属性实现。补间动画 ViewAnimation&#xff08;Tween&#xff09;&#xff1a;不改变view的位置和属性。基于 Framework的绘制转变。帧动画 DrawableAnimation&a…

【网络原理】TCP/IP协议

目录 1.应用层 2.传输层&#xff08;核心问题&#xff09; 2.1 UDP协议 2.1.2 UDP的特点 2.1.3 基于UDP的应用层协议 2.2 TCP协议&#xff08;重点内容&#xff09; 2.2.1 TCP/IP 协议含义 2.2.2 TCP协议端格式&#xff1a; 2.2.3 TCP的特点 2.3 TCP原理 2.4 确认应…