Spring?spel獲取自定義注解參數(shù)值方式
spel獲取自定義注解參數(shù)值
1.注解類
package com.xxx.mall.order.service.component;? import java.lang.annotation.*; ? /** ?* 庫存不足等信息監(jiān)控 ?* Created by xdc on 2019/4/16 15:43 ?*/ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) @Documented public @interface StockWarnCollect { ? ? ? /** 客戶id */ ? ? String customerId(); ? ? ? /** 來源 */ ? ? String source(); ? ? ? /** 請求類型 1:詳情頁 2:購物車去結(jié)算 3:提交訂單 */ ? ? String pageType(); }
2.注解使用
@Override @StockWarnCollect(customerId = "#customerId", source = "#source", pageType = "2") public Map<String, Object> validateCarts(Long customerId, Set<Long> userSelectedIds, Short source, JSONArray couponInfo){ ? ? // 省略 }
3.aop中處理
import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.exception.ExceptionUtils; import org.apache.commons.lang3.StringUtils; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; 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.core.LocalVariableTableParameterNameDiscoverer; import org.springframework.expression.EvaluationContext; import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.stereotype.Component; import java.lang.reflect.Method; /** * 下單失敗、庫存監(jiān)控 * Created by xdc on 2019/4/16 15:45 */ @Aspect @Component @Slf4j public class StockWarnCollectAop { @Pointcut(value = "@annotation(com.xxx.mall.order.service.component.StockWarnCollect)") public void collectStockWarn(){} @Around(value = "collectStockWarn()") public Object around(ProceedingJoinPoint pjp) throws Throwable { Method targetMethod = this.getTargetMethod(pjp); StockWarnCollect stockWarnCollect = targetMethod.getAnnotation(StockWarnCollect.class); // spel信息 String customerIdSpel = stockWarnCollect.customerId(); String sourceSpel = stockWarnCollect.source(); Integer pageType = null; // 操作類型,純字符串 if (StringUtils.isNotBlank(stockWarnCollect.pageType())) { pageType = Integer.valueOf(stockWarnCollect.pageType()); } // 客戶id、來源解析 ExpressionParser parser = new SpelExpressionParser(); LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer(); String[] params = discoverer.getParameterNames(targetMethod); Object[] args = pjp.getArgs(); EvaluationContext context = new StandardEvaluationContext(); for (int len = 0; len < params.length; len++) { context.setVariable(params[len], args[len]); } Expression expression = parser.parseExpression(customerIdSpel); Long customerId = expression.getValue(context, Long.class); expression = parser.parseExpression(sourceSpel); Short source = expression.getValue(context, Short.class); log.info("collectStockWarn customerId:{}, source:{}", customerId, source); // 業(yè)務(wù)邏輯處理 Object result = null; try { result = pjp.proceed(); } catch (Throwable e) { log.info("collectStockWarn watchs creating order errorMsg:{}", ExceptionUtils.getStackTrace(e)); if (e instanceof MallException) { } else { // 未知錯(cuò)誤 } throw e; } try { if (result != null) { } } catch (Exception e) { log.error("collectStockWarn process error, errorMsg:{}", ExceptionUtils.getStackTrace(e)); } return result; } /** * 獲取目標(biāo)方法 */ private Method getTargetMethod(ProceedingJoinPoint pjp) throws NoSuchMethodException { Signature signature = pjp.getSignature(); MethodSignature methodSignature = (MethodSignature)signature; Method agentMethod = methodSignature.getMethod(); return pjp.getTarget().getClass().getMethod(agentMethod.getName(),agentMethod.getParameterTypes()); } }
spel在注解中的使用
SpEL(Spring Expression Language),即Spring表達(dá)式語言,是比JSP的EL更強(qiáng)大的一種表達(dá)式語言。為什么要總結(jié)SpEL,因?yàn)樗梢栽谶\(yùn)行時(shí)查詢和操作數(shù)據(jù),尤其是數(shù)組列表型數(shù)據(jù),因此可以縮減代碼量,優(yōu)化代碼結(jié)構(gòu)。個(gè)人認(rèn)為很有用。
1 語法說明
1.1 SpEL 字面量:
- 整數(shù):#{8}
- 小數(shù):#{8.8}
- 科學(xué)計(jì)數(shù)法:#{1e4}
- String:可以使用單引號(hào)或者雙引號(hào)作為字符串的定界符號(hào)。
- Boolean:#{true}
1.2 SpEL引用bean , 屬性和方法:
- 引用其他對(duì)象:#{car}
- 引用其他對(duì)象的屬性:#{car.brand}
- 調(diào)用其它方法 , 還可以鏈?zhǔn)讲僮鳎?{car.toString()}
- 調(diào)用靜態(tài)方法靜態(tài)屬性:#{T(java.lang.Math).PI}
1.3 SpEL支持的運(yùn)算符號(hào):
- 算術(shù)運(yùn)算符:+,-,*,/,%,^(加號(hào)還可以用作字符串連接)
- 比較運(yùn)算符:< , > , == , >= , <= , lt , gt , eg , le , ge
- 邏輯運(yùn)算符:and , or , not , |
- if-else 運(yùn)算符(類似三目運(yùn)算符):?:(temary), ?:(Elvis)
- 正則表達(dá)式:#{admin.email matches '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,4}'}
2. 基本用法
SpEL有三種用法:
1. 是在注解@Value中;
2. 在XML配置中;
3. 代碼塊中使用Expression。
2.1 @Value
? ? //@Value能修飾成員變量和方法形參 ? ? //#{}內(nèi)就是表達(dá)式的內(nèi)容 ? ? @Value("#{表達(dá)式}") ? ? public String arg;
2.2 <bean>配置
<bean id="xxx" class="com.java.XXXXX.xx"> ? ? <!-- 同@Value,#{}內(nèi)是表達(dá)式的值,可放在property或constructor-arg內(nèi) --> ? ? <property name="arg" value="#{表達(dá)式}"> </bean>
2.3 代碼塊中使用
public class SpELTest {? ? ? public static void main(String[] args) { ? ? ? ? ? //創(chuàng)建ExpressionParser解析表達(dá)式 ? ? ? ? ExpressionParser parser = new SpelExpressionParser(); ? ? ? ? //表達(dá)式放置 ? ? ? ? Expression exp = parser.parseExpression("表達(dá)式"); ? ? ? ? //執(zhí)行表達(dá)式,默認(rèn)容器是spring本身的容器:ApplicationContext ? ? ? ? Object value = exp.getValue(); ? ? ? ?? ? ? ? ? /**如果使用其他的容器,則用下面的方法*/ ? ? ? ? //創(chuàng)建一個(gè)虛擬的容器EvaluationContext ? ? ? ? StandardEvaluationContext ctx = new StandardEvaluationContext(); ? ? ? ? //向容器內(nèi)添加bean ? ? ? ? BeanA beanA = new BeanA(); ? ? ? ? ctx.setVariable("bean_id", beanA); ? ? ? ?? ? ? ? ? //setRootObject并非必須;一個(gè)EvaluationContext只能有一個(gè)RootObject,引用它的屬性時(shí),可以不加前綴 ? ? ? ? ctx.setRootObject(XXX); ? ? ? ?? ? ? ? ? //getValue有參數(shù)ctx,從新的容器中根據(jù)SpEL表達(dá)式獲取所需的值 ? ? ? ? Object value = exp.getValue(ctx); ? ? } }
4 #{…}和${…}
- #{…} 用于執(zhí)行SpEl表達(dá)式,并將內(nèi)容賦值給屬性
- ${…} 主要用于加載外部屬性文件中的值
- #{…} 和${…} 可以混合使用,但是必須#{}外面,${}在里面
4.1 ${…}用法
{}里面的內(nèi)容必須符合SpEL表達(dá)式,通過@Value(“${spelDefault.value}”)可以獲取屬性文件中對(duì)應(yīng)的值,但是如果屬性文件中沒有這個(gè)屬性,則會(huì)報(bào)錯(cuò)??梢酝ㄟ^賦予默認(rèn)值解決這個(gè)問題,如@Value("${spelDefault.value:127.0.0.1}")
// 如果屬性文件沒有spelDefault.value,則會(huì)報(bào)錯(cuò) ? ? // ?@Value("${spelDefault.value}") ? ? // ?private String spelDefault2; ? ? ? // 使用default.value設(shè)置值,如果不存在則使用默認(rèn)值 ? ? @Value("${spelDefault.value:127.0.0.1}") ? ? private String spelDefault;
4.2 #{…}用法
這里只演示簡單用法
? ? // SpEL:調(diào)用字符串Hello World的concat方法 ? ? @Value("#{'Hello World'.concat('!')}") ? ? private String helloWorld; ? ? ? // SpEL: 調(diào)用字符串的getBytes方法,然后調(diào)用length屬性 ? ? @Value("#{'Hello World'.bytes.length}") ? ? private String helloWorldbytes;
4.3 ${…}和#{…}混合使用
${...}和#{...}可以混合使用,如下文代碼執(zhí)行順序:通過${server.name}從屬性文件中獲取值并進(jìn)行替換,然后就變成了 執(zhí)行SpEL表達(dá)式{‘server1,server2,server3’.split(‘,’)}。
// SpEL: 傳入一個(gè)字符串,根據(jù)","切分后插入列表中, #{}和${}配置使用(注意單引號(hào),注意不能反過來${}在外面,#{}在里面) ? ? @Value("#{'${server.name}'.split(',')}") ? ? private List<String> servers;
在上文中在#{}外面,${}在里面可以執(zhí)行成功,那么反過來是否可以呢${}在外面,#{}在里面,如代碼
// SpEL: 注意不能反過來${}在外面,#{}在里面,這個(gè)會(huì)執(zhí)行失敗 ? ? @Value("${#{'HelloWorld'.concat('_')}}") ? ? private List<String> servers2;
答案是不能。因?yàn)閟pring執(zhí)行${}是時(shí)機(jī)要早于#{}。在本例中,Spring會(huì)嘗試從屬性中查找#{‘HelloWorld’.concat(‘_’)},那么肯定找不到,由上文已知如果找不到,然后報(bào)錯(cuò)。所以${}在外面,#{}在里面是非法操作
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
springboot項(xiàng)目打包鏡像方式以及區(qū)分環(huán)境打包的方法
本文主要介紹了springboot項(xiàng)目打包鏡像方式以及區(qū)分環(huán)境打包的方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-03-03Java實(shí)體類實(shí)現(xiàn)鏈?zhǔn)讲僮鲗?shí)例解析
這篇文章主要介紹了Java實(shí)體類實(shí)現(xiàn)鏈?zhǔn)讲僮鲗?shí)例解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12java轉(zhuǎn)換時(shí)區(qū)時(shí)間過程詳解
這篇文章主要介紹了java轉(zhuǎn)換時(shí)區(qū)時(shí)間過程詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11一文教會(huì)你如何搭建vue+springboot項(xiàng)目
最近在搗鼓?SpringBoot?與?Vue?整合的項(xiàng)目,所以下面這篇文章主要給大家介紹了關(guān)于如何通過一篇文章教會(huì)你搭建vue+springboot項(xiàng)目,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-05-05IDEA生成可運(yùn)行jar包(包含第三方j(luò)ar包)流程詳解
這篇文章主要介紹了IDEA生成可運(yùn)行jar包(包含第三方j(luò)ar包)流程詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-11-11SpringBoot集成tomcat詳解實(shí)現(xiàn)過程
采用spring boot之后,一切變得如此簡單,打包->java-jar->運(yùn)維,只需要一個(gè)jar包便可以隨意部署安裝。這篇文章,將對(duì) spring boot集成tomcat的源碼進(jìn)行分析,探索其內(nèi)部的原理2023-02-02