一,前言
对于一些重复性的操作我们可以用提取为util的方式进行处理,但也可以更简便一些,比如自定义个注解进行。选择看这篇文章的小伙伴想必都对注解不陌生,但是可能对它的工作原理不太清楚。这里我们用注解实现对接口的权限校验,让大家感受一下,自定义注解的魅力。
二, 注解的基本认识
1,作用域
我们知道,注解可以用在类上,方法上,参数上等等。这也决定了注解的作用域可以是多方面的。
主要有三类:
-
ElementType.TYPE:表示该注解可以用于类、接口(包括注解类型)或枚举声明。例如,常见的注解
@Entity
和@Service
就是使用在类上的注解。 -
ElementType.FIELD:表示该注解可以用于字段(包括枚举常量)。例如,常见的注解
@Autowired
就是使用在字段上的注解。 -
ElementType.METHOD:表示该注解可以用于方法声明。例如,常见的注解
@Override
就是使用在方法上的注解。
还有其它作用域。注解的作用域由 java.lang.annotation.ElementType
枚举类型定义。我们来看看有哪些吧。
public enum ElementType {/** Class, interface (including annotation interface), enum, or record* declaration */// 可以用于类、接口、枚举声明。TYPE,/** Field declaration (includes enum constants) */// 可以用于字段(包括枚举常量)。FIELD,/** Method declaration */// 可以用于方法声明。METHOD,/** Formal parameter declaration */// 可以用于参数声明。PARAMETER,/** Constructor declaration */// 可以用于构造函数声明。CONSTRUCTOR,/** Local variable declaration */// 可以用于局部变量声明。LOCAL_VARIABLE,/** Annotation interface declaration (Formerly known as an annotation type.) */// 可以用于注解类型声明(即声明注解的注解)。ANNOTATION_TYPE,/** Package declaration */// 可以用于包声明。PACKAGE,/*** Type parameter declaration** @since 1.8*/// 可以用于泛型类型参数的声明。TYPE_PARAMETER,/*** Use of a type** @since 1.8*/// 可以用于任何类型使用的地方,如类型转换、instanceof 表达式、new 表达式等。TYPE_USE,/*** Module declaration.** @since 9*/// 可以用于模块声明,即 module-info.java 文件中的 module 关键字所声明的模块。该注解用于对模块进行注解,例如指定模块的名称、版本等信息。MODULE,/*** Record component** @jls 8.10.3 Record Members* @jls 9.7.4 Where Annotations May Appear** @since 16*/// (Record)类型,它是一种简化的数据持有类,用于替代传统的 Java Bean 类。而 RECORD_COMPONENT 注解用于标记记录类型中的组件,也就是记录的字段或者对应的 accessor 方法。RECORD_COMPONENT;
}
2,生命周期
注解还有一个重要的属性,就是生命周期。
注解的生命周期由 java.lang.annotation.RetentionPolicy
枚举类型定义,它规定了注解在编译时、类加载时和运行时的保留策略。
public enum RetentionPolicy {/*** Annotations are to be discarded by the compiler.*/SOURCE,/*** Annotations are to be recorded in the class file by the compiler* but need not be retained by the VM at run time. This is the default* behavior.*/CLASS,/*** Annotations are to be recorded in the class file by the compiler and* retained by the VM at run time, so they may be read reflectively.** @see java.lang.reflect.AnnotatedElement*/RUNTIME
}
-
SOURCE:该注解只在源代码中保留,编译器在编译时会丢弃这种注解,不会包含在编译后生成的 class 文件中。这意味着在运行时无法通过反射获取到这种注解。
-
CLASS:该注解在编译时会被保留到 class 文件中,但在运行时不会被虚拟机保留,因此通过反射也无法获取到。这是默认的保留策略。
-
RUNTIME:该注解在编译时会被保留到 class 文件中,并且在运行时会被虚拟机保留,因此可以通过反射机制获取到。这种注解通常用于运行时的操作,比如使用反射机制检查类的结构或者处理注解信息。
三,实践使用
1,启动类
package com.luojie;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class Applications {public static void main(String[] args) {SpringApplication.run(Applications.class, args);}
}
2, 注解类
package com.luojie.config.myInterface;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface MyPermission {String value() default "";
}
3,aop实现校验
这里请自己往数据库植入数据,并做好查询返回,如果有不知的地方,可以参照
Spring配置多数据库(采用数据连接池管理)_spring连接多个数据库-CSDN博客
package com.luojie.config.myInterface;import com.luojie.common.NoPermissionException;
import com.luojie.dao.mapper1.Mapper1;
import com.luojie.moudle.UserModel;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import javax.servlet.http.HttpServletRequest;@Component
@Aspect
public class PermissionAspect {@Autowiredprivate HttpServletRequest request;@Autowiredprivate Mapper1 mapper1;@Before("@annotation(requiresPermission)")public void checkPermission(MyPermission requiresPermission) {String permission = requiresPermission.value();// 在这里进行权限校验逻辑// 检查用户是否拥有指定的权限,如果没有权限,可以抛出异常或者记录日志等if (!hasPermission(permission)) {throw new NoPermissionException(500, "没有权限");}}private boolean hasPermission(String permission) {// 一般我们会通过request拿token,解析token和数据库中数据比对,看用户是否有权限,这里我就简化为直接的值String userID = request.getHeader("userID");// 从数据库中拿到该用户的所有权限UserModel user = mapper1.getUser(userID);// 进行权限判断if (user == null) return false;if (user.getRoles().contains(permission)) {return true;}return false;}}
4,统一异常处理类
package com.luojie.common;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;import javax.servlet.http.HttpServletResponse;@ControllerAdvice
@ResponseBody
public class CommonExceptionHandle {@Autowiredprivate HttpServletResponse response;@ExceptionHandler(NoPermissionException.class)public ResponseCommonImpl NoPermission(NoPermissionException ex) {ResponseCommonImpl failedCommon = ResponseUtil.failWithNoPermission(ex.getErrorMsg());response.setStatus(HttpStatus.UNAUTHORIZED.value());return failedCommon;}@ExceptionHandler(Exception.class)public ResponseCommonImpl handleException(Exception ex) {ResponseCommonImpl failedCommon = ResponseUtil.failCommon(ex.getMessage(), null);return failedCommon;}
}
5,返回类
统一返回类写法,请参照JAVA 标准接口返回与i18n国际化配置_java 后端接口返回支持国际化-CSDN博客
6,controller类
package com.luojie.controller;import com.luojie.common.ResponseCommonImpl;
import com.luojie.common.ResponseUtil;
import com.luojie.controImpl.InterfaceTestImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class InterfaceTestController {@Autowiredprivate InterfaceTestImpl interfaceTest;@GetMapping("/in/test")public ResponseCommonImpl test() {System.out.println("111111111111111222222222");interfaceTest.test();return ResponseUtil.success("ok", null);}
}
7,编写Impl类
package com.luojie.controImpl;import com.luojie.common.ResponseCommonImpl;
import com.luojie.config.myInterface.MyPermission;
import org.springframework.stereotype.Service;@Service
public class InterfaceTestImpl {@MyPermission("admin")public ResponseCommonImpl test() {System.out.println("ok!");return null;}
}
四,接口测试
当正确有权限的时候
当没有权限的时候的返回
如果对您有用,感谢老爷点个赞,谢谢。