springboot通過spel結(jié)合aop實現(xiàn)動態(tài)傳參的案例
前言
基于SpingBoot框架中, 我們隨處可以見的便是各種各樣的功能注解, 注解的實現(xiàn)原理AOP之前有說過(翻看本系列的前面幾章即可), 這里不過多贅述.
那么, 你有沒有碰到這樣一種場景: 需要動態(tài)的傳參數(shù)進注解, 注意是動態(tài)的而不是寫死在代碼里的.
針對這種需求, 今天, 我們就來實現(xiàn)一個簡單的案例.
SpEl表達式簡介
正式擼代碼之前, 先了解下SpEl (Spring Expression Language) 表達式, 這是Spring框架中的一個利器.
Spring通過SpEl能在運行時構(gòu)建復(fù)雜表達式、存取對象屬性、對象方法調(diào)用等等.
舉個簡單的例子方便理解, 如下
//定義了一個表達式 String expressionStr = "1+1"; ExpressionParser parser = new SpelExpressionParser(); Expression expression = parser.parseExpression(expressionStr); Integer val = expression.getValue(Integer.class); System.out.println(expressionStr + "的結(jié)果是:" + val);
通過以上案例, 不難理解, 所謂的SpEl, 本質(zhì)上其實就是解析表達式,.
關(guān)于SpEl表達式感興趣的可以自行查閱資料, 本篇不做細致的討論.
實例: SpEl結(jié)合AOP動態(tài)傳參
簡單了解了SpEl表達式, 那么接下來我們就直接開始擼代碼.
先引入必要的pom依賴, 其實只有aop依賴, SpEl本身就被Spring支持, 所以無需額外引入.
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
定義一個SpEl的工具類SpelUtil
public class SpelUtil { /** * 用于SpEL表達式解析. */ private static final SpelExpressionParser parser = new SpelExpressionParser(); /** * 用于獲取方法參數(shù)定義名字. */ private static final DefaultParameterNameDiscoverer nameDiscoverer = new DefaultParameterNameDiscoverer(); /** * 解析SpEL表達式 * * @param spELStr * @param joinPoint * @return */ public static String generateKeyBySpEL(String spELStr, ProceedingJoinPoint joinPoint) { // 通過joinPoint獲取被注解方法 MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); Method method = methodSignature.getMethod(); // 使用Spring的DefaultParameterNameDiscoverer獲取方法形參名數(shù)組 String[] paramNames = nameDiscoverer.getParameterNames(method); // 解析過后的Spring表達式對象 Expression expression = parser.parseExpression(spELStr); // Spring的表達式上下文對象 EvaluationContext context = new StandardEvaluationContext(); // 通過joinPoint獲取被注解方法的形參 Object[] args = joinPoint.getArgs(); // 給上下文賦值 for (int i = 0; i < args.length; i++) { context.setVariable(paramNames[i], args[i]); } // 表達式從上下文中計算出實際參數(shù)值 /*如: @annotation(key="#user.name") method(User user) 那么就可以解析出方法形參的某屬性值,return “xiaoming”; */ return expression.getValue(context).toString(); } }
定義一個帶參注解SpelGetParm
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface SpelGetParm { String parm() default ""; }
定義帶參注解SpelGetParmAop
@Aspect @Slf4j @Component public class SpelGetParmAop { @PostConstruct public void init() { log.info("SpelGetParm init ......"); } /** * 攔截加了SpelGetParm注解的方法請求 * * @param joinPoint * @param spelGetParm * @return * @throws Throwable */ @Around("@annotation(spelGetParm)") public Object beforeInvoce(ProceedingJoinPoint joinPoint, SpelGetParm spelGetParm) throws Throwable { Object result = null; // 方法名 String methodName = joinPoint.getSignature().getName(); //獲取動態(tài)參數(shù) String parm = SpelUtil.generateKeyBySpEL(spelGetParm.parm(), joinPoint); log.info("spel獲取動態(tài)aop參數(shù): {}", parm); try { log.info("執(zhí)行目標方法: {} ==>>開始......", methodName); result = joinPoint.proceed(); log.info("執(zhí)行目標方法: {} ==>>結(jié)束......", methodName); // 返回通知 log.info("目標方法 " + methodName + " 執(zhí)行結(jié)果 " + result); } finally { } // 后置通知 log.info("目標方法 " + methodName + " 結(jié)束"); return result; }
以上已經(jīng)基本實現(xiàn)了案例的核心功能, 接下來我們使用該注解即可
定義一個實體User
@Getter @Setter @NoArgsConstructor @JsonSerialize @JsonInclude(Include.NON_NULL) public class User implements Serializable { private static final long serialVersionUID = -7229987827039544092L; private String name; private Long id; }
我們在UserController直接使用該帶參注解即可
@CrossOrigin @RestController @RequestMapping("/user") public class UserController { @PostMapping("/param") @SpelGetParm(parm = "#user.name") public R repeat(@RequestBody User user) { return R.success(user); } }
最后請求
可以看出, 切面成功獲取到了實體的name值“張三”.
小結(jié)
結(jié)合SpEl表達式可以實現(xiàn)各種“騷操作”, 各位大佬可自由發(fā)揮, 下面一章我們準備結(jié)合SpEl來實現(xiàn)分布式鎖的功能.
項目地址
https://github.com/MrCoderStack/SpringBootDemo/tree/master/sb-spel-annotations
到此這篇關(guān)于springboot spel結(jié)合aop實現(xiàn)動態(tài)傳參的文章就介紹到這了,更多相關(guān)springboot動態(tài)傳參內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
springboot項目如何在linux服務(wù)器上啟動、停止腳本
這篇文章主要介紹了springboot項目如何在linux服務(wù)器上啟動、停止腳本問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-05-05java程序員自己的圖片轉(zhuǎn)文字OCR識圖工具分享
這篇文章主要介紹了java程序員自己的圖片轉(zhuǎn)文字OCR識圖工具,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-11-11springboot實現(xiàn)異步調(diào)用@Async的示例
這篇文章主要介紹了springboot實現(xiàn)異步調(diào)用@Async的示例,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12詳解SpringBoot中@SessionAttributes的使用
這篇文章主要通過示例為大家詳細介紹了SpringBoot中@SessionAttributes的使用,文中的示例代碼講解詳細,感興趣的小伙伴可以了解一下2022-07-07Java servlet 使用 PrintWriter 時的編碼與亂碼的示例代碼
本篇文章主要介紹了Java servlet 使用 PrintWriter 時的編碼與亂碼的示例代碼,探討了 PrintWriter 的缺省編碼與普通字符流的缺省編碼的差異,具有一定的參考價值,有興趣的可以了解一下2017-11-11java.net.SocketException: Connection reset 解決方法
最近糾結(jié)致死的一個java報錯java.net.SocketException: Connection reset 終于得到解決2013-03-03