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

使用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.0modelVersion>
  <groupId>org.springframework.samples.service.servicegroupId>
  <artifactId>SpringAOPTestartifactId>
  <version>0.0.1-SNAPSHOTversion>
  <packaging>warpackaging>

    <properties>

        
        <java.version>1.6java.version>
        <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>

        
        <jsp.version>2.3.1jsp.version>
        <jstl.version>1.2jstl.version>
        <servlet.version>3.1.0servlet.version>


        
        <spring-framework.version>4.3.10.RELEASEspring-framework.version>

        
        <hibernate.version>5.2.10.Finalhibernate.version>

        
        <logback.version>1.2.3logback.version>
        <slf4j.version>1.7.25slf4j.version>

        
        <junit.version>4.12junit.version>

        
        <aspectj.version>1.8.10aspectj.version>

    properties>

    <dependencies>

        
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-webmvcartifactId>
            <version>${spring-framework.version}version>
        dependency>

        
        <dependency>
            <groupId>javax.servletgroupId>
            <artifactId>jstlartifactId>
            <version>${jstl.version}version>
        dependency>
        <dependency>
            <groupId>javax.servletgroupId>
            <artifactId>javax.servlet-apiartifactId>
            <version>${servlet.version}version>
            <scope>providedscope>
        dependency>
        <dependency>
            <groupId>javax.servlet.jspgroupId>
            <artifactId>javax.servlet.jsp-apiartifactId>
            <version>${jsp.version}version>
            <scope>providedscope>
        dependency>

        
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-txartifactId>
            <version>${spring-framework.version}version>
        dependency>

        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-aopartifactId>
            <version>${spring-framework.version}version>
        dependency>

        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-coreartifactId>
            <version>${spring-framework.version}version>
        dependency>

        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-beansartifactId>
            <version>${spring-framework.version}version>
        dependency>

        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-contextartifactId>
            <version>${spring-framework.version}version>
        dependency>

        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-expressionartifactId>
            <version>${spring-framework.version}version>
        dependency>

        
        <dependency>
            <groupId>org.slf4jgroupId>
            <artifactId>slf4j-apiartifactId>
            <version>${slf4j.version}version>
            <scope>compilescope>
        dependency>
        <dependency>
            <groupId>ch.qos.logbackgroupId>
            <artifactId>logback-classicartifactId>
            <version>${logback.version}version>
            <scope>runtimescope>
        dependency>

        
        <dependency>
            <groupId>org.hibernategroupId>
            <artifactId>hibernate-entitymanagerartifactId>
            <version>${hibernate.version}version>
        dependency>


        
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-testartifactId>
            <version>${spring-framework.version}version>
            <scope>testscope>
        dependency>
        <dependency>
            <groupId>junitgroupId>
            <artifactId>junitartifactId>
            <version>${junit.version}version>
            <scope>testscope>
        dependency>

        
        <dependency>
            <groupId>org.aspectjgroupId>
            <artifactId>aspectjweaverartifactId>
            <version>${aspectj.version}version>
        dependency>

    dependencies> 
project>

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


<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,用户语言为中文。项目目录结构很简单:
Beginning Spring学习笔记——第9章 SpEL_第1张图片
事实上,也可以用注解完成配置,此时的配置类为:

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

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

@Component
public 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配置中调用方法

项目目录结构为:
Beginning Spring学习笔记——第9章 SpEL_第2张图片

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


<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();
    }
}

运行输出结果:
Beginning Spring学习笔记——第9章 SpEL_第3张图片

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

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 value = parser.parseExpression("{1,2,3}").getValue(List.class);
        assertThat(value, hasItems(1, 2, 3));
    }

    @Test
    public void inlineListOfListsCreatedOK() {
        List> 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 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 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!"));
    }
}

@Component
public class MyBean {

    public String sayHello() {
        return "Hello!";
    }
}

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

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

<body>
    <spring:eval expression = "@MyBean.sayHi()"/>
body>

你可能感兴趣的:(Beginning,Spring学习笔记)