色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

基于SpEL可以實(shí)現(xiàn)的功能

科技綠洲 ? 來源:Java技術(shù)指北 ? 作者:Java技術(shù)指北 ? 2023-09-30 10:37 ? 次閱讀

Spring Expression Language

概念

Spring Expression Language(簡稱SpEL)是一種強(qiáng)大的表達(dá)式語言,支持在運(yùn)行時(shí)查詢和操作對象圖。該語言的語法類似于Unified EL,但提供了額外的特性, 最顯著的是方法調(diào)用和基本的字符串模板功能。

雖然還有其他幾種Java表達(dá)式語言可用——OGNL、MVEL和JBoss EL等,SpEL是為了向Spring社區(qū)提供一種受良好支持的表達(dá)式語言,可以跨Spring產(chǎn)品組合中的所有產(chǎn)品使用。SpEL基于一種與技術(shù)無關(guān)的API,在需要時(shí)可以集成其他表達(dá)式語言實(shí)現(xiàn)。

作用

基于SpEL我們可以實(shí)現(xiàn)下面這些功能,當(dāng)然是基于表達(dá)式,SpEL提供了表達(dá)式運(yùn)行環(huán)境,而且不依賴于Spring,這樣我可以基于其強(qiáng)大的執(zhí)行器實(shí)現(xiàn)各種擴(kuò)展。

  • 支持功能
    • 字面表達(dá)式
    • 布爾和關(guān)系運(yùn)算符
    • 正則表達(dá)式
    • 類表達(dá)式
    • 訪問屬性、數(shù)組、列表和映射
    • 方法調(diào)用
    • 關(guān)系運(yùn)算符
    • 申明
    • 調(diào)用構(gòu)造函數(shù)
    • bean引用
    • 數(shù)組構(gòu)造
    • 內(nèi)聯(lián)的list
    • 內(nèi)聯(lián)的map
    • 三元運(yùn)算符
    • 變量
    • 用戶自定義函數(shù)
    • 集合投影
    • 集合選擇
    • 模板化表達(dá)式

關(guān)鍵接口

將表達(dá)式字符串解析為可求值的編譯表達(dá)式。支持解析模板以及標(biāo)準(zhǔn)表達(dá)式字符串。

public interface ExpressionParser {

  /**
   * 解析字符串表達(dá)式為Expression對象
   */
  Expression parseExpression(String expressionString) throws ParseException;

  /**
   * 解析字符串表達(dá)式為Expression對象,基于ParserContext解析字符串,比如常見的#{exrp}
   */
  Expression parseExpression(String expressionString, ParserContext context) throws ParseException;

}

表達(dá)式在求值上下文中執(zhí)行。正是在這個(gè)上下文中,表達(dá)式求值期間遇到的引用才會被解析。EvaluationContext接口有一個(gè)默認(rèn)的實(shí)現(xiàn)StandardBeanExpressionResolver,可以通過繼承該類進(jìn)行擴(kuò)展。

public interface EvaluationContext {

 /**
  * 獲取Root上下文對象
  */
 TypedValue getRootObject();

 /**
  * 返回屬性讀寫訪問器
  */
 List< PropertyAccessor > getPropertyAccessors();

 /**
  * 返回構(gòu)造器解析器
  */
 List< ConstructorResolver > getConstructorResolvers();

 /**
  * 返回方法解析器
  */
 List< MethodResolver > getMethodResolvers();

 /**
  * 返回Bean解析器,用于Bean的查找
  */
 @Nullable
 BeanResolver getBeanResolver();

 /**
  * 根據(jù)類名(一般為全限名)返回一個(gè)類型定位器,比如T(java.lang.Math)
  */
 TypeLocator getTypeLocator();

 /**
  * 返回可以將值從一種類型轉(zhuǎn)換(或強(qiáng)制轉(zhuǎn)換)為另一種類型的類型轉(zhuǎn)換器。
  */
 TypeConverter getTypeConverter();

 /**
  * 返回一個(gè)類型比較器,用于比較對象對是否相等。
  */
 TypeComparator getTypeComparator();

 /**
  * 返回一個(gè)運(yùn)算符重載器,該重載器可以支持多個(gè)標(biāo)準(zhǔn)類型集之間的數(shù)學(xué)運(yùn)算。
  */
 OperatorOverloader getOperatorOverloader();

 /**
     * 為變量設(shè)值
  */
 void setVariable(String name, @Nullable Object value);

 /**
  * 從變量取值
  */
 @Nullable
 Object lookupVariable(String name);

}

適用場景

  • 擴(kuò)展變量
    正如Spring中使用的那樣,我們可以基于SpEL實(shí)現(xiàn)基于Spring容器、運(yùn)行環(huán)境上下文等動態(tài)取值。
  • 數(shù)據(jù)審計(jì)
    通過從復(fù)雜的數(shù)據(jù)結(jié)構(gòu)中進(jìn)行數(shù)據(jù)運(yùn)算,一般針對數(shù)據(jù)匯總或者復(fù)雜的結(jié)構(gòu)化數(shù)據(jù)時(shí),通過自定義特定表達(dá)式(類似于DSL)來實(shí)現(xiàn)數(shù)據(jù)運(yùn)算。

Spring的使用

在Spring中有著大量使用SpEL的場景,在平時(shí)的開發(fā)中,可能會看到如下的這些配置,比如通過${}、#{}這些包裹的表達(dá)式,主要都是基于SpEL實(shí)現(xiàn)。

  1. @Value("#{systemProperties['pop3.port'] ?: 25}")
    簡單的看下源嗎,可以看到針對@Value標(biāo)記的類屬性,是如何為其注入屬性:

AutowiredAnnotationBeanPostProcessor -> AutowiredFieldElement#injectvalue = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter)DefaultListableBeanFactory#doResolveDependency

public class DefaultListableBeanFactory {
    
  public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
                                    @Nullable Set< String > autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {

    InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
    try {
      Class< ? > type = descriptor.getDependencyType();
      Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
      if (value != null) {
        if (value instanceof String) {
          // 處理 ${} 占位替換
          String strVal = resolveEmbeddedValue((String) value);
          BeanDefinition bd = (beanName != null && containsBean(beanName) ?
                  getMergedBeanDefinition(beanName) : null);
          // SpEL處理
          value = evaluateBeanDefinitionString(strVal, bd);
        }
      }
      // 省略...
      return result;
    }catch (Exception e){
        // ...
    }
  }
}
  1. @Cacheable(value="users", key="#p0")
    具體實(shí)現(xiàn)可以閱讀源碼:org.springframework.cache.interceptor.CacheOperationExpressionEvaluator
  2. @KafkaListener(topics = "#{'${topics}'.split(',')}")
    具體實(shí)現(xiàn)可以閱讀源碼:org.springframework.kafka.annotation.KafkaListenerAnnotationBeanPostProcessor

示例

下面通過一些測試示例了解SpEL的基本用法,對應(yīng)上面的一些功能實(shí)現(xiàn),通過這些簡單的例子,可以大概了解其語法與使用方式:

public class Tests {
  @Test
  public void runParser() throws NoSuchMethodException {
    SpelExpressionParser parser = new SpelExpressionParser();
    Map map = new HashMap();
    map.put("name", "SpEL");
    StandardEvaluationContext context = new StandardEvaluationContext(map);
    context.setVariable("var1", 1);
    context.setVariable("func1", StringUtils.class.getMethod("hasText", String.class));
    context.setVariable("func2", new Root());
    context.setBeanResolver(new MyBeanResolver());

    log.info("字面量:{}", parser.parseExpression("'hello'").getValue(context));
    log.info("對象屬性:{}", parser.parseExpression("'hello'.bytes").getValue(context));
    log.info("變量:{}", parser.parseExpression("#var1").getValue(context));
    log.info("調(diào)用方法:{}", parser.parseExpression("'hello'.concat(' world')").getValue(context));
    log.info("靜態(tài)方法:{}", parser.parseExpression("T(java.lang.System).currentTimeMillis()").getValue(context));
    log.info("方法:{}", parser.parseExpression("#func1('1')").getValue(context));
    log.info("實(shí)例方法:{}", parser.parseExpression("#func2.print('2')").getValue(context));

    Root root = new Root();
    root.list.add("0");
    parser.parseExpression("list[0]").setValue(context, root, "1");
    log.info("設(shè)值:{}", root);

    log.info("ROOT: {}", parser.parseExpression("#root").getValue(context));
    log.info("ROOT 取值: {}", parser.parseExpression("#root[name]").getValue(context));
    log.info("ROOT 取值: {}", parser.parseExpression("[name]").getValue(context));

    log.info("THIS: {}", parser.parseExpression("#this").getValue(context));

    log.info("運(yùn)算符: {}", parser.parseExpression("1+1").getValue(context));
    log.info("操作符: {}", parser.parseExpression("1==1").getValue(context));
    log.info("邏輯運(yùn)算: {}", parser.parseExpression("true && false").getValue(context));

    ParserContext parserContext = new ParserContext() {
      @Override
      public boolean isTemplate() {
        return true;
      }

      @Override
      public String getExpressionPrefix() {
        return "#{";
      }

      @Override
      public String getExpressionSuffix() {
        return "}";
      }
    };
    log.info("#{表達(dá)式}: {}", parser.parseExpression("#{1+1}", parserContext).getValue(context));

    log.info("Map: {}", parser.parseExpression("{name:'Nikola',dob:'10-July-1856'}").getValue(context));
    log.info("List: {}", parser.parseExpression("{1,2,3,4}").getValue(context));
    log.info("Array: {}", parser.parseExpression("new int[]{1,2,3}").getValue(context));

    log.info("instanceof: {}", parser.parseExpression("'hello' instanceof T(Integer)").getValue(context, Boolean.class));
    log.info("regex: {}", parser.parseExpression("'5.00' matches '^-?d+(.d{2})?$'").getValue(context, Boolean.class));
    log.info("三目運(yùn)算: {}", parser.parseExpression("false ? 'trueExp' : 'falseExp'").getValue(context, String.class));

    log.info("Bean: {}", parser.parseExpression("@bean1").getValue(context));
  }
}

特殊處理

  • T
    通過T(CLASS)指定類型,可以用來類型判斷或者調(diào)用類靜態(tài)方法sec.setTypeLocator(new StandardTypeLocator(evalContext.getBeanFactory().getBeanClassLoader()));
  • @
    通過@Name獲取beansec.setBeanResolver(new BeanFactoryResolver(evalContext.getBeanFactory()));
  • &
    通過&Name獲取beanFactory

sec.setBeanResolver(new BeanFactoryResolver(evalContext.getBeanFactory()));

  • 通過#{}標(biāo)識表達(dá)式,主要在springBeanExpressionResolver resolver = beanFactory.getBeanExpressionResolver(); BeanExpressionContext context = new BeanExpressionContext(beanFactory, null);


從root取值:#root.name 或者 name(#root可以忽略) 從variables取值或方法:#var

擴(kuò)展

下面以一個(gè)示例看下SpEL在我們項(xiàng)目中的具體應(yīng)用:

public class Tests{
  /**
   * 執(zhí)行測試
   */
  @Test
    public void runCalc(){
        SpelExpressionParser parser = new SpelExpressionParser();
        StandardEvaluationContext context = new StandardEvaluationContext();
        context.setVariable("helper",new StudentHelper());
        context.setVariable("students", genStudents(100));

        log.info("max score: {}", parser.parseExpression("#helper.max(#students)").getValue(context, Double.class));
        log.info("min score: {}", parser.parseExpression("#helper.min(#students)").getValue(context, Double.class));
        log.info("avg score: {}", parser.parseExpression("#helper.avg(#students)").getValue(context, Double.class));
    }

  /**
   * 生成測試數(shù)據(jù)
   * @param count
   * @return
   */
  private List< Student > genStudents(int count){
        List< Student > students = new ArrayList<  >();
        Faker faker = new Faker(Locale.CHINA);
        Name name = faker.name();
        Number number = faker.number();
        IntStream.range(0, count).forEach(i- >{
            students.add(new Student(name.name(), number.randomDouble(3, 60, 100)));
        });
        return students;
    }

    @Data
    @AllArgsConstructor
    class Student{
        private String name;
        private double score;
    }

  /**
   * 工具類
   */
  class StudentHelper{

        public double max(List< Student > students){
            if(CollectionUtils.isEmpty(students)){
                return 0;
            }
            return students.stream().mapToDouble(Student::getScore).max().getAsDouble();
        }

        public double min(List< Student > students){
            if(CollectionUtils.isEmpty(students)){
                return 0;
            }
            return students.stream().mapToDouble(Student::getScore).min().getAsDouble();
        }

        public double avg(List< Student > students){
            if(CollectionUtils.isEmpty(students)){
                return 0;
            }
            return students.stream().mapToDouble(Student::getScore).average().getAsDouble();
        }
    }
}

在這個(gè)示例中,我們主要通過SpEL獲取獲取學(xué)生分值的最大值、最小值、平均值等方式,在實(shí)際的項(xiàng)目中使用時(shí),絕非如此簡單,比如我們在做數(shù)據(jù)統(tǒng)計(jì)時(shí),對各項(xiàng)指標(biāo)數(shù)值的計(jì)算就是通過SpEL實(shí)現(xiàn), 因?yàn)榫唧w功能的實(shí)現(xiàn)是由我們自己定義,因此在業(yè)務(wù)擴(kuò)展上會非常的方便。

結(jié)束語

SpEL是一個(gè)功能非常強(qiáng)大的基于Java的解釋型語言解析器,如果你想基于表達(dá)式的形式,對復(fù)雜結(jié)構(gòu)數(shù)據(jù)計(jì)算或?qū)徲?jì)的需求時(shí),不妨試試這個(gè)輕量級工具。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • JAVA
    +關(guān)注

    關(guān)注

    19

    文章

    2973

    瀏覽量

    104928
  • 字符串
    +關(guān)注

    關(guān)注

    1

    文章

    585

    瀏覽量

    20571
  • 執(zhí)行器
    +關(guān)注

    關(guān)注

    5

    文章

    378

    瀏覽量

    19388
  • SPEL
    +關(guān)注

    關(guān)注

    0

    文章

    3

    瀏覽量

    6120
收藏 人收藏

    評論

    相關(guān)推薦

    FPGA可以實(shí)現(xiàn)DSP的功能嗎?

    一般涉及到數(shù)字處理和邏輯控制都用DSP加FPGA實(shí)現(xiàn),最近想用FPGA實(shí)現(xiàn)數(shù)字處理和邏輯控制,聽搞通信的說多加幾個(gè)門就可以了,數(shù)字處理時(shí)鐘要求25MHZ,請高手指點(diǎn)一下。
    發(fā)表于 04-05 10:01

    用什么電子元件可以實(shí)現(xiàn)觸點(diǎn)開關(guān)的功能

    如題:用什么電子元件可以實(shí)現(xiàn)觸點(diǎn)開關(guān)(電鍵)的功能(按下電路接通,松手后電路斷開)?請教各位大神!能說詳細(xì)點(diǎn)最好!謝謝!!!!
    發(fā)表于 12-03 08:52

    耳機(jī)孔可以實(shí)現(xiàn)OTG功能嗎。。。。??????

    普通的手機(jī)耳機(jī)孔可以實(shí)現(xiàn)OTG功能嗎???比如說鏈接外置的攝像頭。。。。。。
    發(fā)表于 08-01 16:05

    請問ADF4106可以實(shí)現(xiàn)分頻器的功能嗎?

    您們好:最近做的項(xiàng)目中需要使用到分頻比很大的分頻器,故計(jì)劃使用ADF4106作為分頻器使用,請問ADF4106可以實(shí)現(xiàn)分頻器的功能嗎,可以的話相噪性能怎么樣呢?謝謝!
    發(fā)表于 10-08 10:34

    使用Azure軟件包連接IoT中心可以實(shí)現(xiàn)哪些功能

    使用Azure軟件包連接IoT中心可以實(shí)現(xiàn)什么功能?Azure IoT中心的架構(gòu)圖及功能
    發(fā)表于 03-30 08:07

    FPGA與ARM核結(jié)合可以實(shí)現(xiàn)功能互補(bǔ)嗎?

    RISC和FPGA結(jié)合成發(fā)展趨勢如何?FPGA與ARM核結(jié)合可以實(shí)現(xiàn)功能互補(bǔ)嗎?
    發(fā)表于 06-18 07:47

    SysTick定時(shí)器的計(jì)時(shí)功能可以實(shí)現(xiàn)精準(zhǔn)延時(shí)嗎

    什么是定時(shí)器?stm32定時(shí)器分為哪幾類?SysTick定時(shí)器的計(jì)時(shí)功能可以實(shí)現(xiàn)精準(zhǔn)延時(shí)嗎?
    發(fā)表于 08-12 06:02

    有什么方法可以實(shí)現(xiàn)stm32的計(jì)數(shù)功能

    stm32的外部時(shí)鐘源模式2和外部時(shí)鐘源模式1的區(qū)別是什么呢?有什么方法可以實(shí)現(xiàn)stm32的計(jì)數(shù)功能呢?
    發(fā)表于 11-23 07:59

    USB Low Speed 設(shè)備可以實(shí)現(xiàn)CDC虛擬串口功能嗎?

    USB Low Speed 設(shè)備可以實(shí)現(xiàn)CDC虛擬串口功能嗎?
    發(fā)表于 06-07 08:44

    并行eeprom是否可以實(shí)現(xiàn)gal芯片功能

    如果不考慮速度的話,他倆能否實(shí)現(xiàn)相同的功能,比如通過地址輸入信號,數(shù)據(jù)數(shù)據(jù),是否可以實(shí)現(xiàn)
    發(fā)表于 10-07 07:15

    智能家居產(chǎn)品可以實(shí)現(xiàn)功能和服務(wù)

    控制、電器的自動控制和遠(yuǎn)程控制等。   四、交互式智能控制:智能家電的語音控制功能可以通過語音識別技術(shù)來實(shí)現(xiàn),智能家居的主動行為響應(yīng)可以通過各種主動傳感器(如溫度、聲音、動作等)來
    的頭像 發(fā)表于 05-14 16:52 ?4361次閱讀

    通過人員定位位置數(shù)據(jù)可以實(shí)現(xiàn)哪些功能

    智慧平臺提供精準(zhǔn)的位置服務(wù),那工業(yè)智慧平臺可以通過人員定位位置數(shù)據(jù)可以實(shí)現(xiàn)哪些功能? 人員定位系統(tǒng)功能 1、三維展示模塊 系統(tǒng)平臺采用三維地
    的頭像 發(fā)表于 12-23 16:42 ?809次閱讀

    技術(shù)解讀 | SpEL表達(dá)式注入漏洞分析、檢查與防御

    在安全角度來看外部來源的數(shù)據(jù),均應(yīng)視為不可信數(shù)據(jù),對外部數(shù)據(jù),其包含的所有信息都須經(jīng)過校驗(yàn)或者過濾,再向下游服務(wù)進(jìn)行傳遞。若無防護(hù)手段,攻擊者可以通過構(gòu)造惡意輸入,對服務(wù)進(jìn)行攻擊。程序中如果使用未經(jīng)
    的頭像 發(fā)表于 03-25 07:55 ?2731次閱讀

    工廠人員定位系統(tǒng)可以實(shí)現(xiàn)哪些功能

    提供一套集人員設(shè)備定位管理的工廠人員定位系統(tǒng)。那么工廠定位系統(tǒng)能夠實(shí)現(xiàn)哪些功能呢? 1、電子圍欄 通過管理軟件(工廠人員定位系統(tǒng))將現(xiàn)場劃分各個(gè)區(qū)域并添加各個(gè)區(qū)域的進(jìn)出權(quán)限(黑白名單),只有獲得進(jìn)出許可的人員在規(guī)
    的頭像 發(fā)表于 05-22 09:34 ?699次閱讀

    Redis可以實(shí)現(xiàn)消息中間件MQ的功能

    是一種通信模式:發(fā)送者(PUBLISH)發(fā)送消息,訂閱者(SUBSCRIBE)接收消息,可以實(shí)現(xiàn)進(jìn)程間的消息傳遞   Redis可以實(shí)現(xiàn)消息中間件MQ的
    的頭像 發(fā)表于 01-25 14:48 ?987次閱讀
    Redis<b class='flag-5'>可以</b><b class='flag-5'>實(shí)現(xiàn)</b>消息中間件MQ的<b class='flag-5'>功能</b>
    主站蜘蛛池模板: 污文乖不疼的| 四虎国产精品永久一区高清 | 国产av免费观看日本| jealousvue成熟40岁| 99热最新网站| G国产精品无马| 99国产小视频| 99精品免费观看| 99视频一区| 99久酒店在线精品2019| 99久久免费精品国产免费| 91麻豆精品| 99精品国产第一福利网站| 97国产露脸精品国产麻豆| 91成品视频| AV午夜午夜快憣免费观看| MM131亚洲精品久久安然| YELLOW在线观看高清视频免费 | 成人女人A级毛片免费软件| 操老太太的逼| 调教日本美女| 国产成人无码一区AV在线观看 | 美女裸露胸部100%无遮挡| 美女搞鸡网站| 欧美色妞AV重囗味视频| 任你躁国语自产二区在线播放| 日本一卡二卡三卡四卡无卡免费播放| 青青久| 色在线视频亚洲欧美| 掀开奶罩边躁狠狠躁软学生| 亚洲 日韩 国产 制服 在线| 亚洲国产综合另类视频| 诱咪youmiss| 521人成a天堂v| 99久久精品免费国产一区二区三区| xart欧美一区在线播放| 国产99精品视频| 黄色小说男男| 美女脱三角裤| 色婷婷99综合久久久精品| 亚洲阿v天堂在线2017|