SpringBoot自定义注解开发指南

目录
  • 一、Java注解(Annotation)
    • 1、JDK基本注解
    • 2、JDK元注解
  • 二、自定义注解开发
    • 1、含义
    • 2、演示
  • 三、完成切面日志操作
  • 四、完成前端响应反应
  • 总结

一、Java注解(Annotation)

含义:Java注解是附加在代码中的一些元信息,用于一些工具在编译、 运行时进行解析和使用,起到说明、配置的功能。

1、JDK基本注解

@Override ——》重写

@Deprecated ——》已过时

@SuppressWarnings(value = "unchecked")  ——》压制编辑器警告

2、JDK元注解

含义:元注解用于修饰其他的注解(纪委:管干部的干部)

①、@Retention ——》定义注解的保留策略

@Retention(RetentionPolicy.SOURCE) //注解仅存在于源码中,在class字节码文件中不包含
@Retention(RetentionPolicy.CLASS)//默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得,
@Retention(RetentionPolicy.RUNTIME)//注解会在class字节码文件中存在,在运行时可以通过反射获取到

②、@Target ——》指定被修饰的Annotation可以放置的位置(被修饰的目标)

      @Target(ElementType.TYPE)  ——》接口、类
      @Target(ElementType.FIELD)   ——》属性
      @Target(ElementType.METHOD)   ——》方法

      @Target(ElementType.PARAMETER)   ——》方法参数
      @Target(ElementType.CONSTRUCTOR)  ——》构造函数
      @Target(ElementType.LOCAL_VARIABLE)  ——》局部变量
      @Target(ElementType.ANNOTATION_TYPE)  ——》注解
      @Target(ElementType.PACKAGE)   ——》包

 注:可以指定多个位置,如:

@Target({ElementType.METHOD, ElementType.TYPE}),也就是此注解可以在方法和类上面使用

③、@Inherited:指定被修饰的Annotation将具有继承性 

④、@Documented:指定被修饰的该Annotation可以被javadoc工具提取成文档.

二、自定义注解开发

1、含义

使用@interface关键字, 其定义过程与定义接口非常类似, 需要注意的是:

Annotation的成员变量在Annotation定义中是以无参的方法形式来声明的, 其方法名和返回值类型定义了该成员变量的名字和类型, 而且我们还可以使用default关键字为这个成员变量设定默认值

2、演示

①、枚举类:enum,指的是常量的集合

②、注解类

Ⅰ、演示@Retention(RetentionPolicy.SOURCE)注解:MyAnnotation.java

package com.lv.annotation;
import org.springframework.beans.factory.annotation.Autowired;
import java.lang.annotation.*;
/**
 * @author T440s
 */

//生成一个注释
@Documented
//表示当前注解可以打在什么东西上面,此处可以放在类上与方法上
@Target({ElementType.TYPE,ElementType.METHOD})
//指定被修饰的Annotation将具有继承性
@Inherited
//注解仅存在于源码中,在class字节码文件中不包含
@Retention(RetentionPolicy.SOURCE)
public @interface MyAnnotation {

    String value() default "";
}

TestController.java:注意这引用了MyAnnotation注解

package com.lv.controller;

import com.lv.annotation.MyAnnotation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@MyAnnotation
@Controller
public class TestController {

    @Autowired
    private String name;

    @MyAnnotation
    public void aa(){

    }

}

运行后target层注解消失:注解仅存在于源码中,在class字节码文件中不包含

Ⅱ、MyAnnotation注解为@Retention(RetentionPolicy.RUNTIME)时

        ——注解会在class字节码文件中存在,在运行时可以通过反射获取到

运行test.java:

package com.lv.controller;

import java.lang.annotation.Annotation;

public class Test {
    public static void main(String[] args) {
//    反射
        for(Annotation a:TestController.class.getAnnotations()){
            System.out.println(a);
        }

    }
}

Ⅲ、取注解里的属性值

注解:MyAnnotation.java

String message() default "aaa";

拿值:

package com.lv.controller;
import com.lv.annotation.MyAnnotation;
import java.lang.annotation.Annotation;
public class Test {
    public static void main(String[] args) {
//    反射
        for(Annotation a:TestController.class.getAnnotations()){
            if(a instanceof MyAnnotation){
                System.out.println(((MyAnnotation) a).message());
            }
        }

    }
}

Ⅳ、判断在该类有无该注解

测试:

package com.lv.controller;
import com.lv.annotation.MyAnnotation;
import java.lang.annotation.Annotation;
public class Test {
    public static void main(String[] args) {
//        直接将MyAnnotation这注解取出
        MyAnnotation myAnnotation=TestController.class.getAnnotation(MyAnnotation.class);
        if(myAnnotation !=null){
            System.out.println(myAnnotation.message());
        }

    }
}

三、完成切面日志操作

当我们在写增删改的时候,会有很多冗余的代码,后期修改很麻烦,如:

 @RequestMapping("/add")
    public String add(){
        System.out.println("xxx在增加");
        System.out.println("增加成功");
        return "yes";
    }

我们就可以定义aop面向切面,将前面那部分放入前置通知,后面一部分后置通知

新建切面:LogAop.java

package com.lv.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect
//类不被识别,将类变成一个组件
@Component
@Slf4j

public class LogAop {
//    指定切入的规则,".."代表可有参可无参
    @Pointcut("execution(* com.lv.controller.*Controller.*(..))")
    public  void logger(){}

//    环绕通知
    @Around("logger()")
    public Object around(ProceedingJoinPoint point){
        //        获得方法名称
        Signature methodName=point.getSignature();
//        日志输出
        log.info(methodName+"进来了");
        Long l1=System.currentTimeMillis();
//        让方法执行
        Object obj=null;
        try {
            obj=point.proceed(point.getArgs());
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        log.info(methodName+"走了"+"\t耗时"+(System.currentTimeMillis()-l1));
        return obj;

    }
}

使用jrebel运行:

package com.lv.controller;

import com.lv.annotation.MyAnnotation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@MyAnnotation
//直接返回json数据
@RestController
//返回页面跳转数据
//@Controller
public class TestController {

    @RequestMapping("/add")
    public String add(){
        return "yes";
    }

    @RequestMapping("/del")
    public String del(){
        return "yes";
    }

    @RequestMapping("/upd")
    public String upd(){
        return "yes";
    }

    @RequestMapping("/list")
    public String list(){
        return "yes";
    }

}

使用注解来开发aop日志:

新建注解类:MyLog.java

package com.lv.annotation;

import java.lang.annotation.*;

@Inherited
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyLog {

}

同样在切面类中,记得改变切入的规则

@Pointcut("@annotation(com.lv.annotation.MyLog)")

需要输出日志的方法就将新建的注解加上

四、完成前端响应反应

传入四个文件:

ResponseParse.java:

package com.lv.response;

import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

/**
 * @author hgh
 */
//响应增强类
@RestControllerAdvice
public class ResponseParse implements ResponseBodyAdvice {

    @Override
    public boolean supports(MethodParameter methodParameter, Class aClass) {
        //返回值决定他是否需要进入beforeBodyWrite
        return methodParameter.getMethod().isAnnotationPresent(ResponseResult.class);
    }

    @Override
    public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        //更改返回值
        if (o == null) {
            return Result.success();
        }
        if (o instanceof Integer) {
            return Result.failure(ResultCode.queryCode((Integer) o));
        }
        if (o instanceof ResultCode) {
            return Result.failure((ResultCode) o);
        }
        if (o instanceof Result) {
            return o;
        }
        return null;
    }
}

 ResponseResult.java:

package com.lv.response;

import java.lang.annotation.*;

/**
 * @author hgh
 */
@Retention(value = RetentionPolicy.RUNTIME)
@Documented
@Target({ElementType.METHOD})
public @interface ResponseResult {

}

Result.java:

package com.lv.response;

import lombok.Data;

import java.io.Serializable;

/**
 * 响应对象封装类
 *
 * @author hgh
 */
@Data
public class Result<T> implements Serializable {

    private final int code;
    private final String message;
    private final T data;

    /**
     * 私有构造, 只允许通过static调用构造
     *
     * @param resultCode 结果枚举
     * @param data       响应数据
     */
    private Result(ResultCode resultCode, T data) {
        this.code = resultCode.getCode();
        this.message = resultCode.getMessage();
        this.data = data;
    }

    /**
     * 成功调用返回的结果(无数据携带)
     *
     * @return Result
     */
    public static Result success() {
        return success(null);
    }

    /**
     * 成功调用返回的结果(数据携带)
     *
     * @return Result
     */
    public static <T> Result success(T data) {
        return new Result(ResultCode.SUCCESS, data);
    }

    /**
     * 失败调用返回的结果(数据携带)
     *
     * @param resultCode 状态枚举
     * @param data       携带的数据
     * @return Result
     */
    public static <T> Result failure(ResultCode resultCode, T data) {
        return new Result(resultCode, data);
    }

    /**
     * 失败调用返回的结果(无数据携带)
     *
     * @param resultCode 状态枚举
     * @return Result
     */
    public static Result failure(ResultCode resultCode) {
        return failure(resultCode, null);
    }

}

ResultCode.java:

package com.lv.response;

import java.io.Serializable;

/**
 * 响应结果码枚举
 *
 * @author hgh
 */

public enum ResultCode implements Serializable {

    /* 正常状态 */
    SUCCESS(100, "成功"),
    FAILURE(101, "失败"),
    UNKNOWN(102, "未知响应"),
    /**
     * 用户code范围: 200~300;
     */
    USER_ACCOUNT_NOT_FIND(201, "用户名不存在"),
    USER_ACCOUNT_DISABLED(202, "该用户已被禁用"),
    USER_PASSWORD_NOT_MATCH(203, "该用户密码不一致"),
    USER_PERMISSION_ERROR(204, "该用户不具备访问权限"),
    USER_STATE_OFF_LINE(205, "该用户未登录");

    private final Integer code;
    private final String message;

    ResultCode(Integer code, String message) {
        this.code = code;
        this.message = message;
    }

    public Integer getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }

    public static ResultCode queryCode(Integer code) {
        for (ResultCode value : values()) {
            if (code.equals(value.code)) {
                return value;
            }
        }
        return UNKNOWN;
    }

}

测试:

package com.lv.controller;

import com.lv.annotation.MyAnnotation;
import com.lv.annotation.MyLog;
import com.lv.response.ResponseResult;
import com.lv.response.Result;
import com.lv.response.ResultCode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@MyAnnotation
//直接返回json数据
@RestController
//返回页面跳转数据
//@Controller
public class TestController {

    @MyLog
    @ResponseResult
    @RequestMapping("/add")
    public Result add(){
        return Result.success("yes");
    }

    @RequestMapping("/del")
    @ResponseResult
    public Object del(){
        return 201;
    }

    @RequestMapping("/upd")
    @ResponseResult
    public Object upd(){
        return ResultCode.USER_ACCOUNT_NOT_FIND;
    }

    @RequestMapping("/list")
    @ResponseResult
    public Object list(){
        return Result.success("yes");
    }

}

增加:

删除:

总结

到此这篇关于SpringBoot自定义注解的文章就介绍到这了,更多相关SpringBoot自定义注解内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • SpringBoot中自定义注解实现参数非空校验的示例

    前言 由于刚写项目不久,在写 web 后台接口时,经常会对前端传入的参数进行一些规则校验,如果入参较少还好,一旦需要校验的参数比较多,那么使用 if 校验会带来大量的重复性工作,并且代码看起来会非常冗余,所以我首先想到能否通过一些手段改进这点,让 Controller 层减少参数校验的冗余代码,提升代码的可阅读性. 经过阅读他人的代码,发现使用 annotation 注解是一个比较方便的手段,SpringBoot 自带的 @RequestParam 注解只会校验请求中该参数是否存在,但是该参数是

  • java SpringBoot自定义注解,及自定义解析器实现对象自动注入操作

    # java-SpringBoot自定义参数解析器实现对象自动注入 解析器逻辑流程图表 后台解析注解的解析器 首先,我在java后台编写了一个解析器,代码如下 import com.ruoyi.framework.interceptor.annotation.LoginUser; import com.ruoyi.project.WebMoudle.WebUser.domain.WebUser; import com.ruoyi.project.WebMoudle.WebUser.service

  • SpringBoot自定义注解API数据加密和签名校验

    api数据数据签名(MD5,SHA1) 签名枚举类SginEnum.java package com.jx.app.api.framework.annotation.enums; /** * @ClassName: SginEnum * @Description: TODO(这是一个签名枚举类) * @author gangyu * @date 2018年11月20日 下午4:30:44 */ public enum SginEnum { //0不需要签名,1使用MD5数据加密 2 使用SHA数

  • SpringBoot基于自定义注解实现切面编程

    1.相关依赖包 <!-- aop 依赖包 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>org.aspectj</groupId> <ar

  • SpringBoot中自定义注解实现控制器访问次数限制实例

    今天给大家介绍一下SpringBoot中如何自定义注解实现控制器访问次数限制. 在Web中最经常发生的就是利用恶性URL访问刷爆服务器之类的攻击,今天我就给大家介绍一下如何利用自定义注解实现这类攻击的防御操作. 其实这类问题一般的解决思路就是:在控制器中加入自定义注解实现访问次数限制的功能. 具体的实现过程看下面的例子: 步骤一:先定义一个注解类,下面看代码事例: package example.controller.limit; import org.springframework.core.

  • SpringBoot如何通过自定义注解实现权限检查详解

    前言 最近开发了一个接口,完成后准备自测时,却被拦截器拦截了,提示:(AUTH-NO)未能获得有效的请求参数!怎么会这样呢? 于是我全局搜了这个提示语,结果发现它被出现在一个Aspect类当中了,并且把一个 @interface 作为了一个切点,原来这里利用了Spring AOP面向切面的方式进行权限控制. SpringBoot通过自定义注解实现日志打印可参考:SpringBoot通过自定义注解实现日志打印 正文 Spring AOP Spring AOP 即面向切面,是对OOP面向对象的一种延

  • SpringBoot使用自定义注解实现权限拦截的示例

    本文介绍了SpringBoot使用自定义注解实现权限拦截的示例,分享给大家,具体如下: HandlerInterceptor(处理器拦截器) 常见使用场景 日志记录: 记录请求信息的日志, 以便进行信息监控, 信息统计, 计算PV(page View)等 性能监控: 权限检查: 通用行为: 使用自定义注解实现权限拦截 首先HandlerInterceptor了解 在HandlerInterceptor中有三个方法: public interface HandlerInterceptor { //

  • SpringBoot自定义注解开发指南

    目录 一.Java注解(Annotation) 1.JDK基本注解 2.JDK元注解 二.自定义注解开发 1.含义 2.演示 三.完成切面日志操作 四.完成前端响应反应 总结 一.Java注解(Annotation) 含义:Java注解是附加在代码中的一些元信息,用于一些工具在编译. 运行时进行解析和使用,起到说明.配置的功能. 1.JDK基本注解 @Override ——>重写 @Deprecated ——>已过时 @SuppressWarnings(value = "unchec

  • 使用Springboot自定义注解,支持SPEL表达式

    目录 Springboot自定义注解,支持SPEL表达式 1.自定义注解 2.使用AOP拦截方法,解析注解参数 自定义注解结合切面和spel表达式 自定义一个注解 自定义一个service类,在需要拦截的方法上加上@Log注解 写一个自定义切面 pom文件的依赖 测试 增加内容 Springboot自定义注解,支持SPEL表达式 举例,自定义redis模糊删除注解 1.自定义注解 import java.lang.annotation.ElementType; import java.lang.

  • SpringBoot自定义注解使用读写分离Mysql数据库的实例教程

    需求场景 为了防止代码中有的SQL慢查询,影响我们线上主数据库的性能.我们需要将sql查询操作切换到从库中进行.为了使用方便,将自定义注解的形式使用. mysql导入的依赖 <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.16</version> </dependency&

  • SpringBoot自定义注解实现Token校验的方法

    1.定义Token的注解,需要Token校验的接口,方法上加上此注解 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementTyp

  • SpringBoot 自定义注解异步记录复杂日志详解

    目录 1.背景 2.技术方案-自定义注解 2.1 注解介绍 2.2 元注解 2.3 实现自定义注解 3.技术方案-AOP切面 3.1 AOP术语解析 3.2 切入点表达式 3.3 ADVICE通知类型 3.4 技术实现 3.5 相关操作 4.高级操作 1.背景 最近接手一个任务,需要给当前项目加一个较为复杂的日志.有多复杂呢? 要有日志类型.不同日志类型要有不同的操作和备注等.作为小白的我最开始的做法是在业务层写代码记录日志,好处就是方便,坏处就是这种做法直接侵袭Service层,Service

  • SpringBoot 自定义注解实现涉密字段脱敏

    目录 1. 创建隐私数据类型枚举:PrivacyTypeEnum 2. 创建自定义隐私注解:PrivacyEncrypt 3. 创建自定义序列化器:PrivacySerializer 4. 隐私数据隐藏工具类:PrivacyUtil 5. 注解使用 关于数据脱敏,网上的文章都是硬编码规则,比如对身份证,手机号,邮件地址等固定写法脱敏.本文在此基础上,拓展动态从数据库查出涉密关键字执行脱敏操作. 数据脱敏:把系统里的一些敏感数据进行加密处理后再返回,达到保护隐私作用,实现效果图如下: 其实要实现上

  • SpringBoot自定义注解之实现AOP切面日志详解

    通过自定义注解的方式(如:@SysLog(obj = "操作对象", text = "操作内容"),在 SpringBoot 中来实现 AOP 切面统一打印出入参日志. 一.先看下项目结构 二.Maven JAR依赖 <!-- AOP -->     <dependency>             <groupId>org.springframework.boot</groupId>             <

  • SpringBoot 自定义注解之脱敏注解详解

    目录 自定义注解之脱敏注解 一.脱敏后的效果 二.代码 1.脱敏注解 2.定义脱敏类型 3.敏感工具类 4.脱敏序列化信息 小结一下 自己手写的一个高效自定义字符串脱敏注解 自己写了个 仅供参考 自定义注解之脱敏注解 数据脱敏是指对某些敏感信息通过脱敏规则进行数据的变形,实现敏感隐私数据的可靠保护.需求是把返回到前端的数据进行脱敏,以免造成隐私信息的泄露. 一.脱敏后的效果 这样显示很不好吧,所有信息都泄露了 这样就很好了吧 二.代码 1.脱敏注解 @Retention(RetentionPol

随机推荐