如何来自定义注解

前面了介绍了注解相关的知识,本文来详细说下如何自定义注解,以及注解在开发中常见的应用场景。


概述

深入理解JAVA中的注解

前面我们知道在获取到AnnotationElement接口中的方法后,就可以在类、方法、成员变量等程序元素中获取到注解信息。

在这里插入图片描述


反射获取注解

先定义一个注解:

package cn.wideth.util.other;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyField {

    String description();
    int length();
}

通过反射获取注解

package cn.wideth.util.other;

import java.lang.reflect.Field;

public class MyFieldTest {

    //使用我们的自定义注解
    @MyField(description = "用户名", length = 12)
    private String username;

    public static void main(String[] args) {

        // 获取类模板
        Class c = MyFieldTest.class;

        // 获取所有字段
        for(Field f : c.getDeclaredFields()){
            // 判断这个字段是否有MyField注解
            if(f.isAnnotationPresent(MyField.class)){
                MyField annotation = f.getAnnotation(MyField.class);
                System.out.println("字段:[" + f.getName() + "], 描述:[" + annotation.description() + "]," +
                        " 长度:[" + annotation.length() +"]");
            }
        }

    }

}

程序结果

在这里插入图片描述


应用场景一:自定义注解+拦截器 实现登录校验

接下来,我们使用springboot拦截器实现这样一个功能,如果方法上加了@LoginRequired,则提示用户该接口需要登录才能访问,否则不需要登录。

首先定义一个LoginRequired注解


@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginRequired {
    
}

然后写两个简单的接口,访问sourceA,sourceB资源

package cn.wideth.controller;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/annotation")
@Api(description = "注解测试", tags = "MyAnnotation")
public class IndexController {

    @GetMapping("/sourceA")
    @ApiOperation(value = "sourceA", notes = "sourceA")
    public String sourceA(){
        return "你正在访问sourceA资源";
    }

    @GetMapping("/sourceB")
    @ApiOperation(value = "sourceB", notes = "sourceB")
    public String sourceB(){
        return "你正在访问sourceB资源";
    }

}

没添加拦截器之前成功访问

在这里插入图片描述
在这里插入图片描述

实现spring的HandlerInterceptor 类先实现拦截器,但不拦截,只是简单打印日志,如下:

package cn.wideth.annotation;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class SourceAccessInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        System.out.println("进入拦截器了");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {

    }
}

实现spring类WebMvcConfigurer,创建配置类把拦截器添加到拦截器链中

package cn.wideth.annotation;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class InterceptorTrainConfigurer implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        
           registry.addInterceptor(new SourceAccessInterceptor())
                //表示拦截/api/annotation下的所有请求
                .addPathPatterns("/api/annotation/*");
    }
}

拦截成功如下

在这里插入图片描述

在sourceB方法上添加我们的登录注解@LoginRequired

package cn.wideth.controller;

import cn.wideth.annotation.LoginRequired;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/annotation")
@Api(description = "注解测试", tags = "MyAnnotation")
public class IndexController {

    @GetMapping("/sourceA")
    @ApiOperation(value = "sourceA", notes = "sourceA")
    public String sourceA(){
        return "你正在访问sourceA资源";
    }

    @LoginRequired
    @GetMapping("/sourceB")
    @ApiOperation(value = "sourceB", notes = "sourceB")
    public String sourceB(){
        return "你正在访问sourceB资源";
    }

}

简单实现登录拦截逻辑

package cn.wideth.annotation;

import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class SourceAccessInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {

        // 如果请求为静态资源请求时,类型转换会报错,类型不对应,所以应再请求时方法请求时再转换类型
        // 如果不是映射到方法直接通过
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        System.out.println("进入拦截器了");
        // 反射获取方法上的LoginRequred注解
        HandlerMethod handlerMethod = (HandlerMethod)handler;
        LoginRequired loginRequired = handlerMethod.getMethod().getAnnotation(LoginRequired.class);
        if(loginRequired == null){
            return true;
        }

        // 有LoginRequired注解说明需要登录,提示用户登录
        response.setContentType("application/json; charset=utf-8");
        response.getWriter().print("你访问的资源需要登录");
        return false;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {

    }
}

运行成功,访问sourceB时需要登录了,访问sourceA则不用登录

在这里插入图片描述

在这里插入图片描述


应用场景二:自定义注解+AOP 实现日志打印

先导入切面需要的依赖包

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

定义一个注解@MyLog

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

定义一个切面类,见如下代码注释理解:

package cn.wideth.annotation;

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

@Aspect // 1.表明这是一个切面类
@Component
public class MyLogAspect {

    // 2. PointCut表示这是一个切点,@annotation表示这个切点切到一个注解上,后面带该注解的全类名
    // 切面最主要的就是切点,所有的故事都围绕切点发生
    // logPointCut()代表切点名称
    @Pointcut("@annotation(cn.wideth.annotation.MyLog)")
    public void logPointCut(){};

    // 3. 环绕通知
    @Around("logPointCut()")
    public void logAround(ProceedingJoinPoint joinPoint){
        // 获取方法名称
        String methodName = joinPoint.getSignature().getName();
        // 获取入参
        Object[] param = joinPoint.getArgs();

        StringBuilder sb = new StringBuilder();
        for(Object o : param){
            sb.append(o + "; ");
        }
        System.out.println("进入[" + methodName + "]方法,参数为:" + sb.toString());

        // 继续执行方法
        try {
            joinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        System.out.println(methodName + "方法执行结束");

    }
}

在IndexController中写一个sourceC进行测试,加上我们的自定义注解:

    @MyLog
    @GetMapping("/sourceC")
    @ApiOperation(value = "sourceC", notes = "sourceC")
    public String sourceC(@RequestParam String source){
        return "你正在访问sourceC资源";
    }

启动springboot web项目,输入访问地址

在这里插入图片描述
在这里插入图片描述


本文小结

本文详细介绍了自定义注解相关的知识。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值