Spring框架学习之高级依赖关系配置(二)

     紧接着上篇内容,本篇文章将主要介绍XML Schema的简化配置和使用SpEL表达式语言来优化我们的配置文件。

一、基于XML Schema的简化配置方式 从Spring2.0以来,Spring支持使用XML Schema来简化配置。在以前的bean元素配置下,所有的属性注入都需要一个property元素,集合属性就需要更多的这样的元素,一旦项目庞大,整个配置文件将无法维护。XML Schema提供的命名空间,可以帮助我们的配置文件缩减容量。主要有三种命名空间:

  • p:命名空间简化属性配置
  • c:命名空间简化构造配置
  • util:命名空间简化集合配置

1、使用p:命名空间简化配置 首先看一个我们常见的bean的配置:

<bean id="person" class="MyPackage.Person">
    <property name="name" value="single"/>
    <property name="age" value="22"/>
</bean>

这是一个再普通不过的bean的配置,它等效于:

<bean id="person" class="MyPackage.Person" p:name="single" p:age="22" />

但是这么做的前提是,在Spring配置文件的XML头部导入p的命名空间:

xmlns:p="http://www.springframework.org/schema/p"

无论你是使用IDE自动导入的,还是使用工具,或者自己手动导入的,想要使用p Schema,就必须该命名空间。p是property的缩写,除了可以注入普通的值类型之外,还可以注入引用类型,例如:

<bean id="person" class="MyPackage.Person" p:name-ref="stuName" p:age="22" />

2、使用c:命名空间简化配置 我们使用p命名空间简化了设置注入操作,c命名空间则是constructor的缩写,它用于简化构造注入的操作。例如:

<bean id="person" class="MyPackage.Person" c:name="single" c:age="22" />

Spring最后会调用构造注入,将name和age作为参数出入bean类的构造器中,构造实例返回。除此之外,还可以这么写:

<bean id="person" class="MyPackage.Person" c:_0="single" c:_1="22" />

下划线加上数字表示,这是构造器中的第几个参数,到时候会被Spring按序传入构造器中。

3、使用util命名空间简化配置 util给我们提供了以下几个元素:

  • constant:对于在配置文件中获取指定类的静态Field的值的一个简化配置
  • property-path:对于在配置文件中获取调用getter方法的一个简化配置
  • list:简化list作为bean的配置
  • map:简化map作为bean的配置
  • set:简化set作为bean的配置
  • properties:加载一份properties属性文件

^1、constant 首先看一段我们之前用于获取一个类的静态属性的配置:

<bean id="person" class="MyPackage.Person">
    <property name="name" value="single"/>
    <property name="age">
        <bean class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean">
            <property name="targetClass" value="MyPackage.BeanStaticClass"/>
            <property name="targetField" value="age"/>
        </bean>
    </property>
</bean>

这是一段我们之前用于在配置文件中获取类静态属性的代码,总体上来说还是挺繁琐的,下面我们使用util下的constant来简化这段配置。

<bean id="person" class="MyPackage.Person">
    <property name="name" value="single"/>
    <property name="age">
        <util:constant static-field="MyPackage.BeanStaticClass.age"/>
    </property>
</bean>

static-field属性的值指定了需要访问那个类下的那个静态属性。如果需要将该值单独定义在容器中,可以为其增加id属性以便其他bean实例获取。

^2、property-path 对于在配置文件中直接调用其他bean的getter方法,我们一般如是配置:

<bean id="student" class="MyPackage.Student">
    <property name="age" value="22"/>
</bean>

<bean id="person" class="MyPackage.Person">
    <property name="name" value="single"/>
    <property name="age">
        <bean class="org.springframework.beans.factory.config.PropertyPathFactoryBean">
            <property name="targetBeanName" value="student"/>
            <property name="propertyPath" value="age"/>
        </bean>
    </property>
</bean>

一般我们需要这么写来配置,下面我们通过util的property-path来简化配置:

<bean id="student" class="MyPackage.Student">
    <property name="age" value="22"/>
</bean>

<bean id="person" class="MyPackage.Person">
    <property name="name" value="single"/>
    <property name="age">
        <util:property-path path="student.age"/>
    </property>
</bean>

property-path的值指定了需要调用那个bean实例的那个getter方法。如果需要单独将该值配置在容器中,可以为其指定id。

^3、list 通过util,Spring允许我们单独定义一个list bean在容器中。一个list bean给我们提供如下几个属性:

  • id:容器中的唯一标识
  • list-class:指定实现该list的实现类,默认为ArrayList
  • scope:指定该list的作用域

下面我们定义一个list bean:

<util:list id="list" list-class="java.util.ArrayList">
    <value>nanjing</value>
    <value>nantong</value>
    <value>yancheng</value>
</util:list>

^4、map和set 有关map和set的util使用,基本和list的使用情况类似,此处只给出创建bean的配置代码,其他的不再赘述。

//map
<util:map id="map" map-class="java.util.HashMap">
    <entry key="中国银行" value="241241212" />
   <entry key="建设银行" value="543534545"/>
</util:map>
//set
<util:set id="set" set-class="java.util.HashSet">
    <value>single</value>
    <value>cyy</value>
</util:set>

至此,有关XML Schema的基本内容已如上述所述,Spring中还有一些其他相关的Schema文件,它们各自有各自的作用,有用于简化AOP配置的,有用于简化事务配置的等等,我们将在后续文章中进行学习。

二、使用Spring的EL表达式语言 Spring的配置文件有一个非常明显的缺陷,大量的静态注入,动态性不强。而SpEL类似于jsp的EL,采用表达式的方式为属性注入值,当程序动态运行时,这些表达式的值才确定。SpEL可以单独使用,也可以在Spring配置文件中使用,我们此处主要介绍在配置文件中的SpEL的使用。

1、创建数组 有时我们的实例中有类型为数组的属性,那么我们就可以通过SpEL定义数组作为参数注入依赖给该属性。

<bean id="person" class="MyPackage.Person">
    <property name="array" value="#{new int[]{12,2,5,7,8,6,8}}" />
</bean>

在Spring配置中使用SpEL中,基本的使用格式如下:

#{expression}

2、创建list集合 我们也可以使用SpEL定义list集合,例如:

<bean id="person" class="MyPackage.Person">
    <property name="list" value="#{{'single','cyy','xijingping'}}"/>
</bean>

list集合的定义使用 "{....}",大括号中的每个元素都对应于list中的一个元素。

我们可以通过以下语法格式访问list中的元素:

listName[index]

访问容器中map集合中的元素:

mapName[key]

例如:

<bean id="person" class="MyPackage.Person">
    <property name="name" value="#{list[0]}"/>
</bean>

这段配置会向person实例中的name属性注入容器中已经定义好的名为list的第一个元素的值。

3、调用方法 在SpEL中调用任意方法将会比我们之前介绍的那种纯配置形式简单很多,例如:

<bean id="person" class="MyPackage.Person">
    <property name="name" value="#{'hello'.concat(' world!')}"/>
</bean>

我们这里调用了jdk中的方法为person实例的name属性注入依赖值,当然也可以是我们自己定义的方法,可以是任意的方法。

4、类运算符 SpEL提供了一种运算符:T()。该运算符告诉Spring将括号中的内容作为一类类型而不是作为字符串进行解析。例如:

<bean id="person" class="MyPackage.Person">
    <property name="name" value="#{java.lang.Math.random()}"/>
</bean>

这样书写,Java会给我们报错,说java.lang.Math.random()并不是一个字符串,但是如果给他加上单引号,那么该函数就会以纯字符串作为参数注入给name属性,并不会被识别为正常函数。

正确书写格式如下:

<bean id="person" class="MyPackage.Person">
    <property name="name" value="#{T(java.lang.Math).random()}"/>
</bean>

5、集合选择 使用SpEL集合选择的基本格式如下:

collection.?[condition_expr]

只有符合条件condition_expr的集合元素才会被选择出来,例如:

<bean id="person" class="MyPackage.Person">
    <property name="list" value="#{list.?[length()>7]}"/>
</bean>

Spring会遍历整个list,每个元素都会调用他的length方法判断是否大于7,如果不是将舍弃该元素,否则该元素才会被帅选出来。

6、集合投影 集合投影可以让集合中的每个元素都去执行同一个方法,每个元素调用同一个方法,不同元素的该方法的返回值将构成最终的新集合。例如:

<bean id="person" class="MyPackage.Person">
    <property name="list" value="#{list.![length()]}"/>
</bean>

最终新集合中的元素是原集合中每个元素的长度。

SpEL的用法远非如此,这里只是列举了常用的几种用法。

两篇文章加强了对bean配置的理解,有总结不到之处,望指出!

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Aloys的开发之路

C/C++中的abort、atexit、exit和_Exit

     这几个函数都在头文件#include <stdlib.h>中声明。exit、_Exit与abort函数使程序终止,控制并不返回到这些函数的调用者。 e...

2419
来自专栏desperate633

Java动态代理与静态代理静态代理动态代理

我们先看一个简单的例子,当我们需要程序中加入方法执行的日志信息的时候,很显然我们最容易想到的实现方法,就是在方法前后插入日志记录信息。

1592
来自专栏逸鹏说道

js处理异常try{}catch(e){}

程序开发中,编程人员经常要面对的是如何编写代码来响应错误事件的发生,即例外处理(exception handlers)。如果例外处理代码设计得周全,那么最终呈现...

3595
来自专栏知无涯

XML创建或改变某个新属性

2848
来自专栏Java学习网

Java内存模型深度解读

Java内存模型深度解读 Java内存模型规范了Java虚拟机与计算机内存是如何协同工作的。Java虚拟机是一个完整的计算机的一个模型,因此这个模型自然也包含一...

2627
来自专栏漫漫全栈路

ASP.NET MVC 行为详解

前面分别介绍了MVC中的三个重要部分,而行为,则是其中C-Controller中的重要内容,下面详解一二。 一般继承自Controller类,类Controll...

2824
来自专栏java一日一条

Java 并发开发:内置锁 Synchronized

在多线程编程中,线程安全问题是一个最为关键的问题,其核心概念就在于正确性,即当多个线程访问某一共享、可变数据时,始终都不会导致数据破坏以及其他不该出现的结果。而...

782
来自专栏C/C++基础

段错误之memset对类对象的误用

使用new定义一个DICCUOriginalTask的对象指针之后,使用memset将对象实体置为0之后,在使用delete析构该对象,就会出现莫名其妙的段错误...

1121
来自专栏有趣的django

12.Flask-Restful定义Restful的视图 参数认证标准化返回参数

 如果使用Flask-restful,那么定义视图函数的时候,就要继承flask_restful.Resourse类,然后再根据当前请求的method来定义相应...

2342
来自专栏coderhuo

可怕的extern关键字一、不利之处二、例子三、分析四、正确做法

如果函数原型改变的话,每个extern声明的地方都要改一遍。 如果有地方没改到呢? 我们通过一个例子来看下悲剧是怎么发生的。

982

扫码关注云+社区

领取腾讯云代金券