Spring教程12-强大飘逸的表达式语言

阅读原文

一、探讨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=icypt
app.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 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中,所谓的占位符就是“${…}”。

4、JavaConfig中使用:

修改JavaConfig.java

@Bean
public AppService appService(@Value("app.id")String appId, @Value("${app.name}") String appName) {
	return  new AppService(appId, appName);
}

通过@Value,我们就可以获取到app.properties中对应的属性值。

5、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 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>

6、创建测试类

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 - 当前时间毫秒数: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。

更多最新技术文章,请关注“冰点IT”公众号

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值