您现在的位置:首页 >> 前端 >> 内容

BeginningSpring学习笔记——SpEL

时间:2017/9/5 10:03:00 点击:

  核心提示:使用SpEL配置应用程序本章使用的依赖基本被下文件包括:4.0.0org.springframework.samples.service.serviceSpringAOPTest0.0.1-SNAPS...

使用SpEL配置应用程序


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


  4.0.0
  org.springframework.samples.service.service
  SpringAOPTest
  0.0.1-SNAPSHOT
  war

    

        
        1.6
        UTF-8
        UTF-8

        
        <jsp.version>2.3.1
        1.2
        3.1.0


        
        4.3.10.RELEASE

        
        5.2.10.Final

        
        1.2.3
        1.7.25

        
        4.12

        
        <aspectj.version>1.8.10

    

    

        
        
            org.springframework
            spring-webmvc
            ${spring-framework.version}
        

        
        
            javax.servlet
            jstl
            ${jstl.version}
        
        
            javax.servlet
            javax.servlet-api
            ${servlet.version}
            provided
        
        
            javax.servlet.jsp
            javax.servlet.jsp-api
            ${jsp.version}
            provided
        

        
        
            org.springframework
            spring-tx
            ${spring-framework.version}
        

        
            org.springframework
            spring-aop
            ${spring-framework.version}
        

        
            org.springframework
            spring-core
            ${spring-framework.version}
        

        
            org.springframework
            spring-beans
            ${spring-framework.version}
        

        
            org.springframework
            spring-context
            ${spring-framework.version}
        

        
            org.springframework
            spring-expression
            ${spring-framework.version}
        

        
        
            org.slf4j
            slf4j-api
            ${slf4j.version}
            compile
        
        
            ch.qos.logback
            logback-classic
            ${logback.version}
            runtime
        

        
        
            org.hibernate
            hibernate-entitymanager
            ${hibernate.version}
        


        
        
            org.springframework
            spring-test
            ${spring-framework.version}
            test
        
        
            junit
            junit
            ${junit.version}
            test
        

        
        
            org.aspectj
            aspectjweaver
            ${aspectj.version}
        

     

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




    
        
    

此处将类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,用户语言为中文。项目目录结构很简单:
BeginningSpring学习笔记——SpEL
事实上,也可以用注解完成配置,此时的配置类为:

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

项目目录结构为:
BeginningSpring学习笔记——SpEL

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




    
        
        
    

    
        
        
    

该上下文文件中定义了两个类为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();
    }
}

运行输出结果:
BeginningSpring学习笔记——SpEL

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

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页面或为变量分配值。

Tags:BE EG GI IN 
作者:网络 来源:LonelyQuan