本章使用的依赖基本被下文件包括:
<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,用户语言为中文。项目目录结构很简单:
事实上,也可以用注解完成配置,此时的配置类为:
@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!"));
}
}
测试通过。
首先在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();
}
}
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对象中,之后就可以在变量名前加#引用已经注册的变量了。
可以在评估上下文中设置一个根对象,当表达式中遇到未知方法和属性时使用该对象进行查找。
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 {
}
提供对当前评估过程的引用。
使用前缀@来访问:
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"));
}
}
<, >, <=, >=, ==, !=, lt, gt, le, ge, eq, ne
+, -, *, /, %, ^
&&, ||, !, and, or, not, between, instanceof
? : (ternary), ? : (elvis)
?.(safe navigation), ?[...](selection), ![...](projection), ^[...](first element), $[...](last element)
"'Hello' instanceof T(String)"
返回一个true值的Boolean变量。 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));
}
在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>