SpEL语言的
SpEL即Spring动态表达语言,是一个支持运行时查询和操作对象对的强大的动态语言。
#
引入该语言的原因:
(1)弥补Java语言的劣势——无法直接进行表达式语句的动态解析。
Java语言是一门强类型语言,所有的代码必须在运行前进行严格的类型检验
并且编译成JVM字节码,虽然安全性及性能较好,但是不具有灵活性
(2)动态语言具有的优势:可以在运行时进行程序结构及变量类型的改变、简单、灵活。
(如:Python、JavaScript、Ruby等语言)
(3)Java在实现复杂的业务系统,大型商业系统、分布式系统及其中间件等方面优势很强,
有时候需要引用动态语言的一些特性,jdk1.6后内嵌JavaScript解析引擎,方便在Java中调用JavaScript编写的动态脚本。
(4)于是Spring社区弥补Java语言动态性方面的不足,提供了Spring动态语言(SpEL语言)。
SpEL提供的核心接口
ExpressionParse接口
该接口用来解析表达式字符串,即用一个单引号标注或者转义的双引号标注的字符串。
实现类:SplExpressionParsser
EvaluationnContext接口
EvaluationContect接口提供属性、方法、字段解析器。
默认实现类StandardEvaluationContext的内部使用反射机制来操作对象;
为了提高性能,在其内容会对已经获得的Method、Field和Constructor实例进行缓存。
SpEL基础表达式
文本字符解析
在文本白表达式中支持字符串,日期,字符,布尔以及null。
若为字符串需要使用单引号或者反斜线+双引号将字符串括起来。
如:“ ‘hello world’”或者“\"hello world\"”
package com.dyy.springcore.spel
import org.springframework.expression.ExpressionParser
import org.springframework.expression.spel.standard.SpelExpressionParser
public class SpelTextApplication {
public static void main(String[] args) {
//文本字符解析
//构造表达式的解析实例
ExpressionParser parser = new SpelExpressionParser()
//字符串需要使用单引号引起来
String strVal = parser.parseExpression("'hello world'").getValue(String.class)
System.out.println(strVal)
//字符串使用双引号则需要转义
String strVal2 = parser.parseExpression("\"hello world\"").getValue(String.class)
System.out.println(strVal2)
//给出数字解析成int类型
Integer intVal = parser.parseExpression("110").getValue(Integer.class)
System.out.println(intVal)
//解析boolean
Boolean booVal = parser.parseExpression("true").getValue(Boolean.class)
System.out.println(booVal)
}
}
对象属性解析
在进行对象属性解析时需要在取值时传递一个计算上下文参数“EvaluationContext”
在下例中将User实例作为上下文的根对象传递给EvaluationContext
这样的话SpEL表达式解析器就可以根据属性路径表达式获取上下文根对象的属性值。
package com.dyy.springcore.spel;
import java.util.Date;
public class User {
/**
* 用户名
*/
private String userName;
/**
* 最近访问时间
*/
private Date lastVisit;
/**
* 用户积分
*/
private Integer credits;
/**
* 用户出生地
*/
private PlaceOfBirth placeOfBirth;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Date getLastVisit() {
return lastVisit;
}
public void setLastVisit(Date lastVisit) {
this.lastVisit = lastVisit;
}
public Integer getCredits() {
return credits;
}
public void setCredits(Integer credits) {
this.credits = credits;
}
public PlaceOfBirth getPlaceOfBirth() {
return placeOfBirth;
}
public void setPlaceOfBirth(PlaceOfBirth placeOfBirth) {
this.placeOfBirth = placeOfBirth;
}
}
package com.dyy.springcore.spel;
public class PlaceOfBirth {
private final String nation;
private final String district;
public PlaceOfBirth(String nation, String district) {
this.nation = nation;
this.district = district;
}
public String getNation() {
return nation;
}
public String getDistrict() {
return district;
}
@Override
public String toString() {
return "PlaceOfBirth{" +
"nation='" + nation + '\'' +
", district='" + district + '\'' +
'}';
}
}
package com.dyy.springcore.spel
import org.springframework.expression.EvaluationContext
import org.springframework.expression.ExpressionParser
import org.springframework.expression.spel.standard.SpelExpressionParser
import org.springframework.expression.spel.support.StandardEvaluationContext
import java.util.Date
public class SpelObjectAplication {
public static void main(String[] args) {
//创建user对象
User user = new User()
user.setUserName("lemon")
user.setLastVisit(new Date())
user.setCredits(10)
PlaceOfBirth placeOfBirth = new PlaceOfBirth("陕西","渭南")
user.setPlaceOfBirth(placeOfBirth)
System.out.println(user)
System.out.println(user.getPlaceOfBirth())
//SpEL访问对象属性
ExpressionParser parser = new SpelExpressionParser()
//上下文
EvaluationContext context = new StandardEvaluationContext(user)
//访问用户姓名
System.out.println()
String strName = (String) parser.parseExpression("userName").getValue(context)
System.out.println(strName)
//访问出生地
PlaceOfBirth placeOfBirth1 = parser.parseExpression("placeOfBirth").getValue(context,PlaceOfBirth.class)
System.out.println(placeOfBirth1)
//访问出生地中的省份
String strNation = parser.parseExpression("placeOfBirth.nation").getValue(context,String.class)
System.out.println(strNation)
//在不使用上下文时,也可达到与上述同样的效果,但是相比来说效率较低
String strNation2 = parser.parseExpression("placeOfBirth.nation").getValue(user,String.class)
System.out.println(strNation2)
}
}
数组和集合解析
在SpEL中
package com.dyy.springcore.spel
import org.springframework.expression.ExpressionParser
import org.springframework.expression.spel.standard.SpelExpressionParser
import sun.plugin.javascript.navig.Array
import java.util.Arrays
import java.util.List
import java.util.Map
public class SpelCollectionApplicatio {
public static void main(String[] args) {
ExpressionParser parser = new SpelExpressionParser()
//List集合
List listValue = (List) parser.parseExpression("{1,2,3}").getValue()
System.out.println(listValue)
List listValue2 = (List) parser.parseExpression("{'lemon','demon'}").getValue()
System.out.println(listValue2)
//数组
int [] intArray = (int[]) parser.parseExpression("new int[]{1,2,3}").getValue()
System.out.println(Arrays.toString(intArray))
for (int i = 0
System.out.print(" "+intArray[i])
}
System.out.println()
//Map集合
Map mapVal = (Map) parser.parseExpression("{userName:'lemon',password:'lemon' }").getValue()
System.out.println(mapVal)
}
}
方法解析
SpEL中,方法调用支持Java可以访问的方法,
如:静态方法,对象方法、可变方法参数,还可以调用Spring类型所有可访问的方法
package com.dyy.springcore.spel
import org.springframework.expression.EvaluationContext
import org.springframework.expression.ExpressionParser
import org.springframework.expression.spel.standard.SpelExpressionParser
import org.springframework.expression.spel.support.StandardEvaluationContext
import java.text.SimpleDateFormat
import java.util.Date
import java.util.logging.SimpleFormatter
public class SpelMethodApplication {
public static void main(String[] args) {
ExpressionParser parser = new SpelExpressionParser()
//String中方法的解析(substring)
String strName = (String) parser.parseExpression("'hello world'.substring(6)").getValue()
System.out.println(strName)
//静态方法System.currentTimeMillis()
Long dateVal = parser.parseExpression("T(java.lang.System).currentTimeMillis()").getValue(Long.class)
System.out.println(dateVal)
//实例方法
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd")
EvaluationContext context = new StandardEvaluationContext(simpleDateFormat)
String timeVal = parser.parseExpression("format(new java.util.Date())").getValue(context,String.class)
System.out.println(timeVal)
}
}
Spring中使用SpEL
基于XML的配置
通过java.lang.Math的静态方法获取随机数
package com.dyy.springcore.spel;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import sun.awt.AppContext;
public class GuessNumber {
private double num;
public double getNum() {
return num;
}
public void setNum(double num) {
this.num = num;
}
@Override
public String toString() {
return "GuessNumber{" +
"num=" + num +
'}';
}
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("application-content.xml");
GuessNumber guessNumber = (GuessNumber) context.getBean("guessNumber");
for (int i = 0;i <10;i++){
System.out.println(guessNumber.hashCode());
}
}
}
<bean id="guessNumber" class="com.dyy.springcore.spel.GuessNumber" scope="prototype">
<property name="num" value="#{T(java.lang.Math).random()}"/>
</bean>
Spring中两个内置Bean
我们可以通过systemProperties和systemEnviroment两个Bean,
来获取系统属性变量值和系统环境变量值。
package com.dyy.springcore.spel;
/**
*@Description: 此类用于测试Spring中内置的Bean——systemProperties
* 此外还有一个内置Bean :systemEnvironment
*@Author: dyy
*@CreateDate:7-17
*/
public class SystemPropertiesBean {
private String classPath;
private String javaHome;
private String javaVersion;
private String osName;
public String getClassPath() {
return classPath;
}
public void setClassPath(String classPath) {
this.classPath = classPath;
}
public String getJavaHome() {
return javaHome;
}
public void setJavaHome(String javaHome) {
this.javaHome = javaHome;
}
public String getJavaVersion() {
return javaVersion;
}
public void setJavaVersion(String javaVersion) {
this.javaVersion = javaVersion;
}
public String getOsName() {
return osName;
}
public void setOsName(String osName) {
this.osName = osName;
}
@Override
public String toString() {
return "SystemPropertiesBean{" +
"classPath='" + classPath + '\'' +
", javaHome='" + javaHome + '\'' +
", javaVersion='" + javaVersion + '\'' +
", osName='" + osName + '\'' +
'}';
}
}
<bean id="systemPropertiesBean" class="com.dyy.springcore.spel.SystemPropertiesBean">
<property name="classPath" value="#{systemProperties['java.class.path']}"/>
<property name="javaHome" value="#{systemProperties['java.home']}"/>
<property name="javaVersion" value="#{systemProperties['java.version']}"/>
<property name="osName" value="#{systemProperties['os.name']}"/>
</bean>
基于注解的配置
@Value注解可以用在类的属性、方法及构造函数上。
如:从配置文件中加载参数值
#
配置参数:database.properties
url=jdbc:mysql://localhost:3306/memo
classname=com.mysql.jdbc.Driver
username=root
password=100521
我们要使@Compent注解生效,则应该在配置文件中开启自动扫描。
<context:component-scan base-package="com.dyy.springcore.spel"/>
<util:properties id="properties" location="database.properties"/>
package com.dyy.springcore.spel;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
*@Description: 此类用于基于注解的配置类
*@Author: dyy
*@CreateDate:7-17
*/
@Component
public class SystemEnvirmentBean {
@Value(value = "#{properties['url']}")
private String url;
@Value(value = "#{properties['classname']}")
private String classname;
@Value(value = "#{properties['username']}")
private String username;
@Value(value = "#{properties['password']}")
private String password;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getClassname() {
return classname;
}
public void setClassname(String classname) {
this.classname = classname;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "SystemEnvirmentBean{" +
"url='" + url + '\'' +
", classname='" + classname + '\'' +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
在上面我们使用@Value(value = "#{properties['username']}")等来进行配合注解使用,
实则Spring提供属性占位符,使得我们使用起来更加方便。@Value("${jdbc.password}")
资源配置文件
当我们在配置Bean的时候,大多数情况都是固定的,
但是我们难免会遇到一些不可预知的信息,
如:配置数据库用户名、密码
此时我们就需要外部资源配置文件来解决该问题
<bean class="org.springframework.beans.factory.config.PreferencesPlaceholderConfigurer">
<property name="fileEncoding" value="UTF-8"/>
<property name="location" value="database.properties"/>
</bean>
<bean class="org.springframework.beans.factory.config.PreferencesPlaceholderConfigurer">
<property name="fileEncoding" value="UTF-8"/>
<property name="locations">
<list>
<value>file:D://database.properties</value>
<value>classpath:database.properties</value>
</list>
</property>
</bean>
使用配置属性文件
<context:property-placeholder file-encoding="UTF-8" location="classpath:database.properties"/>
使用@Value注解给Bean属性注入值
package com.dyy.springcore.spel;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
*@Description: 该类采用属性占位符$ 来进行@Value注解的配置
*@Author: dyy
*@CreateDate:7-17
*/
@Component
public class MyDataSource {
@Value("${url}")
private String url;
@Value("${classname}")
private String classname;
@Value("${username}")
private String username;
@Value("${password}")
private String password;
@Override
public String toString() {
return "MyDataSource{" +
"url='" + url + '\'' +
", classname='" + classname + '\'' +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
Spring资源接口
在使用的过程中,我们未免会与一些资源文件打交道,如:来自本地的,来自jar包的,来自一个URL的等。
Spring的Resource接口提供了统一的访问底层资源的能力。
#
ByteArrayResource:代表了byte[]数组资源。
InputStreamResource:代表了java.io.InputStream字节流
FileSystemResource:代表了java.io.File资源
ClassPathRsource:代表了classpath路径的资源,将使用ClassLoader进行加载资源
#
我们知道了Spring资源接口的实现类之后,则可以
通过文件名的前缀来动态决定加载资源时使用的类。
package com.dyy.springcore.spel;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import java.io.*;
import java.net.MalformedURLException;
public class ResourceLoad {
public static Resource loadResource(String fileName){
if(fileName==null||fileName.length()==0){
return null;
}
if(fileName.startsWith("file:")){
return new FileSystemResource(fileName.substring("file:".length()));
}
if(fileName.startsWith("classpath")){
return new ClassPathResource(fileName.substring("classpath:".length()));
}
if(fileName.startsWith("http")||fileName.startsWith("https")||fileName.startsWith("ftp")){
try {
return new UrlResource(fileName);
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
return null;
}
public static void writeToDirection(Resource resource){
File file = new File("D:\\data.properties");
FileOutputStream fileOutputStream = null;
InputStream inputStream = null;
try {
inputStream = resource.getInputStream();
fileOutputStream = new FileOutputStream("file");
int len = 0;
byte[] data = new byte[1024];
while ((len = inputStream.read())!=-1){
fileOutputStream.write(data,0,len);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
fileOutputStream.close();
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Resource resource = loadResource("classpath:database.properties");
writeToDirection(resource);
}
}