Beginning Spring学习笔记——第9章 SpEL

来源:互联网 发布:淘宝滥发信息怎么申诉 编辑:程序博客网 时间:2024/06/14 13:55

使用SpEL配置应用程序


本章使用的依赖基本被下文件包括:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">  <modelVersion>4.0.0</modelVersion>  <groupId>org.springframework.samples.service.service</groupId>  <artifactId>SpringAOPTest</artifactId>  <version>0.0.1-SNAPSHOT</version>  <packaging>war</packaging>    <properties>        <!-- Generic properties -->        <java.version>1.6</java.version>        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>        <!-- Web -->        <jsp.version>2.3.1</jsp.version>        <jstl.version>1.2</jstl.version>        <servlet.version>3.1.0</servlet.version>        <!-- Spring -->        <spring-framework.version>4.3.10.RELEASE</spring-framework.version>        <!-- Hibernate / JPA -->        <hibernate.version>5.2.10.Final</hibernate.version>        <!-- Logging -->        <logback.version>1.2.3</logback.version>        <slf4j.version>1.7.25</slf4j.version>        <!-- Test -->        <junit.version>4.12</junit.version>        <!-- AspectJ -->        <aspectj.version>1.8.10</aspectj.version>    </properties>    <dependencies>        <!-- Spring MVC -->        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-webmvc</artifactId>            <version>${spring-framework.version}</version>        </dependency>        <!-- Other Web dependencies -->        <dependency>            <groupId>javax.servlet</groupId>            <artifactId>jstl</artifactId>            <version>${jstl.version}</version>        </dependency>        <dependency>            <groupId>javax.servlet</groupId>            <artifactId>javax.servlet-api</artifactId>            <version>${servlet.version}</version>            <scope>provided</scope>        </dependency>        <dependency>            <groupId>javax.servlet.jsp</groupId>            <artifactId>javax.servlet.jsp-api</artifactId>            <version>${jsp.version}</version>            <scope>provided</scope>        </dependency>        <!-- Spring and Transactions -->        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-tx</artifactId>            <version>${spring-framework.version}</version>        </dependency>        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-aop</artifactId>            <version>${spring-framework.version}</version>        </dependency>        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-core</artifactId>            <version>${spring-framework.version}</version>        </dependency>        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-beans</artifactId>            <version>${spring-framework.version}</version>        </dependency>        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-context</artifactId>            <version>${spring-framework.version}</version>        </dependency>        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-expression</artifactId>            <version>${spring-framework.version}</version>        </dependency>        <!-- Logging with SLF4J & LogBack -->        <dependency>            <groupId>org.slf4j</groupId>            <artifactId>slf4j-api</artifactId>            <version>${slf4j.version}</version>            <scope>compile</scope>        </dependency>        <dependency>            <groupId>ch.qos.logback</groupId>            <artifactId>logback-classic</artifactId>            <version>${logback.version}</version>            <scope>runtime</scope>        </dependency>        <!-- Hibernate -->        <dependency>            <groupId>org.hibernate</groupId>            <artifactId>hibernate-entitymanager</artifactId>            <version>${hibernate.version}</version>        </dependency>        <!-- Test Artifacts -->        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-test</artifactId>            <version>${spring-framework.version}</version>            <scope>test</scope>        </dependency>        <dependency>            <groupId>junit</groupId>            <artifactId>junit</artifactId>            <version>${junit.version}</version>            <scope>test</scope>        </dependency>        <!-- AspectJ -->        <dependency>            <groupId>org.aspectj</groupId>            <artifactId>aspectjweaver</artifactId>            <version>${aspectj.version}</version>        </dependency>    </dependencies> </project>

首先在src/main/resource文件夹下创建上下文配置文件applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"       xsi:schemaLocation="http://www.springframework.org/schema/beans                           http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">    <bean id="show1" class="com.wiley.beginningspring.ch9.MyBean">        <property name="message" value="#{systemProperties['user.language']}" />    </bean></beans>

此处将类MyBean定义成了名为show1的类,并向其中的message属性通过SpEL注入了系统属性中的用户语言。
接下来创建MyBean类。

public class MyBean {    private String message;    public String getMessage() {        return message;    }    public void setMessage(String message) {        this.message = message;    }}

然后就可以在Main类中测试配置了。

public class Main {    public static void main(String... args) {        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");        MyBean myBean = context.getBean(MyBean.class);        System.out.println(myBean.getMessage());    }}

输出结果为zh,用户语言为中文。项目目录结构很简单:
目录结构
事实上,也可以用注解完成配置,此时的配置类为:

@Configuration@ComponentScan(basePackages = {"com.wiley.beginningspring.ch9"})public class ApplicationConfig {}

而MyBean类需要在类定义时被定义为一个Spring Bean:

@Componentpublic class MyBean {    @Value("#{systemProperties['user.language']}")    private String message;    public String getMessage() {        return message;    }}

其中message属性上通过@Value注解使用相同的SpEL语句注入了用户语言的值。

创建一个分析器


SpEL上下文中定义的表达式都应该首先被ExpressionParser解析然后被评估,该分析器对象是线程安全的。默认情况下,表达式模板以‘#’开头,‘}’结尾。分析器对象创建如下:
ExpressionParser parser = new SpELExpressionParser();
创建完分析器实例后就可以用它的parseExpression方法解析一个表达式创建一个表达式实例:
Expression expression = parser.parseExpression("'Hello World'");
然后就可以通过它的getValue方法获得表达式评估的值:
String value = expression.getValue(String.class)
下用 SpEL 解析一个 Hello World:

public class HelloWorldTest {    ExpressionParser parser;    @Before    public void setup() {        parser = new SpelExpressionParser();    }    @Test    public void helloWorldParsedOK() {        Expression expression = parser.parseExpression("'Hello World!'");        String value = expression.getValue(String.class);        assertThat(value, is("Hello World!"));    }}

测试通过。

通过SpEL调用方法


xml配置中调用方法

项目目录结构为:
目录结构

首先在src/main/resource文件夹中创建applicationContext.xml文件。

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"       xsi:schemaLocation="http://www.springframework.org/schema/beans                           http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">    <bean id="show1" class="com.wiley.beginningspring.ch9.Show">        <property name="instrument" value="Piano" />        <property name="song" value="Turning Tables" />    </bean>    <bean id="show2" class="com.wiley.beginningspring.ch9.Show">        <property name="instrument" value="Guitar" />        <property name="song" value="#{show2.guitarSong()}" />    </bean></beans>

该上下文文件中定义了两个类为Show的Bean。分别用字符串常量和SpEL表达式调用方法注入了值。
然后创建Show类:

public class Show {    private String instrument;    private String song;    public void setInstrument(String instrument) {        this.instrument = instrument;    }    public void setSong(String song) {        this.song = song;    }    public String guitarSong() {        return "More Than Words";    }    public void present() {        System.out.println("Playing " + song + " with instrument " + instrument);    }}

然后创建Main方法执行程序:

public class Main {    public static void main(String... args) {        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");        Show show1 = (Show) context.getBean("show1");        show1.present();        Show show2 = (Show) context.getBean("show2");        show2.present();    }}

运行输出结果:
输出结果

在字符串上调用方法及链接调用

public class NestedMethodInvocationStringConcatTest {    ExpressionParser parser;    @Before    public void setup() {        parser = new SpelExpressionParser();    }    @Test    public void helloParsedAndConcatenatedWithWorldAndThenLengthMethodInvoked() {        Expression exp = parser.parseExpression("'Hello'.concat(' World!').length()");        Integer value = exp.getValue(Integer.class);        assertThat(value, is(12));    }}

调用构造函数

public class ConstructorInvocationTest {    ExpressionParser parser;    @Before    public void setup() {        parser = new SpelExpressionParser();    }    @Test    public void constructorInvocationWorksOK() {        Expression exp = parser.parseExpression("new Double(3.141592653589793)");        Double value = exp.getValue(Double.class);        assertThat(value, is(3.141592653589793));    }}

调用静态方法

public class StaticConstantFieldAccessTest {    ExpressionParser parser;    @Before    public void setup() {        parser = new SpelExpressionParser();    }    @Test    public void staticConstantFieldAccessWorksOK() {        Expression exp = parser.parseExpression("T(java.lang.Math).PI");        Double value = exp.getValue(Double.class);        assertThat(value, is(3.141592653589793));    }}

使用变量和函数


可以通过context.setVariable("name",...)注册一个变量到评估上下文StandardEvaluationContext对象中,之后就可以在变量名前加#引用已经注册的变量了。

#root

可以在评估上下文中设置一个根对象,当表达式中遇到未知方法和属性时使用该对象进行查找。

public class RootVariablesTests {    ExpressionParser parser;    @Before    public void setup() {        parser = new SpelExpressionParser();    }    @Test    public void rootVariableRegisteredOK() {        StandardEvaluationContext context = new StandardEvaluationContext();        context.setRootObject(new MyBean());        assertTrue(parser.parseExpression("#root").getValue(context) instanceof MyBean);    }}

其中MyBean为任意类。

public class MyBean {}

#this

提供对当前评估过程的引用。

访问一同属性和环境变量

使用前缀@来访问:

String value = parser.parseExpression("@systemEnvironment[JAVA_HOME]").getValue(context, String.class);String value = parser.parseExpression("@systemProperties['java.version']").getValue(context, String.class);

内联列表

public class InlineListTests {    ExpressionParser parser;    @Before    public void setup() {        parser = new SpelExpressionParser();    }    @Test    public void inlineListCreatedOK() {        List<Integer> value = parser.parseExpression("{1,2,3}").getValue(List.class);        assertThat(value, hasItems(1, 2, 3));    }    @Test    public void inlineListOfListsCreatedOK() {        List<List<Integer>> value = parser.parseExpression("{{1,2},{3,4},{5,6}}").getValue(List.class);        assertThat(value, hasItems(Arrays.asList(1,2), Arrays.asList(3,4), Arrays.asList(5,6)));    }}

注册函数

除了注册变量外还可以注册函数,并在之后调用。注册方法为context.registerFunction("name",method)

public class FunctionRegistrationTests {    ExpressionParser parser;    @Before    public void setup() {        parser = new SpelExpressionParser();    }    @Test    public void functionRegisteredOK() throws NoSuchMethodException {        StandardEvaluationContext context = new StandardEvaluationContext();        context.registerFunction("capitalize",                StringUtils.class.getDeclaredMethod("capitalize", new Class[] { String.class }));        String value = parser.parseExpression("#capitalize('hello')").getValue(context, String.class);        assertThat(value, is("Hello"));    }}

SpEL运算符


  • 关系:<, >, <=, >=, ==, !=, lt, gt, le, ge, eq, ne
  • 算数:+, -, *, /, %, ^
  • 逻辑: &&, ||, !, and, or, not, between, instanceof
  • 条件:? : (ternary), ? : (elvis)
  • 其他类型:?.(safe navigation), ?[...](selection), ![...](projection), ^[...](first element), $[...](last element)
    其中 instantceof 可以用来判定表达式是否为某个类的实例,如"'Hello' instanceof T(String)"返回一个true值的Boolean变量。
    安全导航运算符用于在嵌套属性上进行导航,使未初始化的属性返回null值而不是抛出SpelEvaluationException。如
public class SafeNavigationOperatorsTest {    ExpressionParser p;    @Before    public void setup() {        p = new SpelExpressionParser();    }    @Test    public void safeNavigationOperatorsWorkOK() {        Employee employee = new Employee("Mert");        StandardEvaluationContext context = new StandardEvaluationContext(employee);        assertThat(p.parseExpression("Address?.Name").getValue(context, String.class), is(nullValue()));    }}

利用之前提到的#this还可以进行集合选择与投影将其转换为另一个集合。

    @Test    public void collectionSelectedOK() {        StandardEvaluationContext context = new StandardEvaluationContext();        context.setRootObject(Arrays.asList(1,2,3,4,5,6,7,8,9));        List<Integer> evenNumbers = parser.parseExpression("#root.?[#this%2 == 0 ?: false]").getValue(context, List.class);        assertThat(evenNumbers, hasItems(2, 4, 6, 8));    }

其中#this用来遍历集合中元素。
还可以通过![…]将一个集合投影到另一个集合,如下:

public class Worker {    private String name;    private Country birthPlace;    public Worker(String name, Country birthPlace) {        this.name = name;        this.birthPlace = birthPlace;    }    public String getName() {        return name;    }    public Country getBirthPlace() {        return birthPlace;    }}public enum Country {    TR,    USA,    DE}

投影Worker到Country:

    @Test    public void collectionProjectedOK() {        StandardEvaluationContext context = new StandardEvaluationContext();        context.setRootObject(Arrays.asList(                new Worker("Mert", Country.DE),                new Worker("Funda", Country.TR),                new Worker("Tugce", Country.USA)));        List<Country> birthPlaces = parser.parseExpression("#root.![#this.birthPlace]").getValue(context, List.class);        assertThat(birthPlaces, hasItems(Country.TR, Country.USA, Country.DE));    }

使用SpEL中的实用工具


访问Spring Bean

在Bean名称前添加@来访问

public class SpringBeanAccessTests {    ExpressionParser parser;    @Before    public void setup() {        parser = new SpelExpressionParser();    }    @Test    public void springBeanAccessWorksOK() {        StandardEvaluationContext context = new StandardEvaluationContext();        context.setBeanResolver(new BeanFactoryResolver(new AnnotationConfigApplicationContext(ApplicationConfig.class)));        Expression exp = parser.parseExpression("@myBean.sayHello()");        String value = exp.getValue(context, String.class);        assertThat(value, is("Hello!"));    }}@Componentpublic class MyBean {    public String sayHello() {        return "Hello!";    }}@Configuration@ComponentScan(basePackages = {"com.wiley.beginningspring.ch9"})public class ApplicationConfig {}

<spring:eval>

使用spring.tld中的该标签可以将评估值显示到JSP页面或为变量分配值。

<body>    <spring:eval expression = "@MyBean.sayHi()"/></body>
阅读全文
0 0
原创粉丝点击