Spring5 强大飘逸的表达式语言
一、探讨Environment的使用
1、Spring属性解析器标准接口介绍
public interface PropertyResolver {
//检查是否包含该属性,包含返回true,否则返回false
boolean containsProperty(String key);
//根据属性标识获取该属性对应的值,非必须定义,没有指定返回类型
String getProperty(String key);
//根据属性标识获取该属性对应的值,非必须定义,指定了默认值,没有指定返回类型
String getProperty(String key, String defaultValue);
//根据属性标识获取该属性对应的值,非必须定义,指定返回类型
<T> T getProperty(String key, Class<T> targetType);
//根据属性标识获取该属性对应的值,非必须定义,指定了默认值,指定返回类型
<T> T getProperty(String key, Class<T> targetType, T defaultValue);
//根据属性标识获取该属性对应的值,必须定义,如果没有获取到属性会抛出异常、没有指定返回类型
String getRequiredProperty(String key) throws IllegalStateException;
//根据属性标识获取该属性对应的值,必须定义,如果没有获取到属性会抛出异常(IllegalStateException)、指定返回类型
<T> T getRequiredProperty(String key, Class<T> targetType) throws IllegalStateException;
//解析占位符,非必须定义
String resolvePlaceholders(String text);
//解析占位符,必须定义,否则抛出异常
String resolveRequiredPlaceholders(String text) throws IllegalArgumentException
}
2、Spring Environment接口介绍
public interface Environment extends PropertyResolver {
//获得激活的Profile数组
String[] getActiveProfiles();
//获得默认的Profile数组
String[] getDefaultProfiles();
//支持给定的profile
boolean acceptsProfiles(String... profiles);
boolean acceptsProfiles(Profiles profiles);
}
3、Spring Environment的使用
创建配置文件app.properties
app.id=icypt
app.name=冰点科技
创建配置类JavaConfig.java
@Configuration
@PropertySource(value = , encoding = )
public class JavaConfig {
private Environment environment;
public AppService appService() {
return new AppService(environment.getRequiredProperty("app.id"), environment.getProperty("app.name"));
}
}
创建AppService.java
public class AppService {
private String appId;
private String appName;
public AppService(String appId, String appName) {
this.appId = appId;
this.appName = appName;
}
public String toString() {
return "AppService{" +
"appId='" + appId + '\'' +
", appName='" + appName + '\'' +
'}';
}
}
创建测试类TestAppService.java
public class TestAppService {
public static final Logger logger = LoggerFactory.getLogger(TestAppService.class);
public static AnnotationConfigApplicationContext acac = new AnnotationConfigApplicationContext(JavaConfig.class);
public void TestToString() {
AppService appService = acac.getBean("appService", AppService.class);
logger.info(appService.toString());
}
}
运行测试结果:
16:17:17,917 DEBUG main env.PropertySourcesPropertyResolver:115 - Found key 'app.id' in PropertySource 'class path resource [app.properties]' with value of type String
16:17:17,917 DEBUG main env.PropertySourcesPropertyResolver:115 - Found key 'app.name' in PropertySource 'class path resource [app.properties]' with value of type String
16:17:17,953 INFO main service.TestAppService:23 - AppService{appId='icypt', appName='冰点科技'}
总结一下:
以上这个测试我先定义了一个配置文件,在这个配置文件中定义了两对属性,当AppService实例化时通过Environment实例获取配置文件属性并通过构造注入到AppService实例之中完成实例化,最后打印了APPService的toString方法,由日志可以看出,完全符合我们想要的结果,至于Environment的其他方法就不一一演示了,大家化几分钟时间去测试一下,比较简单。
二、属性占位符的使用
Spring支持将属性值定义到外部的配置文件中,并使用占位符的方式将其插入到SpringBean中,所谓的占位符就是“${...}”。
1、JavaConfig中使用:
修改JavaConfig.java
@Bean
public AppService appService( String appId, String appName) {
return new AppService(appId, appName);
}
通过@Value,我们就可以获取到app.properties中对应的属性值。
2、XMLConfig中使用:
创建UserService.java
public class UserService {
private String userName;
private String password;
private String appId;
private String appName;
public UserService(String userName, String password, String appId, String appName) {
this.userName = userName;
this.password = password;
this.appId = appId;
this.appName = appName;
}
public String toString() {
return "UserService{" +
"userName='" + userName + '\'' +
", password='" + password + '\'' +
", appId='" + appId + '\'' +
", appName='" + appName + '\'' +
'}';
}
}
创建配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--<context:property-placeholder/>-->
<context:component-scan base-package="com.icypt"/>
<bean id="userService" class="com.icypt.learn.service.UserService"
c:userName="dg"
c:appId="${app.id}"
c:appName="${app.name}"
c:password="123"/>
</beans>
创建测试类TestUserService.java:
public class TestUserService {
public static final Logger logger = LoggerFactory.getLogger(TestUserService.class);
public static ClassPathXmlApplicationContext cpxac = new ClassPathXmlApplicationContext("application.xml");
public void TestToString() {
UserService userService = cpxac.getBean("userService", UserService.class);
logger.info(userService.toString());
}
}
运行结果:
17:11:07,285 DEBUG main env.PropertySourcesPropertyResolver:115 - Found key 'app.name' in PropertySource 'class path resource [app.properties]' with value of type String
17:11:07,286 DEBUG main env.PropertySourcesPropertyResolver:115 - Found key 'app.name' in PropertySource 'environmentProperties' with value of type String
17:11:07,326 INFO main service.TestUserService:24 - UserService{userName='dg', password='123', appId='icypt', appName='冰点科技'}
在Xml中使用占位符时一定要注意:
要么在Xml中配置<context:property-placeholder/>,要么在JavaConfig中配置:
@Bean
public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
否则,Spring无法识别占位符,将以字符串的形式注入到属性当中,类似“${app.id}”。
三、Spring常用表达式的使用
Spring3引入了Spring表达式语言(Spring Expression Language, SpEL),它能够以一种强大和简洁的方式将值装配到Bean属性和构造器参数中,在这个过程中所使用的表达式会在运行时计算得到值。
下面将常见的表达式在XML中使用做一演示,来激发你写出更加风骚的表达式:
1、演示前的准备工作
创建UserTags.java
public class UserTag {
private String tagName;
private String title;
public String getTagName() {
return tagName;
}
public void setTagName(String tagName) {
this.tagName = tagName;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
修改UserService.java
private List<UserTag> tagList;
2、演示常用SpEL表达式
修改配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd">
<!--<context:property-placeholder/>-->
<context:component-scan base-package="com.icypt"/>
<bean id="userService" class="com.icypt.learn.service.UserService"
c:userName="dg"
c:appId="${app.id}"
c:appName="${app.name}"
c:password="123"
p:tagList-ref="lists"
/>
<!--标签集合-->
<util:list id="lists">
<bean class="com.icypt.learn.service.UserTag"
p:tagName="java" p:title="java develop"/>
<bean class="com.icypt.learn.service.UserTag"
p:tagName="java" p:title="java test"/>
<bean class="com.icypt.learn.service.UserTag"
p:tagName="spring" p:title="spring develop"/>
<bean class="com.icypt.learn.service.UserTag"
p:tagName="spring" p:title="spring test"/>
<bean class="com.icypt.learn.service.UserTag"
p:tagName="mybatis" p:title="mybatis develop"/>
</util:list>
<!-- 常见表达式使用 -->
<bean id="expressionService" class="com.icypt.learn.service.ExpressionService">
<!-- 当前时间毫秒数 -->
<property name="currentTimeMilis" value="#{ T(System).currentTimeMillis()}"/>
<!-- 获取对象属性 -->
<property name="appId" value="#{userService.getAppId()}"/>
<!-- 获取系统属性,Spring在容器加载的时候会把System.getProperties()获取的值默认放在systemProperties中 -->
<property name="javaVersion" value="#{systemProperties['java.version']}"/>
<!--转换科学计数法所得到的值-->
<property name="salary" value="#{9.2E3}"/>
<!--根据BeanID引入Bean-->
<property name="userService" value="#{userService}"/>
<!--注入对象为空判断-->
<property name="appName" value="#{appService.getAppName()?.substring(1)}"/>
<!--指定返回结果类型-->
<property name="randomNum" value="#{T(java.lang.Math).random()}"/>
<!--基本运算符的使用-->
<property name="baseOperator" value="#{(100 + 1) * 2}"/>
<!--三目运算符使用以及验证空-->
<property name="trinocular" value="#{userService.getPassword()?:'is blank'}"/>
<!--正则表达式使用-->
<property name="regex" value="#{userService.getUserName().matches('[a-z]')}"/>
<!--数组使用-->
<property name="arrayFirst" value="#{userService.getTagList()[0].getTagName()}"/>
<!--查询运算符,获得第一个值, 从tagList中找到第一个tagName等于java的tag,然后获取tagName-->
<property name="queryFirst" value="#{userService.getTagList().^[tagName eq 'java'].getTitle()}"/>
<!--查询运算符,获得最后一个值,从tagList中找到最后一个tagName等于java的tag,然后获取tagName-->
<property name="queryLast" value="#{userService.getTagList().$[tagName eq 'spring'].getTitle()}"/>
<!--投影运算符,从tagList中重组所有tagName的集合-->
<property name="projection" value="#{userService.getTagList().![tagName]}"/>
</bean>
</beans>
3、创建测试类
public class TestExpressionService {
public static final Logger logger = LoggerFactory.getLogger(TestExpressionService.class);
public static ClassPathXmlApplicationContext cpxac = new ClassPathXmlApplicationContext("application.xml");
public void TestToString() {
ExpressionService expressionService = cpxac.getBean("expressionService", ExpressionService.class);
logger.info("当前时间毫秒数:" + expressionService.getCurrentTimeMilis().toString());
logger.info("获取对象属性,appId:" + expressionService.getAppId());
logger.info("获取系统属性,java.version:" + expressionService.getJavaVersion());
logger.info("转换科学计数法所得到的值,salary:" + expressionService.getSalary());
logger.info("根据BeanID引入Bean:" + expressionService.getUserService().toString());
logger.info("注入对象为空判断,appName:" + expressionService.getAppName());
logger.info("指定返回结果类型,randomNum:" + expressionService.getRandomNum());
logger.info("基本运算符的使用,baseOperator:" + expressionService.getBaseOperator());
logger.info("三目运算符使用以及验证空,trinocular:" + expressionService.getTrinocular());
logger.info("正则表达式使用,regex:" + expressionService.getRegex());
logger.info("数组使用,arrayFirst:" + expressionService.getArrayFirst());
logger.info("查询运算符,获得第一个值,queryFirst:" + expressionService.getQueryFirst());
logger.info("查询运算符,获得最后一个值,queryLast:" + expressionService.getQueryLast());
logger.info("投影运算符,projection:" + expressionService.getProjection());
}
}
运行结果:
21:11:20,121 INFO main service.TestExpressionService:24 - 当前时间毫秒数:1556716280003
21:11:20,121 INFO main service.TestExpressionService:25 - 获取对象属性,appId:icypt
21:11:20,121 INFO main service.TestExpressionService:26 - 获取系统属性,java.version:1.8.0_131
21:11:20,123 INFO main service.TestExpressionService:27 - 转换科学计数法所得到的值,salary:9200.0
21:11:20,124 INFO main service.TestExpressionService:28 - 根据BeanID引入Bean,userServiceID:UserService{userName='dg', password='123', appId='icypt', appName='冰点科技'}
21:11:20,124 INFO main service.TestExpressionService:29 - 注入对象为空判断,appName:点科技
21:11:20,124 INFO main service.TestExpressionService:30 - 指定返回结果类型,randomNum:0
21:11:20,125 INFO main service.TestExpressionService:31 - 基本运算符的使用,baseOperator:202
21:11:20,125 INFO main service.TestExpressionService:32 - 三目运算符使用以及验证空,trinocular:123
21:11:20,125 INFO main service.TestExpressionService:33 - 正则表达式使用,regex:false
21:11:20,125 INFO main service.TestExpressionService:34 - 数组使用,arrayFirst:java
21:11:20,125 INFO main service.TestExpressionService:35 - 查询运算符,获得第一个值,queryFirst:java develop
21:11:20,126 INFO main service.TestExpressionService:36 - 查询运算符,获得最后一个值,queryLast:spring test
21:11:20,126 INFO main service.TestExpressionService:37 - 投影运算符,projection:[java, java, spring, spring, mybatis]
由运行结果可以得出已经达到了表达式所需要表达的预期效果,到此,Spring表达式就探讨完了,大家可以根据上面这些例子,脑洞大开,写出更加飘逸的表达式来测试。那么我们有关Spring的第一大特性(依赖注入(IOC)与控制反转(DI))基本都探讨完了,下次我们正式进入面向切面编程,也就是所谓的AOP。
关注我↓↓↓
您的关注是对我最大的鼓励!