XML Schema 概览
本附录列出了与核心容器相关的 XML Schema(架构定义)。
util 命名空间架构
顾名思义,util 标签用于处理常见的实用型配置问题,例如配置集合、引用常量等。 要使用 util 架构中的标签,你需要在 Spring XML 配置文件的顶部添加以下前导声明(代码片段中的文本引用了正确的架构,以便你可以使用 util 命名空间中的标签):
<?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:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd">
<!-- 在此处定义 Bean -->
</beans>使用 <util:constant/>
考虑以下 Bean 定义:
<bean id="..." class="...">
<property name="isolation">
<bean id="java.sql.Connection.TRANSACTION_SERIALIZABLE"
class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean" />
</property>
</bean>上述配置使用 Spring 的 FactoryBean 实现(FieldRetrievingFactoryBean)将 Bean 的 isolation 属性值设置为 java.sql.Connection.TRANSACTION_SERIALIZABLE 常量的值。这虽然可行,但非常冗长,并且(不必要地)向最终用户暴露了 Spring 的内部工作机制。
以下基于 XML Schema 的版本更加简洁,清晰地表达了开发者的意图(“注入此常量值”),并且具有更好的可读性:
<bean id="..." class="...">
<property name="isolation">
<util:constant static-field="java.sql.Connection.TRANSACTION_SERIALIZABLE"/>
</property>
</bean>通过字段值设置 Bean 属性或构造函数参数
FieldRetrievingFactoryBean 是一个 FactoryBean,用于检索 static 或非静态字段值。它通常用于检索 public static final 常量,然后将其用于设置另一个 Bean 的属性值或构造函数参数。
以下示例显示了如何通过使用 staticField 属性公开静态字段:
<bean id="myField"
class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean">
<property name="staticField" value="java.sql.Connection.TRANSACTION_SERIALIZABLE"/>
</bean>还有一种便捷用法,即直接将静态字段指定为 Bean 名称,如下例所示:
<bean id="java.sql.Connection.TRANSACTION_SERIALIZABLE"
class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean"/>这确实意味着你无法再自选 Bean 的 id(因此任何引用它的其他 Bean 也必须使用这个较长的名称),但这种形式对于定义来说非常简洁。作为内部 Bean 使用时也非常方便,因为无需为 Bean 引用指定 id,如下例所示:
<bean id="..." class="...">
<property name="isolation">
<bean id="java.sql.Connection.TRANSACTION_SERIALIZABLE"
class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean" />
</property>
</bean>你也可以访问另一个 Bean 的非静态(实例)字段,如 FieldRetrievingFactoryBean 类的 API 文档中所述。
在 Spring 中注入枚举值作为属性或构造函数参数非常容易。你实际上不需要做任何事情,也不需要了解任何关于 Spring 内部的信息(甚至不需要了解像 FieldRetrievingFactoryBean 这样的类)。
以下示例枚举展示了注入枚举值是多么简单:
package jakarta.persistence;
public enum PersistenceContextType {
TRANSACTION,
EXTENDED
}package jakarta.persistence
enum class PersistenceContextType {
TRANSACTION,
EXTENDED
}现在考虑以下类型为 PersistenceContextType 的 setter 方法和对应的 Bean 定义:
package example;
public class Client {
private PersistenceContextType persistenceContextType;
public void setPersistenceContextType(PersistenceContextType type) {
this.persistenceContextType = type;
}
}package example
class Client {
lateinit var persistenceContextType: PersistenceContextType
}<bean class="example.Client">
<property name="persistenceContextType" value="TRANSACTION"/>
</bean>使用 <util:property-path/>
考虑以下示例:
<!-- 被名称引用的目标 Bean -->
<bean id="testBean" class="org.springframework.beans.TestBean" scope="prototype">
<property name="age" value="10"/>
<property name="spouse">
<bean class="org.springframework.beans.TestBean">
<property name="age" value="11"/>
</bean>
</property>
</bean>
<!-- 结果为 10,即 bean 'testBean' 的属性 'age' 的值 -->
<bean id="testBean.age" class="org.springframework.beans.factory.config.PropertyPathFactoryBean"/>上述配置使用 Spring 的 FactoryBean 实现(PropertyPathFactoryBean)创建了一个名为 testBean.age 的 Bean(类型为 int),其值等于 testBean Bean 的 age 属性。
现在考虑以下示例,它增加了一个 <util:property-path/> 元素:
<!-- 被名称引用的目标 Bean -->
<bean id="testBean" class="org.springframework.beans.TestBean" scope="prototype">
<property name="age" value="10"/>
<property name="spouse">
<bean class="org.springframework.beans.TestBean">
<property name="age" value="11"/>
</bean>
</property>
</bean>
<!-- 结果为 10,即 bean 'testBean' 的属性 'age' 的值 -->
<util:property-path id="name" path="testBean.age"/><property-path/> 元素的 path 属性值遵循 beanName.beanProperty 的形式。在这种情况下,它获取了名为 testBean 的 Bean 的 age 属性。该 age 属性的值为 10。
使用 <util:property-path/> 设置 Bean 属性或构造函数参数
PropertyPathFactoryBean 是一个 FactoryBean,用于评估给定目标对象上的属性路径。目标对象可以直接指定,也可以通过 Bean 名称指定。然后你可以在另一个 Bean 定义中将此值用作属性值或构造函数参数。
以下示例显示了如何通过名称对另一个 Bean 使用路径:
<!-- 被名称引用的目标 Bean -->
<bean id="person" class="org.springframework.beans.TestBean" scope="prototype">
<property name="age" value="10"/>
<property name="spouse">
<bean class="org.springframework.beans.TestBean">
<property name="age" value="11"/>
</bean>
</property>
</bean>
<!-- 结果为 11,即 bean 'person' 的属性 'spouse.age' 的值 -->
<bean id="theAge"
class="org.springframework.beans.factory.config.PropertyPathFactoryBean">
<property name="targetBeanName" value="person"/>
<property name="propertyPath" value="spouse.age"/>
</bean>在以下示例中,针对一个内部 Bean 评估路径:
<!-- 结果为 12,即内部 Bean 的 'age' 属性值 -->
<bean id="theAge"
class="org.springframework.beans.factory.config.PropertyPathFactoryBean">
<property name="targetObject">
<bean class="org.springframework.beans.TestBean">
<property name="age" value="12"/>
</bean>
</property>
<property name="propertyPath" value="age"/>
</bean>还有一种快捷形式,其中 Bean 名称就是属性路径。下例展示了这种快捷形式:
<!-- 结果为 10,即 bean 'person' 的属性 'age' 的值 -->
<bean id="person.age"
class="org.springframework.beans.factory.config.PropertyPathFactoryBean"/>这种形式确实意味着无法自选 Bean 的名称。任何对它的引用也必须使用相同的 id(即该路径)。如果将其用作内部 Bean,则完全不需要引用它,如下例所示:
<bean id="..." class="...">
<property name="age">
<bean id="person.age"
class="org.springframework.beans.factory.config.PropertyPathFactoryBean"/>
</property>
</bean>你可以在定义中显式设置结果类型。这对于大多数用例来说不是必须的,但有时很有用。有关此特性的更多信息,请参阅 javadoc。
使用 <util:properties/>
考虑以下示例:
<!-- 创建一个 java.util.Properties 实例,其值从提供的路径加载 -->
<bean id="jdbcConfiguration" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="location" value="classpath:com/foo/jdbc-production.properties"/>
</bean>上述配置使用 Spring 的 FactoryBean 实现(PropertiesFactoryBean)来实例化一个 java.util.Properties 实例,其值从提供的 Resource 位置加载。
以下示例使用 util:properties 元素实现了更简洁的表示:
<!-- 创建一个 java.util.Properties 实例,其值从提供的路径加载 -->
<util:properties id="jdbcConfiguration" location="classpath:com/foo/jdbc-production.properties"/>使用 <util:list/>
考虑以下示例:
<!-- 创建一个 java.util.List 实例,其值从提供的 'sourceList' 加载 -->
<bean id="emails" class="org.springframework.beans.factory.config.ListFactoryBean">
<property name="sourceList">
<list>
<value>pechorin@hero.org</value>
<value>raskolnikov@slums.org</value>
<value>stavrogin@gov.org</value>
<value>porfiry@gov.org</value>
</list>
</property>
</bean>上述配置使用 Spring 的 FactoryBean 实现(ListFactoryBean)来创建一个 java.util.List 实例,并使用提供的 sourceList 中的值对其进行初始化。
以下示例使用 <util:list/> 元素实现了更简洁的表示:
<!-- 创建一个包含所提供值的 java.util.List 实例 -->
<util:list id="emails">
<value>pechorin@hero.org</value>
<value>raskolnikov@slums.org</value>
<value>stavrogin@gov.org</value>
<value>porfiry@gov.org</value>
</util:list>你也可以通过在 <util:list/> 元素上使用 list-class 属性,显式控制实例化的 List 的具体类型。例如,如果我们确实需要实例化一个 java.util.LinkedList,可以使用以下配置:
<util:list id="emails" list-class="java.util.LinkedList">
<value>jackshaftoe@vagabond.org</value>
<value>eliza@thinkingmanscrumpet.org</value>
<value>vanhoek@pirate.org</value>
<value>d'Arcachon@nemesis.org</value>
</util:list>如果没有提供 list-class 属性,容器将自行选择一个 List 实现。
使用 <util:map/>
考虑以下示例:
<!-- 创建一个 java.util.Map 实例,其值从提供的 'sourceMap' 加载 -->
<bean id="emails" class="org.springframework.beans.factory.config.MapFactoryBean">
<property name="sourceMap">
<map>
<entry key="pechorin" value="pechorin@hero.org"/>
<entry key="raskolnikov" value="raskolnikov@slums.org"/>
<entry key="stavrogin" value="stavrogin@gov.org"/>
<entry key="porfiry" value="porfiry@gov.org"/>
</map>
</property>
</bean>上述配置使用 Spring 的 FactoryBean 实现(MapFactoryBean)来创建一个 java.util.Map 实例,并使用提供的 'sourceMap' 中的键值对对其进行初始化。
以下示例使用 <util:map/> 元素实现了更简洁的表示:
<!-- 创建一个包含所提供键值对的 java.util.Map 实例 -->
<util:map id="emails">
<entry key="pechorin" value="pechorin@hero.org"/>
<entry key="raskolnikov" value="raskolnikov@slums.org"/>
<entry key="stavrogin" value="stavrogin@gov.org"/>
<entry key="porfiry" value="porfiry@gov.org"/>
</util:map>你也可以通过在 <util:map/> 元素上使用 'map-class' 属性,显式控制实例化的 Map 的具体类型。例如,如果我们确实需要实例化一个 java.util.TreeMap,可以使用以下配置:
<util:map id="emails" map-class="java.util.TreeMap">
<entry key="pechorin" value="pechorin@hero.org"/>
<entry key="raskolnikov" value="raskolnikov@slums.org"/>
<entry key="stavrogin" value="stavrogin@gov.org"/>
<entry key="porfiry" value="porfiry@gov.org"/>
</util:map>如果没有提供 'map-class' 属性,容器将自行选择一个 Map 实现。
使用 <util:set/>
考虑以下示例:
<!-- 创建一个 java.util.Set 实例,其值从提供的 'sourceSet' 加载 -->
<bean id="emails" class="org.springframework.beans.factory.config.SetFactoryBean">
<property name="sourceSet">
<set>
<value>pechorin@hero.org</value>
<value>raskolnikov@slums.org</value>
<value>stavrogin@gov.org</value>
<value>porfiry@gov.org</value>
</set>
</property>
</bean>上述配置使用 Spring 的 FactoryBean 实现(SetFactoryBean)来创建一个 java.util.Set 实例,并使用提供的 sourceSet 中的值对其进行初始化。
以下示例使用 <util:set/> 元素实现了更简洁的表示:
<!-- 创建一个包含所提供值的 java.util.Set 实例 -->
<util:set id="emails">
<value>pechorin@hero.org</value>
<value>raskolnikov@slums.org</value>
<value>stavrogin@gov.org</value>
<value>porfiry@gov.org</value>
</util:list>你也可以通过在 <util:set/> 元素上使用 set-class 属性,显式控制实例化的 Set 的具体类型。例如,如果我们确实需要实例化一个 java.util.TreeSet,可以使用以下配置:
<util:set id="emails" set-class="java.util.TreeSet">
<value>pechorin@hero.org</value>
<value>raskolnikov@slums.org</value>
<value>stavrogin@gov.org</value>
<value>porfiry@gov.org</value>
</util:set>如果没有提供 set-class 属性,容器将自行选择一个 Set 实现。
aop 命名空间架构
aop 标签用于配置 Spring 中的所有 AOP 相关事项,包括 Spring 自身的基于代理的 AOP 框架以及 Spring 与 AspectJ AOP 框架的集成。这些标签在标题为 使用 Spring 进行面向切面编程 的章节中已有详尽介绍。
为了完整起见,要使用 aop 架构中的标签,你需要在 Spring XML 配置文件的顶部添加以下前导声明(片段中的文本引用了正确的架构,以便你可以使用 aop 命名空间中的标签):
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 在此处定义 Bean -->
</beans>context 命名空间架构
context 标签处理与底层机制相关的 ApplicationContext 配置——也就是说,通常不是对最终用户重要的 Bean,而是像 BeanFactoryPostProcessors 这样在 Spring 中承担大量“繁重”工作的 Bean。以下片段引用了正确的架构,以便你可以使用 context 命名空间中的元素:
<?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 https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 在此处定义 Bean -->
</beans>使用 <property-placeholder/>
该元素激活对 ${…} 占位符的替换功能,这些占位符将根据指定的属性文件进行解析(作为 Spring 资源位置)。此元素是一个便捷机制,为你设置了一个 PropertySourcesPlaceholderConfigurer。如果你需要对具体的 PropertySourcesPlaceholderConfigurer 设置进行更多控制,可以自行显式定义一个 Bean。
警告
对于给定的应用程序,通常只应定义一个此类元素,并包含其所需的属性。只要使用了不同的占位符语法(${…}),就可以配置多个属性占位符。
如果你需要模块化用于替换的属性源,不应创建多个属性占位符。相反,每个模块都应该向 Environment 贡献一个 PropertySource。或者,你可以创建自己的 PropertySourcesPlaceholderConfigurer Bean,用于收集所有要使用的属性。
使用 <annotation-config/>
该元素激活 Spring 基础设施以探测 Bean 类中的注解:
- Spring 的
@Configuration模型 @Autowired/@Inject、@Value和@Lookup- JSR-250 的
@Resource、@PostConstruct和@PreDestroy(如果可用) - JAX-WS 的
@WebServiceRef和 EJB 3 的@EJB(如果可用) - JPA 的
@PersistenceContext和@PersistenceUnit(如果可用) - Spring 的
@EventListener
或者,你可以选择为这些注解显式激活各个 BeanPostProcessors。
注意
该元素不会激活 Spring 的 @Transactional 注解处理;你可以为此目的使用 <tx:annotation-driven/> 元素。类似地,Spring 的 缓存注解 也需要显式启用。
使用 <component-scan/>
该元素在关于 基于注解的容器配置 的章节中已有详细说明。
使用 <load-time-weaver/>
该元素在关于 Spring 框架中使用 AspectJ 进行加载时织入 的章节中已有详细说明。
使用 <spring-configured/>
该元素在关于 使用 AspectJ 通过 Spring 对领域对象进行依赖注入 的章节中已有详细说明。
使用 <mbean-export/>
该元素在关于 配置基于注解的 MBean 导出 的章节中已有详细说明。
Beans 架构
最后但同样重要的是,我们有 beans 架构中的元素。自该框架诞生之初,这些元素就已存在于 Spring 中。这里没有展示 beans 架构中各种元素的示例,因为它们在 依赖项和配置细节(实际上是整个相关章节)中已有非常详尽的介绍。
请注意,你可以向 <bean/> XML 定义中添加零个或多个键值对。如何处理这些额外的元数据(如果有的话)完全取决于你自己的自定义逻辑(因此通常仅在按照标题为 XML Schema 授权 的附录所述编写自己的自定义元素时才有用)。
以下示例显示了在包含它的 <bean/> 上下文中的 <meta/> 元素(请注意,如果没有特定的逻辑来解释它,该元数据目前实际上是无用的)。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="foo" class="x.y.Foo">
<meta key="cacheName" value="foo"/> <!-- (1) -->
<property name="name" value="Rick"/>
</bean>
</beans>- 这是示例
meta元素。
对于前面的示例,你可以假设存在某种逻辑可以消费该 Bean 定义,并使用提供的元数据设置某些缓存基础设施。
补充教学
1. 从“繁琐”到“语义化”:命名空间的进化
在 Spring 2.0 之前,所有的配置本质上都是在操作 BeanDefinition。如果你想引用一个静态常量,你必须老老实实地定义一个 FieldRetrievingFactoryBean。
- 痛点:XML 充满了
org.springframework.beans.factory.config.*这样技术性的类名,干扰了业务逻辑的阅读。 - 进化:
util,aop,context等命名空间的引入,将“技术实现”包装成了“意图标签”。例如<util:constant/>直接告诉阅读者:“我在这里取一个常量”。
2. util:properties vs @Value
util:properties:通常用于将整个.properties文件加载为一个java.util.Properties对象 Bean,方便直接注入到需要集合对象的 API 中。${...}占位符:用于将具体的属性值解析到字符串或基本类型中。- 最佳实践:如果你只需要几个配置项,用
@Value;如果你需要把一组配置作为一个整体传递(例如数据库连接池的配置对象),用util:properties。
3. <context:annotation-config/> 的幕后
这个简单的标签实际上注册了多个重要组件:
AutowiredAnnotationBeanPostProcessor(处理@Autowired)CommonAnnotationBeanPostProcessor(处理@Resource,@PostConstruct)ConfigurationClassPostProcessor(处理@Configuration)
- 关键点:
context:component-scan元素的annotation-config属性默认值为true。因此如果你启用了包扫描,通常不需要再显式写annotation-config。
4. XML 元数据 (<meta/>) 的现实应用
虽然 <meta/> 在文档中看起来比较冷门,但在高度定制化的企业框架中非常有用。它可以用来在不修改代码的情况下,为 Bean 附加上诸如“安全级别”、“审计开关”或“监控频率”等非功能性属性,再由自定义的 BeanPostProcessor 读取并执行相应的逻辑。