springboot如何通過(guò)自定義注解對(duì)方法參數(shù)進(jìn)行攔截驗(yàn)證
springboot通過(guò)自定義注解對(duì)方法參數(shù)進(jìn)行攔截驗(yàn)證
元注解參數(shù)說(shuō)明
@Target定義注解的作用目標(biāo),也就是可以定義注解具體作用在類上,方法上,還是變量上@Retention定義注解的保留策略
| RetentionPolicy.SOURCE | 注解僅存在于源碼中在class字節(jié)碼文件中不包含 |
| RetentionPolicy.CLASS | 默認(rèn)的保留策略注解會(huì)在class字節(jié)碼文件中存在但運(yùn)行時(shí)無(wú)法獲得; |
| RetentionPolicy.RUNTIME | 注解會(huì)在class字節(jié)碼文件中存在,在運(yùn)行時(shí)可以通過(guò)反射獲取到。 |
@Document說(shuō)明該注解將被包含在javadoc中@Inherited說(shuō)明子類可以繼承父類中的該注解
@Target類型和說(shuō)明
| 類型 | 說(shuō)明 |
| ElementType.TYPE | 接口、類、枚舉、注解 |
| ElementType.FIELD | 字段、枚舉的常量 |
| ElementType.METHOD | 方法 |
| ElementType.PARAMETER | 方法參數(shù) |
| ElementType.CONSTRUCTOR | 構(gòu)造函數(shù) |
| ElementType.LOCAL_VARIABLE | 局部變量 |
| ElementType.ANNOTATION_TYPE | 注解 |
| ElementType.PACKAGE | 包 |
具體實(shí)現(xiàn):簡(jiǎn)單版
1、引入坐標(biāo)
<!-- 引入aop切面支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>2、創(chuàng)建自定義注解
package com.hk.annotation;
import java.lang.annotation.*;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface ParamsIntercept {
}@Target注解指定ElementType@Inherited說(shuō)明子類可以繼承父類中的該注解@Retention注解指定RetentionPolicy
注意:
注解支持的元素類型除了上面的String之外還有以下:
- 基本類型(int、char、byte、double、float、long、boolean)
- 字符串String
- 類Class
- 枚舉enum
- 注解Annotation
- 上述類型的數(shù)組類型
當(dāng)使用其他類型修飾注解元素時(shí),編譯期會(huì)報(bào)錯(cuò)
Invalid type 'Integer' for annotation member
基本類型的包裝類型也是不允許在注解中修飾注解元素的;上述代碼中 subjectId 不能 定義為 Integer
3、創(chuàng)建切面進(jìn)行判斷
import com.alibaba.fastjson.JSONObject;
import com.bxm.adsmanager.model.dao.user.User;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
@Aspect
@Component
public class ParamsBeforeAspect {
private static final Logger logger = Logger.getLogger(ParamsBeforeAspect .class);
@Before("@annotation(com.hk.annotation.ParamsIntercept)")
public void doBefore(JoinPoint point){
try {
long startTime = System.currentTimeMillis();//開始時(shí)間
Method method = getMethod(point);
if(null == method){
if(logger.isWarnEnabled()){
logger.warn("method is null");
}
return;
}
Object[] args = point.getArgs();//獲取請(qǐng)求參數(shù)
if (ArrayUtils.isNotEmpty(args)) {
for (Object arg : args) {
// 對(duì)參數(shù)進(jìn)行判斷
}
}
long endTime = System.currentTimeMillis();//結(jié)束時(shí)間
float excTime=(float)(endTime-startTime)/1000;
logger.info("總耗時(shí):" + excTime+"s");
}catch (Exception e){
logger.error("記錄日志異常",e);
}
}
private Method getMethod(JoinPoint point) {
MethodSignature methodSignature = (MethodSignature) point.getSignature();
Class<?> targetClass = point.getTarget().getClass();
try {
return targetClass.getMethod(methodSignature.getName(), methodSignature.getParameterTypes());
} catch (NoSuchMethodException e) {
return null;
}
}4、controller使用
@Controller
@RequestMapping(value = "/")
public class UserManagerController {
//1.傳入的是一個(gè)具體的值
@ParamsIntercept
@RequestMapping(value = "/getUser/{subjectId}")
public R<String> getUserDetail(@PathVariable Integer subjectId) {
try {
//處理自己的業(yè)務(wù)
} catch (Exception e) {
e.printStackTrace();
return R.error(e.getMessage());
}
return R.error("操作失敗");
}
} 具體實(shí)現(xiàn):封裝版
1、引入坐標(biāo)
<!-- 引入aop切面支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>2、創(chuàng)建自定義注解
package com.hk.annotation;
import java.lang.annotation.*;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface ParamsIntercept {
String subjectId();
}3、創(chuàng)建切面
package com.hk.aspect;
import com.warmer.base.enums.ReturnStatus;
import com.warmer.base.util.R;
import com.warmer.base.util.SpringUtils;
import com.warmer.base.util.StringUtil;
import com.warmer.web.annotation.AnnotationResolver;
import com.warmer.web.annotation.DomainOwner;
import com.warmer.web.entity.KgDomain;
import com.warmer.web.security.TokenService;
import com.warmer.web.service.KnowledgeGraphService;
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.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
@Aspect
@Component
public class DomainValidAspect {
@Pointcut("@annotation(com.hk.annotation.ParamsIntercept)")
public void annotationPointCut() {
}
@Before("annotationPointCut()")
public void doBefore(JoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
// 獲取方法注解
ParamsIntercept paramsIntercept = signature.getMethod().getAnnotation(ParamsIntercept.class);
// 獲取參數(shù)
String subjectIdCode = paramsIntercept.subjectId();
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = servletRequestAttributes.getRequest();
Integer memberId = (Integer) request.getAttribute("memberId");
AnnotationResolver annotationResolver = AnnotationResolver.newInstance();
Integer resolver = (Integer) annotationResolver.resolver(joinPoint, subjectIdCode);
log.info("獲取請(qǐng)求參數(shù):subjectId:{}, memberId:{}", resolver, memberId);
// 具體業(yè)務(wù)代碼
.......
}
}
4、封裝獲取參數(shù)工具
package com.hk.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import java.lang.reflect.Method;
import java.util.Map;
/**
* @description:
* @author: HK
* @since: 2024/9/25 15:16
*/
public class AnnotationResolver {
private static AnnotationResolver resolver ;
public static AnnotationResolver newInstance(){
if (resolver == null) {
return resolver = new AnnotationResolver();
}else{
return resolver;
}
}
public Object resolver(JoinPoint joinPoint, String str) {
if (str == null) return null ;
Object value = null;
if (str.matches("#\\{\\D*\\}")) {
String newStr = str.replaceAll("#\\{", "").replaceAll("\\}", "");
if (newStr.contains(".")) { // 復(fù)雜類型
try {
value = complexResolver(joinPoint, newStr);
} catch (Exception e) {
e.printStackTrace();
}
} else {
value = simpleResolver(joinPoint, newStr);
}
} else { //非變量
value = str;
}
return value;
}
private Object complexResolver(JoinPoint joinPoint, String str) throws Exception {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
String[] names = methodSignature.getParameterNames();
Object[] args = joinPoint.getArgs();
String[] strs = str.split("\\.");
for (int i = 0; i < names.length; i++) {
if (strs[0].equals(names[i])) {
Object obj = args[i];
//這里處理出入?yún)?shù)為Map的邏輯
if(obj instanceof Map){
Map item=(Map) obj;
return item.get(strs[1]);
}
Method dmethod = obj.getClass().getDeclaredMethod(getMethodName(strs[1]), null);
Object value = dmethod.invoke(args[i]);
return getValue(value, 1, strs);
}
}
return null;
}
private Object getValue(Object obj, int index, String[] strs) {
try {
if (obj != null && index < strs.length - 1) {
Method method = obj.getClass().getDeclaredMethod(getMethodName(strs[index + 1]), null);
obj = method.invoke(obj);
getValue(obj, index + 1, strs);
}
return obj;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
private String getMethodName(String name) {
return "get" + name.replaceFirst(name.substring(0, 1), name.substring(0, 1).toUpperCase());
}
private Object simpleResolver(JoinPoint joinPoint, String str) {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
String[] names = methodSignature.getParameterNames();
Object[] args = joinPoint.getArgs();
for (int i = 0; i < names.length; i++) {
if (str.equals(names[i])) {
return args[i];
}
}
return null;
}
}5、controller使用
@Controller
@RequestMapping(value = "/")
public class UserManagerController {
//1.傳入的是一個(gè)具體的值
@ParamsIntercept(subjectId= "#{userCode}")
@RequestMapping(value = "/getUser/{subjectId}")
public R<String> getUserDetail(@PathVariable Integer subjectId) {
try {
//處理自己的業(yè)務(wù)
} catch (Exception e) {
e.printStackTrace();
return R.error(e.getMessage());
}
return R.error("操作失敗");
}
//2.傳入的是一個(gè)對(duì)象
@ParamsIntercept(subjectId= "#{userItem.subjectId}")
//3.傳入的可能是一個(gè)map
@ParamsIntercept(subjectId= "#{params.subjectId}")
} 總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Spring Boot 中的任務(wù)執(zhí)行器基本概念及使用方法
務(wù)執(zhí)行器是 Spring Boot 中的一個(gè)非常實(shí)用的模塊,它可以簡(jiǎn)化異步任務(wù)的開發(fā)和管理,在本文中,我們介紹了任務(wù)執(zhí)行器的基本概念和使用方法,以及一個(gè)完整的示例代碼,需要的朋友可以參考下2023-07-07
郵件的組織結(jié)構(gòu)介紹 郵件實(shí)現(xiàn)詳解(三)
這篇文章主要為大家詳細(xì)介紹了郵件的組織結(jié)構(gòu),郵件內(nèi)容的基本格式和具體細(xì)節(jié),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-10-10
詳解Spring Data JPA中Repository的接口查詢方法
repository代理有兩種方式從方法名中派生出特定存儲(chǔ)查詢:通過(guò)直接從方法名派生查詢和通過(guò)使用一個(gè)手動(dòng)定義的查詢。本文將通過(guò)示例詳細(xì)講解Spring Data JPA中Repository的接口查詢方法,需要的可以參考一下2022-04-04
帶你了解Java數(shù)據(jù)結(jié)構(gòu)和算法之哈希表
這篇文章主要為大家介紹了Java數(shù)據(jù)結(jié)構(gòu)和算法之哈希表,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助2022-01-01
Java多線程中Thread.currentThread()和this的區(qū)別詳解
這篇文章主要介紹了Java多線程中Thread.currentThread()和this的區(qū)別詳解,Thread.currentThread()方法返回的是對(duì)當(dāng)前正在執(zhí)行的線程對(duì)象的引用,this代表的是當(dāng)前調(diào)用它所在函數(shù)所屬的對(duì)象的引用,需要的朋友可以參考下2023-08-08

