一,前言
对于一些重复性的操作我们可以用提取为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 {
@Autowired
private HttpServletRequest request;
@Autowired
private 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 {
@Autowired
private 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 {
@Autowired
private 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;
}
}
四,接口测试
当正确有权限的时候
当没有权限的时候的返回
如果对您有用,感谢老爷点个赞,谢谢。