编写 Spring 配置文件的13个最佳实践

3,246 阅读7分钟
原文链接: dev-cheats.com

Spring 是一个功能强大的Java应用框架,提供各种配置选项。它的核心特性是为简单的Java对象(PO​​JO)提供服务,称为 Bean。 Spring使用依赖注入(DI)来实现简化和增加可测试性。Spring beans及依赖,以及beans类所需的服务都在配置文件中进行了详细的说明,这个配置文件是典型的XML格式。但是它既冗长又不实用。对于需要定义大量Spring beans的大工程来说,我们难以阅读和管理它。

在这篇文章里,对于Spring XML的配置,我将向你展示13种比较好的实践。其中的一些实践不仅是好的实践,更是必要的实践。除此以外,还有其他因素,例如领域模型的设计,都能影响XML的配置,但是这篇文章重点研究XML配置的易读性和易管理性。

文章导航

1. 给每个配置文件加上描述

最好使用descriptive id和名称来代替在XML配置文件中的注释。此外,加上一个配置文件header也很有用处,它可以概述文件中所定义的bean。你可以选择将描述内容加入description标签中。例如:

<beans>
    <description>
        This configuration file will have all beans
        which may be used for controlling transactions.
    </description>
    ...
</beans>

使用description标签的一个好处是可以容易地利用工具从标签中选取出description(的内容)。

2. 使用统一的命名规范

这个方式对于Java编码也一样适用。在工程中使用清晰的、描述性的、协调的通俗名称对于开发者理解XML配置是十分有益的。

例如对于bean ID,你可以根据通俗的 Java 类名来命名它。对于例子中 OrderServiceDAO 的bean ID 命名为 orderServiceDAO。 对于大的工程,你可以在bean ID 前面加上包名作为前缀。

3. 引用schema不使用版本号

我在之前的帖子中也提到过这个功能。为了提高可维护性,我特意将其包括在内,因为它一直是重要且有益的。 没必要在配置文件中指定 schema 的版本号,你可以省略它,事实上你应该一直省略它。

Spring会自动选择项目依赖项(jar)中可用的最高版本。此外,随着项目的发展,Spring版本将会更新,我们不需要维护所有的XML配置文件来查看新特性。

<?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: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">

  <!-- Other bean definitions-->

</beans>

4. Setter注入优先于构造器注入

Spring提供3种类型的依赖注入:constructor injection, setter injection, 和 method injection。 我们一般只用前两种类型。

<!-- Constructor injection -->
<bean id="employeeDAO"   class="com.devcheats.dao.EmployeeDAO">
    <constructor-arg ref="datasource"/>
</bean>

<!-- Setter injection -->
<bean id="employeeDAO" class="com.devcheats.dao.EmployeeDAO">
    <property name="datasource"  ref="datasource">
</bean>

构造函数注入可以提供最简单的线程安全,即对象不可变。此外,它还保证了对象不会在没有完全初始化的情况下被传递给其他bean。

Setter 注入提供了非常理想的功能,即灵活性或可维护性。如果在bean中设置了多个属性,那么为构造函数创建一个长列表不是一个好主意。 此外,如果可能,某些属性可能是可选的。

更喜欢灵活性。为了使对象不可变或线程安全,请遵循其他编程规则。

5. 对于构造器参数匹配,类型名比序号好

当一个构造器含有一个以上的同种类型的参数,或者属性值的标签已经被占用时,Spring允许你使用从0计数的序号来解决这些会带来混淆的问题。例如:

<!-- Index based constructor injection -->
<bean id="employeeDAO" class="com.devcheats.EmployeeDAO">
    <constructor-arg index="0" value="rest"/>
    <constructor-arg index="1" value="8080"/>
</bean>

像下面这样,利用类型属性来编写会更好一些:

<!-- Type based constructor injection -->
<bean id="employeeDAO" class="com.devcheats.EmployeeDAO">
    <constructor-arg type="java.lang.String" value="rest"/>
    <constructor-arg type="int" value="8080"/>
</bean>

使用索引可以稍稍减少一些冗长,但是和使用类型属性相比,它还是有容易发生错误的倾向和难于阅读的缺点。 你应该只在构造器参数不明确的时候,才使用索引这一方法。

6. 使用简洁的XML格式

简洁形式避免了冗长,是因为它从子元素中将属性值和参考写到属性中。例如下面的例子:

<!-- Expanded version -->
<bean id="employeeDAO" class="com.devcheats.dao.EmployeeDAO">
    <property name="datasource">
        <ref bean="datasource"></ref>
        <value>datasource</value>
     </property>
</bean>

可以使用简洁形式将上述代码重写为:

<!-- Shorter/shortcut version -->
<bean id="employeeDAO" class="com.devcheats.dao.EmployeeDAO">
    <property name="datasource"  ref="datasource" value="datasource">
</bean>

简洁形式不但可以节约你的打字,而且可以使XML配置文件清晰。 它最引人注目的是当在一个配置文件中有大量定义的类时可以提高易读性。

7. 尽可能重用bean定义

Spring提供一种类似继承一样的机制来减少配置信息的复制并简化XML配置。定义一个子类可以从它父类那里继承配置信息,而父类实质上作为子类的一个模板。这就是大工程中所谓的重用。你所需要做的就是在父类bean中设置abstract=true,然后在子bean注明它自己的父类bean。

以数据源定义为例:

<bean id="abstractDataSource" class="org.apache.commons.dbcp.BasicDataSource"
    destroy-method="close"
    p:driverClassName="${jdbc.driverClassName}"
    p:username="${jdbc.username}"
    p:password="${jdbc.password}" />

<bean id="concreteDataSourceOne"
    parent="abstractDataSource"
    p:url="${jdbc.databaseurlOne}"/>

<bean id="concreteDataSourceTwo"
    parent="abstractDataSource"
    p:url="${jdbc.databaseurlTwo}"/>

8. 总是使用id作为bean标识

你可以指定一个id或名称来作为bean的标识符。虽然使用id不会提高易读性,但是它可以让XML parser对bean的引用有效方面进行更好的验证。如果由于XML IDREF的限制而不能使用某个id,你可以利用names来作为bean的标识符。XML IDREF的限制是id必须以字母开头(或者在XML规范中定义的标点符号),后面接着字母,数字,连字号,下划线,冒号等。实际上,遇到XML IDREF限制的问题是很少见的。

9. 避免自动装配

Spring可以通过类的自省来自动绑定其依赖部分,使得你不必明确指明bean的属性和构造器。Bean的属性可以通过属性名称或类型匹配来实现自动绑定。构造器通过类型匹配来实现自动绑定。你甚至可以指定自动检测自动绑定模式,它可以引导Spring选择一种适当的运行机制。先来看看下面的一个例子:

<bean id="employeeDAO"
    class="com.devcheats.dao.EmployeeDAO"
    autowire="byName"/>

EmployeeDAO 类的属性名在容器中用于匹配bean实例。 自动绑定可以潜在地节省一些打字和减少一些混乱。但是在现实世界的工程里你不应该使用这种方式,这是因为它牺牲了配置的清晰性和可维护性。 许多指南和介绍中大量吹捧自动绑定是Spring的一种极好的特征而没有提到这一特性所带来的牺牲。

依我的观点,这就像Spring中的object-pooling,它更像是一种为了占据更多市场的商业特征。 它对于XML配置文件的小巧化是一个好办法,但实际上也增加了复杂程度,尤其当你运行有大量类声明的工程时。 虽然Spring允许你混合自动绑定和手动绑定,但是这个矛盾会使XML配置更加晦涩难懂。

10. 总是使用classpath作为前缀

当导入资源、XML配置、属性等总是使用classpath作为前缀。这为资源的位置提供了一致性和清晰性。并非Spring的每个特性都具有相同的类路径:保证一致性。

classpath由构建工具和IDE决定。一般包括 src/main/javasrc/main/resourcessrc/test/javasrc/test/resources

<!-- Always use classpath: prefix-->
<import resource="classpath:/META-INF/spring/applicationContext-security.xml"/>

11. 引用外部属性文件

通常有多个与应用程序运行时相关的配置参数。它们被传递给bean配置上下文文件中的bean定义。 在配置文件中而不是硬编码写死。相反,将它们抽离到一些属性文件。

更好的将它们分组在单独的文件中,基于它们的用法或模块,即JDBC中的所有数据源相关配置在 jdbc.properties 文件中。

<bean id="abstractDataSource" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close"
        p:driverClassName="${jdbc.driverClassName}"
        p:username="${jdbc.username}"
        p:password="${jdbc.password}" />

和 properties 文件

/* file://jdbc.properties */

jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.username=root
jdbc.password=password

12. 在开发阶段使用依赖检验

你可以在bean中给依赖检验的属性设置值,而不采用原先默认的空值,属性设置例如simple,object或all,以便容器进行依赖检验。当bean的全部的属性(或某类属性)需要被明确设置或自动绑定时,依赖检验便显得很有用。

<bean id="abstractDataSource" class="org.apache.commons.dbcp.BasicDataSource"
    destroy-method="close"
    p:driverClassName="${jdbc.driverClassName}"
    p:username="${jdbc.username}"
    p:password="${jdbc.password}"
    dependency-check="all" />

在这个例子里,容器确保为 abstractDataSource bean 设置的属性不是 primitives 或者 collections。 为所有的bean设置默认依赖检测也是可以的,但是我们很少这样做,是因为有些bean的属性根本就不必设置。

13. 不要过度使用依赖注入

作为最后一点,Spring ApplicationContext可以替你创建Java对象,但是并不是所有的Java对象都通过依赖注入来创建的。例如,全局的对象不应该通过ApplicationContext来创建。Spring是一个很棒的框架,但是,就易读性和易管理性而言,当定义大量bean的时候,基于XML的配置问题就会突出。过度的依赖注入会使XML配置变得复杂而且臃肿。记住!使用强大的IDE时,例如Eclipse和IntelliJ,与XML文件相比,Java代码更加易读,易维护,易管理。

总结

对于Spring的配置,XML是很优秀的方式。但当定义大量bean时,基于XML配置会变得冗长,笨拙。Spring提供了丰富的配置选项。适当地利用其中的选项可以使XML配置清晰,但是,有些选项,例如autowiring(自动绑定),往往会降低易读性和易维护性。文章中所列举的实例,可以帮助你创建出清晰易读的XML配置文件。

祝你学习愉快!


原文出处:https://howtodoinjava.com/best-practices/13-best-practices-for-writing-spring-configuration-files

王爵nice 翻译