vlambda博客
学习文章列表

Spring5 强大飘逸的表达式语言

一、探讨Environment的使用

1、Spring属性解析器标准接口介绍

public interface PropertyResolver { //检查是否包含该属性,包含返回true,否则返回false boolean containsProperty(String key); //根据属性标识获取该属性对应的值,非必须定义,没有指定返回类型 @Nullable String getProperty(String key); //根据属性标识获取该属性对应的值,非必须定义,指定了默认值,没有指定返回类型 String getProperty(String key, String defaultValue); //根据属性标识获取该属性对应的值,非必须定义,指定返回类型 @Nullable <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 @Deprecated boolean acceptsProfiles(String... profiles); boolean acceptsProfiles(Profiles profiles);}

3、Spring Environment的使用

创建配置文件app.properties

app.id=icyptapp.name=冰点科技

创建配置类JavaConfig.java

@Configuration@PropertySource(value = "classpath:app.properties", encoding = "UTF-8")public class JavaConfig { @Autowired private Environment environment; @Bean 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; } @Override 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); @Test 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 String16:17:17,917 DEBUG main env.PropertySourcesPropertyResolver:115 - Found key 'app.name' in PropertySource 'class path resource [app.properties]' with value of type String16:17:17,953 INFO main service.TestAppService:23 - AppService{appId='icypt', appName='冰点科技'}

总结一下:

以上这个测试我先定义了一个配置文件,在这个配置文件中定义了两对属性,当AppService实例化时通过Environment实例获取配置文件属性并通过构造注入到AppService实例之中完成实例化,最后打印了APPService的toString方法,由日志可以看出,完全符合我们想要的结果,至于Environment的其他方法就不一一演示了,大家化几分钟时间去测试一下,比较简单。

二、属性占位符的使用

Spring支持将属性值定义到外部的配置文件中,并使用占位符的方式将其插入到SpringBean中,所谓的占位符就是“${...}”。

1、JavaConfig中使用:

修改JavaConfig.java

@Beanpublic AppService appService(@Value("app.id")String appId, @Value("${app.name}") 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; } @Override 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"); @Test 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 String17:11:07,286 DEBUG main env.PropertySourcesPropertyResolver:115 - Found key 'app.name' in PropertySource 'environmentProperties' with value of type String17:11:07,326 INFO main service.TestUserService:24 - UserService{userName='dg', password='123', appId='icypt', appName='冰点科技'}

在Xml中使用占位符时一定要注意:

要么在Xml中配置<context:property-placeholder/>,要么在JavaConfig中配置:

@Beanpublic 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"); @Test 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 - 当前时间毫秒数:155671628000321:11:20,121 INFO main service.TestExpressionService:25 - 获取对象属性,appId:icypt21:11:20,121 INFO main service.TestExpressionService:26 - 获取系统属性,java.version:1.8.0_13121:11:20,123 INFO main service.TestExpressionService:27 - 转换科学计数法所得到的值,salary:9200.021: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:021:11:20,125 INFO main service.TestExpressionService:31 - 基本运算符的使用,baseOperator:20221:11:20,125 INFO main service.TestExpressionService:32 - 三目运算符使用以及验证空,trinocular:12321:11:20,125 INFO main service.TestExpressionService:33 - 正则表达式使用,regex:false21:11:20,125 INFO main service.TestExpressionService:34 - 数组使用,arrayFirst:java21:11:20,125 INFO main service.TestExpressionService:35 - 查询运算符,获得第一个值,queryFirst:java develop21:11:20,126 INFO main service.TestExpressionService:36 - 查询运算符,获得最后一个值,queryLast:spring test21:11:20,126 INFO main service.TestExpressionService:37 - 投影运算符,projection:[java, java, spring, spring, mybatis]

由运行结果可以得出已经达到了表达式所需要表达的预期效果,到此,Spring表达式就探讨完了,大家可以根据上面这些例子,脑洞大开,写出更加飘逸的表达式来测试。那么我们有关Spring的第一大特性(依赖注入(IOC)与控制反转(DI))基本都探讨完了,下次我们正式进入面向切面编程,也就是所谓的AOP。

关注我↓↓↓

您的关注是对我最大的鼓励!