SpEL表達式


  Spring Expression Language,簡稱SpEl。其可以在程序中單獨使用,也可以在Spring應用中進行bean定義時使用。其核心是org.springframework.expression.Expression接口,Spring使用該接口來表示EL中的表達式。通過Expression接口的系列getValue()方法,可以獲取對應Expression在特定EvaluationContext下的值,也可以通過其系列setValue()方法來設值。對應的Expression通常不是由直接來new對應實現類的實例,而是通過Spring提供的org.springframework.expression.ExpressionParser接口的系列parseExpression()方法來將一個字符串類型的表達式解析為一個Expression。
  簡單的示例,在該示例中我們將字符串表達式“1+2”解析為一個Expression,然后進行計算得出其值為3。

@Test
public void test() {
    String expressionStr = "1+2";
    ExpressionParser parser = new SpelExpressionParser();
    Expression expression = parser.parseExpression(expressionStr);
    Integer val = expression.getValue(Integer.class);
    System.out.println(expressionStr + "的結果是:" + val);
}

  Expression接口有一系列的getValue()方法,當其不接收任何參數時表示將會把Expression的計算結果當做一個Object進行返回,如果我們希望返回的是特定的類型,則可以傳遞對應的類型作為getValue()方法的參數,如上述示例中傳遞的Interger.class。

字符串
  SpEl允許在表達式中直接使用int、double、String等。Expression可以通過對應的字符串進行解析,那么表達式就是需要表示一個字符串時應該如何表示呢?這個時候需要通過單引號“’”來進行包裹。而當我們的字符串中包含單引號時,那么對應的單引號需要使用一個單引號進行轉義,即連續兩個單引號

@Test
public void test04() {
    ExpressionParser parser = new SpelExpressionParser();
    Assert.assertTrue(parser.parseExpression("'abc'").getValue().equals("abc"));
    Assert.assertTrue(parser.parseExpression("'''abc'").getValue().equals("'abc"));
}

 訪問方法

  在SpEl表達式中我們也可以直接訪問對象的方法。在下述示例中我們就直接在SpEl中訪問了字符串的length()方法

@Test
public void test05() {
    ExpressionParser parser = new SpelExpressionParser();
    //直接訪問String的length()方法。
    Assert.assertTrue(parser.parseExpression("'abc'.length()").getValue().equals(3));
}

使用EvaluationContext

  先來看一個例子,在下列示例中我們在表達式中直接寫name和getName(),這個時候Expression是無法解析的,因為其不知道name和getName()對應什么意思。

@Test
public void test06() {
    ExpressionParser parser = new SpelExpressionParser();
    parser.parseExpression("name").getValue();
    parser.parseExpression("getName()").getValue();
}

  通過指定EvaluationContext,就可以讓name和getName()變得有意義。指定了EvaluationContext之后,Expression將根據對應的EvaluationContext來進行解析。如下示例中我們構建了一個基於user對象的EvaluationContext,user對象將作為StandardEvaluationContext的rootObject,那么對應的Expression就將根據該rootObject對象來獲取對應表達式的值。可以看到user對象定義了一個getName()方法,在解析name和getName()表達式時都將訪問user對象的getName()方法,即它們的返回結果都為字符串“abc”。

@Test
public void test06() {
    Object user = new Object() {
        public String getName() {
            return "abc";
        }
    };
    EvaluationContext context = new StandardEvaluationContext(user);
    ExpressionParser parser = new SpelExpressionParser();
    Assert.assertTrue(parser.parseExpression("name").getValue(context, String.class).equals("abc"));
    Assert.assertTrue(parser.parseExpression("getName()").getValue(context, String.class).equals("abc"));
}

  表達式name表示對應EvaluationContext的rootObject的一個屬性,在進行解析時,如果對應的get方法存在,則將直接訪問對應的get方法,如上述示例中的getName(),否則將直接對其進行訪問,這個時候就需要我們的屬性是公有的,以便外部類可以訪問。
  對於對象而言,我們可以訪問其屬性的屬性或方法,中間以點進行連接。

使用rootObject

當我們的表達式是基於某一個對象的時,我們也可以把對應的對象作為一個rootObject傳遞給對應的Expression以進行取值。如上述示例我們也可以直接將user對象作為rootObject傳遞給對應的Expression以獲取對應的值。

@Test
public void test07() {
    Object user = new Object() {
        public String getName() {
            return "abc";
        }
    };
    ExpressionParser parser = new SpelExpressionParser();
    Assert.assertTrue(parser.parseExpression("name").getValue(user, String.class).equals("abc"));
    Assert.assertTrue(parser.parseExpression("getName()").getValue(user, String.class).equals("abc"));
}

List、Array、Map等元素的訪問

  在SpEl中我們可以通過索引的形式訪問List或Array的某一個元素,對應的索引是從0開始的,以“list[index]”的形式出現。如下述示例中的test08_1和test08_2。

@Test
public void test08_1() {
    Object user = new Object() {
        public List<String> getInterests() {
            List<String> interests = Arrays.asList(new String[] {"BasketBall", "FootBall"});
            return interests;
        }
    };
    ExpressionParser parser = new SpelExpressionParser();
    Assert.assertTrue(parser.parseExpression("interests[0]").getValue(user, String.class).equals("BasketBall"));
    Assert.assertTrue(parser.parseExpression("interests[1]").getValue(user, String.class).equals("FootBall"));
}
    
@Test
public void test08_2() {
    Object user = new Object() {
        public String[] getInterests() {
            return new String[] {"BasketBall", "FootBall"};
        }
    };
    ExpressionParser parser = new SpelExpressionParser();
    Assert.assertTrue(parser.parseExpression("interests[0]").getValue(user, String.class).equals("BasketBall"));
    Assert.assertTrue(parser.parseExpression("interests[1]").getValue(user, String.class).equals("FootBall"));
}

對於Map而言,則是通過類似於“map[key]”的形式訪問對應的元素的。示例如下

@Test
public void test08_3() {
    Object user = new Object() {
        public Map<String, String> getInterests() {
            Map<String, String> interests = new HashMap<String, String>();
            interests.put("key1", "BasketBall");
            interests.put("key2", "FootBall");
            return interests;
        }
    };
    ExpressionParser parser = new SpelExpressionParser();
    Assert.assertTrue(parser.parseExpression("interests['key1']").getValue(user, String.class).equals("BasketBall"));
    Assert.assertTrue(parser.parseExpression("interests['key2']").getValue(user, String.class).equals("FootBall"));
}

構造List

在SpEl中可以使用“{e1,e2,e3}”的形式來構造一個List,如下示例中我們就構造了一個List。

@Test
public void test09() {
    ExpressionParser parser = new SpelExpressionParser();
    List<Integer> intList = (List<Integer>)parser.parseExpression("{1,2,3,4,5,6}").getValue();
    int index = 0;
    for (Integer i : intList) {
        Assert.assertTrue(i == ++index);
    }
}

構造Map
  Map是可以key-value的形式存在的,在SpEl中如果需要構造一個Map,則可以使用“{key1:value1,key2:value2}”這樣的形式進行定義,即使用大括號包起來,然后key和value之間以冒號“:”分隔構成一個Entry,多個Entry之間以逗號分隔。如下示例中我們就構建了一個key為String,value為Long類型的Map。

@Test
public void test10() {
    ExpressionParser parser = new SpelExpressionParser();
    Map<String, Long> map = (Map<String, Long>)parser.parseExpression("{'key1':1L,'key2':2L}").getValue();
    Assert.assertTrue(map.get("key1").equals(1L));
    Assert.assertTrue(map.get("key2").equals(2L));
}

集合選擇
  SpEl允許將集合中的某些元素選出組成一個新的集合進行返回,這就是所謂的集合。打個比方,我們有一個List,其包含1-9共9個數字,通過集合選擇的功能,我們可以選出其中的奇數組成一個新的List進行返回,即1、3、5、7、9。集合的選擇使用的語法是“collection.?[condition]”,condition中直接使用的屬性、方法等都是針對於集合中的元素來的。如下示例中我們的user對象的getInterests()方法返回包含三個元素的List,然后我們通過endsWith(‘Ball’)篩選出以Ball結尾的元素組成一個新的List。

@Test
public void test12_1() {
    Object user = new Object() {
        public List<String> getInterests() {
            List<String> interests = new ArrayList<String>();
            interests.add("BasketBall");
            interests.add("FootBall");
            interests.add("Movie");
            return interests;
        }
    };
    ExpressionParser parser = new SpelExpressionParser();
    List<String> interests = (List<String>)parser.parseExpression("interests.?[endsWith('Ball')]").getValue(user);
    Assert.assertTrue(interests.size() == 2);
    Assert.assertTrue(interests.get(0).equals("BasketBall"));
    Assert.assertTrue(interests.get(1).equals("FootBall"));
}

  對於Map的選擇而言,其中的condition中直接使用的屬性和方法針對的主體都是Map的Entry。如下示例中我們通過條件value.endsWith(‘Ball’)選出Map中value以Ball結尾的Entry組成一個新的Map進行返回,對應的條件相當於Entry.getValue().endsWith(“Ball”)。

@Test
public void test12_2() {
    Object user = new Object() {
        public Map<String, String> getInterests() {
            Map<String, String> interests = new HashMap<String, String>();
            interests.put("key1", "BasketBall");
            interests.put("key2", "FootBall");
            interests.put("key3", "Movie");
            return interests;
        }
    };
    ExpressionParser parser = new SpelExpressionParser();
    Map<String, String> interests = (Map<String, String>)parser.parseExpression("interests.?[value.endsWith('Ball')]").getValue(user);
    Assert.assertTrue(interests.size() == 2);
    Assert.assertTrue(interests.get("key1").equals("BasketBall"));
    Assert.assertTrue(interests.get("key2").equals("FootBall"));
}

集合投影
  集合投影的意思是將集合中每個元素的某部分內容的組成一個新的集合進行返回。集合投影的語法是“collection.![projectionExpression]”,其中projectionExpression中直接使用的屬性和方法都是針對於collection中的每個元素而言的,對於List而言其就表示List中的每個元素,對於Map而言,其就表示Map中的每個Entry。在如下示例中我們就將List中的每一個元素調用endsWith()方法后的結果組成一個新的List進行返回。

@Test
public void test13_1() {
    Object user = new Object() {
        public List<String> getInterests() {
            List<String> interests = new ArrayList<String>();
            interests.add("BasketBall");
            interests.add("FootBall");
            interests.add("Movie");
            return interests;
        }
    };
    ExpressionParser parser = new SpelExpressionParser();
    List<Boolean> interests = (List<Boolean>)parser.parseExpression("interests.![endsWith('Ball')]").getValue(user);
    Assert.assertTrue(interests.size() == 3);
    Assert.assertTrue(interests.get(0).equals(true));
    Assert.assertTrue(interests.get(1).equals(true));
    Assert.assertTrue(interests.get(2).equals(false));
}

Map進行投影的結果是一個List。如下示例中我們就將一個Map的value投影為一個List,對應List中元素的順序是不定的。

@Test
public void test13_2() {
    Object user = new Object() {
        public Map<String, String> getInterests() {
            Map<String, String> interests = new HashMap<String, String>();
            interests.put("key1", "BasketBall");
            interests.put("key2", "FootBall");
            interests.put("key3", "Movie");
            return interests;
        }
    };
    ExpressionParser parser = new SpelExpressionParser();
    List<String> interests = (List<String>)parser.parseExpression("interests.![value]").getValue(user);
    Assert.assertTrue(interests.size() == 3);
    for (String interest : interests) {
        Assert.assertTrue(interest.equals("BasketBall") || interest.equals("FootBall") || interest.equals("Movie"));
    }
}

設置變量
  在前面我們已經介紹了EvaluationContext和rootObject的用法,貌似使用EvaluationContext時,直接使用rootObject更簡單一些。那是不是所有使用EvaluationContext的地方,都可以改成使用rootObject呢?

  答案是否定的。EvaluationContext的功能相比rootObject而言還是要豐富很多的,如其可以設置變量、方法等供表達式使用。對於變量而言,可以通過EvaluationContext的setVariable()方法進行設置,然后在表達式中使用時,通過“#varName”的形式進行使用。如下示例中我們就給EvaluationContext設置了一個名為“user”的變量,然后在表達式中通過“#user”來使用該變量。

@Test
public void test14() {
    Object user = new Object() {
        public String getName() {
            return "abc";
        }
    };
    EvaluationContext context = new StandardEvaluationContext();
    //1、設置變量
    context.setVariable("user", user);
    ExpressionParser parser = new SpelExpressionParser();
    //2、表達式中以#varName的形式使用變量
    Expression expression = parser.parseExpression("#user.name");
    //3、在獲取表達式對應的值時傳入包含對應變量定義的EvaluationContext
    String userName = expression.getValue(context, String.class);
    //表達式中使用變量,並在獲取值時傳遞包含對應變量定義的EvaluationContext。
    Assert.assertTrue(userName.equals("abc"));
}

#root

#root在表達式中永遠都指向對應EvaluationContext的rootObject對象。在如下示例中#root就指向了對應的user對象。

@Test
public void test14_1() {
    Object user = new Object() {
        public String getName() {
            return "abc";
        }
    };
    EvaluationContext context = new StandardEvaluationContext(user);
    ExpressionParser parser = new SpelExpressionParser();
    Assert.assertTrue(parser.parseExpression("#root.name").getValue(context).equals("abc"));
}

#this

#this永遠指向當前對象,其通常用於集合類型,表示集合中的一個元素。如下示例中我們就使用了#this表示當前元素以選出奇數作為一個新的List進行返回。

@Test
public void test14_2() {
    ExpressionParser parser = new SpelExpressionParser();
    List<Integer> intList = (List<Integer>)parser.parseExpression("{1,2,3,4,5,6}").getValue();
    EvaluationContext context = new StandardEvaluationContext(intList);
    //從List中選出為奇數的元素作為一個List進行返回,135。
    List<Integer> oddList = (List<Integer>)parser.parseExpression("#root.?[#this%2==1]").getValue(context);
    for (Integer odd : oddList) {
        Assert.assertTrue(odd%2 == 1);
    }
}

注冊方法
StandardEvaluationContext允許我們在其中注冊方法,然后在表達式中使用對應的方法。注冊的方法必須是一個static類型的公有方法。注冊方法是通過StandardEvaluationContext的registerFunction(funName,method)方法進行,其中第一個參數表示需要在表達式中使用的方法名稱,第二個參數表示需要注冊的java.lang.reflect.Method。在表達式中我們可以使用類似於“#funName(params…)”的形式來使用對應的方法。如下示例中我們就通過StandardEvaluationContext注冊了一個名叫plusTen的方法。

static class MathUtils {
        public static int plusTen(int i) {
            return i+10;
        }
    }
    
    @Test
    public void test15() throws NoSuchMethodException, SecurityException {
        ExpressionParser parser = new SpelExpressionParser();
        //1、獲取需要設置的java.lang.reflect.Method,需是static類型
        Method plusTen = MathUtils.class.getDeclaredMethod("plusTen", int.class);
        StandardEvaluationContext context = new StandardEvaluationContext();
        //2、注冊方法到StandardEvaluationContext,第一個參數對應表達式中需要使用的方法名
        context.registerFunction("plusTen", plusTen);
        //3、表達式中使用注冊的方法
        Expression expression = parser.parseExpression("#plusTen(10)");
        //4、傳遞包含對應方法注冊的StandardEvaluationContext給Expression以獲取對應的值
        int result = expression.getValue(context, int.class);
        Assert.assertTrue(result == 20);
    }

new對象
  SpEl支持我們直接在表達式中使用Java中new對象的語法來new一個對象,在new對象的時候,需要我們指定對應類的包名,java.lang包除外。如下示例中我們就在表達式中new了一個java.util.Date對象,然后調用了其toLocaleString()方法。

  @Test
    public void test16() {
        ExpressionParser parser = new SpelExpressionParser();
        String currentTime = (String)parser.parseExpression("new java.util.Date().toLocaleString()").getValue();
        System.out.println(currentTime);
    }

賦值

SpEl也支持給表達式賦值,其是通過Expression的setValue()方法進行的,在賦值時需要指定rootObject或對應的EvaluationContext。示例如下。

@Test
public void test17_1() {
    ExpressionParser parser = new SpelExpressionParser();
    Date d = new java.util.Date();
    //設日期為1號
    parser.parseExpression("date").setValue(d, 1);
    int date = (Integer)parser.parseExpression("date").getValue(d);
    Assert.assertTrue(date == 1);
}

其也支持List、Map等的賦值。對於List和Array而言,在進行賦值時是通過元素的索引進行的,且對應的索引必須是存在的。如下示例中我們就將list的第一個元素由0設置為了1

@Test
public void test17_2() {
    ExpressionParser parser = new SpelExpressionParser();
    List<Integer> list = new ArrayList<Integer>(1);
    list.add(0);//添加一個元素0
    EvaluationContext context = new StandardEvaluationContext();
    //添加變量以方便表達式訪問
    context.setVariable("list", list);
    //設置第一個元素的值為1
    parser.parseExpression("#list[0]").setValue(context, 1);
    int first = (Integer)parser.parseExpression("#list[0]").getValue(context);
    Assert.assertTrue(first == 1);
}

對於Map的賦值而言是通過key進行的,對應的key在Map中可以先不存在。如下示例就是對Map的賦值。

@Test
public void test17_3() {
    ExpressionParser parser = new SpelExpressionParser();
    Map<String, Integer> map = new HashMap<String, Integer>();
    EvaluationContext context = new StandardEvaluationContext();
    //添加變量以方便表達式訪問
    context.setVariable("map", map);
    //設置第一個元素的值為1
    parser.parseExpression("#map['key1']").setValue(context, 1);
    int first = (Integer)parser.parseExpression("#map['key1']").getValue(context);
    Assert.assertTrue(first == 1);
}

訪問靜態方法或屬性

  SpEl也支持訪問類的靜態方法或屬性,其在進行訪問的時候需要使用“T(type)”的形式來表示對應的靜態類,其中type表示對應類的全限定名,即包括對應的包名。如下示例中就在表達式中訪問了java.util.Calendar的靜態屬性DATE

@Test
public void test18() {
    ExpressionParser parser = new SpelExpressionParser();
    Assert.assertTrue(parser.parseExpression("T(java.util.Calendar).DATE").getValue(int.class) == 5);
}

使用字符代替符號

SpEl也允許我們使用某些字符來代替對應的符號,如ge(>=)、gt(>)、lt(<)、le(<=)、eq(==)、ne(!=)、div(/)、mod(%)、not(!),而且它們都是大小寫不敏感的。使用時中間要以空格分開,示例如下。

@Test
public void test19() {
    ExpressionParser parser = new SpelExpressionParser();
    Assert.assertTrue(parser.parseExpression("1 lt 2").getValue(boolean.class));//1<2
    Assert.assertTrue(parser.parseExpression("1 le 2").getValue(boolean.class));//1<=2
    Assert.assertTrue(parser.parseExpression("2 gt 1").getValue(boolean.class));//2>1
    Assert.assertTrue(parser.parseExpression("2 ge 1").getValue(boolean.class));//2>=1
    Assert.assertTrue(parser.parseExpression("1 ne 2").getValue(boolean.class));//1!=2
    Assert.assertTrue(parser.parseExpression("not false").getValue(boolean.class));//!false
}

使用正則表達式

SpEl也支持使用正則表達式,其中對應的關鍵字為match。如下示例中即在表達式中使用了正則表達式,表示123是否匹配正則表達式“\d{3}”。

@Test
public void test20 () {
    ExpressionParser parser = new SpelExpressionParser();
    Assert.assertTrue(parser.parseExpression("123 matches '\\d{3}'").getValue(Boolean.class));//正則匹配三位數字
}

使用instanceof

SpEl也支持在表達式中使用instanceof關鍵字,以檢測對象是否是特定類型的示例。

@Test
public void test21 () {
    ExpressionParser parser = new SpelExpressionParser();
    Assert.assertTrue(parser.parseExpression("'123' instanceof T(String)").getValue(Boolean.class));//檢測字符串是否是String的實例。
}

三目運算(if..else..)

SpEl也支持在表達式中使用三目運算符,形式為“exp?trueVal:falseVal”,即如果exp的值為true則返回trueVal,否則返回falseVal。

@Test
public void test22 () {
    ExpressionParser parser = new SpelExpressionParser();
    Assert.assertTrue(parser.parseExpression("1>2 ? 1 : 2").getValue(int.class) == 2);//1跟2之間的較大者為2。
    Assert.assertTrue(parser.parseExpression("1<2 ? 2 : 1").getValue(int.class) == 2);//1跟2之間的較大者為2。
}

表達式模板
  SpEL還支持在解析表達式時將其當做一個字符串模板進行解析,即可以在表達式中混合普通的文本和特定的表達式塊,然后在解析的時候將解析將對其中的表達式塊進行計算,以實現模板功能。此功能需要我們在解析表達式時傳入一個特定的ParserContext,其可以影響SpEl表達式的解析,對應的模板功能應該傳遞一個TemplateParserContext。這樣Spring在解析對應的SpEl表達式時將會把其當做一個模板,然后對其中“#{exp}”形式的表達式進行計算。如下示例就是表達式模板的一個簡單用法,其中使用#{}包起來的表達式會被當做一個普通的SpEl表達式進行計算以得出當前的年份,再進行替換,所以所得結果將是“the year is 2014”。

@Test
public void test23 () {
    //the year is 2014
    String expressionStr = "the year is #{T(java.util.Calendar).getInstance().get(T(java.util.Calendar).YEAR)}";
    ExpressionParser parser = new SpelExpressionParser();
    Expression expression = parser.parseExpression(expressionStr, new TemplateParserContext());
    Assert.assertTrue(expression.getValue().equals("the year is 2014"));
}

設置默認值

SpEl表達式中支持“a?:b”這樣的語法來設置默認值。其表示如果a不為null時其結果為a,否則就為b。

@Test
public void test24 () {
    ExpressionParser parser = new SpelExpressionParser();
    Assert.assertTrue(parser.parseExpression("#abc?:123").getValue().equals(123));//變量abc不存在
    Assert.assertTrue(parser.parseExpression("1?:123").getValue().equals(1));//數字1不為null
}

安全導航
  我們可能經常會使用類似於“a.b.c”這樣的用法,表示a的b屬性的c屬性,但如果a為null或者a的b屬性為null時都會出現空指針。為了避免此種情況發生,我們可以在SpEl表達式中使用安全導航,這樣當a為null或a的b屬性為null時將直接返回null,而不拋出空指針異常。SpEl表達式中安全導航的語法是將點“.”替換為“?.”,即不使用“a.b.c”,而是使用“a?.b?.c”。

@Test
public void test25 () {
    ExpressionParser parser = new SpelExpressionParser();
    Assert.assertNull(parser.parseExpression("null?.abc").getValue());
    Assert.assertNull(parser.parseExpression("T(System)?.getProperty('abc')?.length()").getValue());//數字1不為null
}

獲取bean對象

  在SpEL表達式里面也可以直接訪問bean對象,前提是指定一個BeanResolver。BeanResolver是一個接口,其只定義了一個方法resolve,用以通過beanName解析為對應的bean對象並返回,具體定義如下。

public interface BeanResolver {

    Object resolve(EvaluationContext context, String beanName) throws AccessException;

}

  如果要在SpEL表達式中訪問bean對象,需要通過StandardEvaluationContext來設置對應的BeanResolver,同時需要在SpEL表達式中以“@beanName”的方式來訪問對應的bean對象。如下是一段示例代碼,在表達式中獲取到了名稱為hello的bean對象,並訪問了其getKey()方法。
  

@Test
public void test26() {
    ExpressionParser parser = new SpelExpressionParser();
    StandardEvaluationContext context = new StandardEvaluationContext();
    context.setBeanResolver(new MyBeanResolver());
    //訪問bean名稱為hello的bean對象的getKey()方法。
    Object obj = parser.parseExpression("@hello.key").getValue(context);
    System.out.println(obj);
}

private static class MyBeanResolver implements BeanResolver {

    private static ApplicationContext appContext = new ClassPathXmlApplicationContext("applicationContext.xml");
    
    public Object resolve(EvaluationContext context, String beanName)
            throws AccessException {
        return appContext.getBean(beanName);
    }
    
}

SpelParserConfiguration
  在構建SpelExpressionParser時,可以給其傳遞一個SpelParserConfiguration對象以對SpelExpressionParser進行配置。其可以用於指定在遇到List或Array為null時是否自動new一個對應的實例,對應SpelParserConfiguration的第一個構造參數也可以指定在List或Array中對應索引超出了當前索引的最大值時是否自動進行擴充,對應SpelParserConfiguration的第二個構造參數

  如下示例中我們就使用了SpelParserConfiguration對象,指定在對應的List或Array為null時自動new一個對應的對象,並且在對應的索引超出了List或Array當前的最大索引時自動對其進行擴充。所以如下示例中,在我們第一次訪問User的interests時其為null,之后第二次訪問時,由於指定將自動new對應的對象並且在索引超出時自動進行擴充,所以將new一個List的實例,對應ArrayList,且在索引5不存在時將自動擴充並進行填值,填值時將對List的元素類型String new 6次。所以對於這種情況,我們需要保證List或Array中存放的元素類型存在無參構造方法。

class User {
    public List<String> interests;
}

@Test
public void test() {
    User user = new User();
    SpelParserConfiguration parserConfig = new SpelParserConfiguration(true, true);
    ExpressionParser parser = new SpelExpressionParser(parserConfig);
    //第一次為null
    Assert.assertNull(parser.parseExpression("interests").getValue(user));
    //自動new一個List的實例,對應ArrayList,並自動new String()添加6次。
    Assert.assertTrue(parser.parseExpression("interests[5]").getValue(user).equals(""));
    //size為6
    Assert.assertTrue(parser.parseExpression("interests.size()").getValue(user).equals(6));
}

在bean定義中使用SpEl

在bean定義中使用SpEl表達式的語法是“#{exp}”。exp就是對應的表達式。如下示例中我們定義一個名為hello的bean,在指定其userDir時我們使用了表達式。

<bean id="hello" class="com.app.Hello">
    <property name="userDir" value="#{T(System).getProperty('user.dir')}"/>
</bean>

對於系統屬性而言,在bean定義中使用時有一個內置的變量可以使用叫systemProperties,而且在使用時不需要加“#”,不需要以“#systemProperties”的形式出現。所以上述示例也可以是如下這樣。

<bean id="hello" class="com.elim.learn.spring.bean.Hello">
    <property name="userDir" value="#{systemProperties['user.dir']}"/>
</bean>

 引用其它bean的屬性

在進行bean定義時,我們也可以通過表達式引用其它bean定義的屬性。如下示例中我們就在定義id為world的bean的key屬性時通過表達式引用了名為hello的bean的key屬性,即world的key屬性也將被賦予值“abc”。

<bean id="hello" class="com.app.Hello">
    <property name="key" value="abc"/>
</bean>

<bean id="world" class="com.app.World">
    <property name="key" value="#{hello.key}"/>
</bean>

基於注解配置的使用
  在基於注解配置的bean定義中,可以使用SpEl表達式進行某些定義。在基於注解配置bean定義時,我們可以使用@Value注解定義在方法或屬性上來指定對應的值。此時我們就可以使用對應的表達式,當然不使用表達式也是可以的。如下示例中我們就通過@Value指定了userDir和key的值。其中userDir的值的定義使用了SpEl表達式,而key的值的定義是直接定義的。

public class Hello {

    @Value("#{systemProperties['user.dir']}")
    private String userDir;
    @Value("abc")
    private String key;

    public String getUserDir() {
        return userDir;
    }

    public void setUserDir(String userDir) {
        this.userDir = userDir;
    }

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }

}

 


  

參考:

原文:https://blog.csdn.net/elim168/article/details/77973357 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM