SpringBoot自定义注解

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;
    }
    @ArgIntercept
    public 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 {
    @Override
    public 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 {
    @Override
    public 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")
    @LoginIntercept
    public 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 {
    @TableId
    private 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: root
    password: 3fa4d180
    driver: com.mysql.cj.jdbc.Driver
    url: 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 {
    @Autowired
    private 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 {
    @Override
    public 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 {
    @Override
    public 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 {
    @Autowired
    private 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")
    @AdminIntercept
    public 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: root
    password: 3fa4d180
    driver: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/young?useSSL=false&serverTimezone=UTC
  redis:
    host: 127.0.0.1
    port: 6379
    jedis:
      max-idle: 8
      pool:
        max-active: 8
        min-idle: 0
        max-wait: 3000
    timeout: 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 {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Override
    public 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;
    }
    @Override
    public 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")
    @LimitIntercept
    public 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)

  • 3
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值