<util:constant id="X" static-field="java.lang.Integer.MAX_VALUE"/>
上面的代码声明了一个DI为X的bean,并设置它的值为Integer.MAX_VALUE。
The next element,util:list,声明一个实现了java.util.List的bean,更准确的说是java.util.List。你可以知道List的实现类并且向List中添加值,
Description: Declaration of the list Bean
<util:constant id="Y" static-field="java.lang.Integer.MAX_VALUE"/>
<util:list id="X" list-class="java.util.ArrayList">
<value>value1</value>
<ref bean="Y"/>
</util:list>
上面的代码声明了ID为X的,类型为java.util.ArrayList<Object>的bean。并设置他的值为 String(“value1″)和Integer.MAX_VALUE。我们通过下面的Java代码验证这个bean,
Descrpiton: Usage of the util:list Declaration
List y = (List)context.getBean("X");
for (Object o : y) {
System.out.println(o.getClass() + " " + o);
}
运行结果:
class java.lang.String value1
class java.lang.Integer 2147483647
接着我们看一下util:map元素。你可以指定一个实现java.util.Map接口的类的bean,并设置map的元素。如,
Description: Usage of the map Declaration
<util:constant id="Y" static-field="java.lang.Integer.MAX_VALUE"/>
<util:map id="Z" map-class="java.util.HashMap">
<entry key="x" value="y"/>
<entry key="y"><ref bean="X"/></entry>
</util:map>
这里声明了一个bean ID为Z并且实现java.lang.Map<Object,Object>接口的,类型为java.util.HashMap的bean。他的值为”x” => String(“y”)和”y” => Integer.MAX_VALUE。用类似上面的JAVA代码验证结果如下,
y => class java.lang.Integer 2147483647
x => class java.lang.String y"
下面是util schema中的util:properties元素,
Description: Declaration of the properties Element
<util:properties id="BeanId"
location="classpath:com/apress/prospring2/ch07/util/Main.properties"/>
被BeanId表示的bean声明为java.util.Properties bean并且从classpath中的com/apress/prospring2/ch07/util/Main.properties中读取值。就像你看到的一样,location属性可以是一个Spring resource,这意味着你可以为location属性设置任何经过Spring resource editor验证过的pattern。
下一个元素是util:property-path元素。它允许从一个存在的bean中提取属性值。考虑下面清单,我们有一个ID为simple的 SimpleBean类型的bean。我们将通过util:property-path使用simple.getName()返回的值创建一个ID为Q的 bean。
Description: Declaration of the simple and property-path Beans
// SimpleBean.java
public class SimpleBean {
private String name;
private String value;
public SimpleBean() {
this.name = "Name";
this.value = "Anirvan Chakraborty";
}
public String getName() {
return name;
}
public String getValue() {
return value;
}
}
Description: greeter-context-2.0.xml
<bean id="simple" class="com.apress.prospring2.namespaces.util.SimpleBean"/>
<util:property-path id="Q" path="simple.name"/>
这里ID为Q的util:property-path bean将被设置为“Name”。
最后,util:set元素声明实现了java.util.Set<Object>接口。
Description: Showing a util:set Configuration
<util:constant id="X" static-field="java.lang.Integer.MAX_VALUE"/>
<util:set id="S" set-class="java.util.HashSet">
<value>foo</value>
<ref bean="X"/>
</util:set>
set元素必须符合指定的Set实现类的规范(contract);也就是说如果我们改变set-class为 java.util.TreeSet,上面构造的bean将会失败,因为TreeSet不能包含不同类型的对象。
The tx Schema
Spring提供了广泛的事务支持,<tx>标签将用来配置事务bean。下面的代码片段引用了正确的schema并使你可以使用tx 命名空间中的标签。
Description: Code Snippet for Transactional Bean Declaration Using the tx Namespace
<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"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-2.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
<!—transactional bean declarations here -->
</beans>
后面有一章(第16章)会专门讨论Spring对事务的支持,同样会讨论<tx>标签的各种属性。
The aop Schema
<aop>标签配置和AOP有关的Spring bean,包括Spring自己的proxy基础的AOP框架和Spring整合的AspectJ AOP框架。这些标签在前面的章节有介绍。所以这里只简单的说一下,
Description: Correct Usage of the aop Schema
<?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"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<!-- <bean/> definitions here -->
</beans>
The jee Schema
Spring通过使用jee命名空间提供对Java Enterprise Edition(Java EE或JEE)的支持。他现在可以通过简单的方法配置通过JNDI查找对象。如,
Description: Configuration for Correct Usage of the jee Namespace
<?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:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee-2.5.xsd">
<!-- <bean/> definitions here -->
</beans>
为了比对新旧配置文件,下面我们来看一下在Spring 1.x中的JNDI对象查找配置,
Description: Spring 1.x-Specific Configuration of JNDI Object Lookup
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="jdbc/dataSource"/>
</bean>
<bean id="someDao" class="com.apress.chapter7.JdbcSomeDao">
<property name="dataSource" ref="dataSource"/>
</bean>
上面你可以看到江jndiObjectFactoryBean配置为简单的Spring bean,并将这个bean的引用传递给Spring数据访问对象bean。
下面让我们看一下使用Spring 2.5的jee命名空间进行更简单的配置。
Description: JNDI Object Lookup Using the <jee> Tag
<jee:jndi-lookup id="dataSource" jndi-name="jdbc/MyDataSource"/>
<bean id="userDao" class="com.foo.JdbcUserDao">
<property name="dataSource" ref="dataSource"/>
</bean>
The lang Schema
lang schema涉及关于在Spring容器中使用动态语言(如,JRuby或Groovy)暴露对象的领域。这里的标签(和动态语言)将在这本书的14章讨论。
Description: Correct Usage of the lang Schema
<?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:lang="http://www.springframework.org/schema/lang"
xsi:schemaLocation="
http://www.springframework.org/schema/beans ➥
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/lang ➥
http://www.springframework.org/schema/lang/spring-lang-2.5.xsd">
<!-- <bean/> definitions here -->
</beans>
Note 我们忽略了Spring 2.5的tool schema,他现在正在被重新审核。不过你可以通过spring-tool-2.5.xsd对他进行学习。
Behind the Schema Scenes
现在了解了如何使用标准的schema,下面讲学习更有趣的东西-Spring如何处理新的配置文件。下面让我们看一下当Spring遭遇一个 schema引用时发什么事情。
下图是一个UML sequence diagram。他显示了Spring如何读取配置文件和处理步骤。
Description: Loading Spring configuration
一个非常好的开始的地方是创建ApplicationContext的实现时,例如调用new ClasspathXmlApplicationContext()。如果我们使用默认设置,Spring将在 AbstractXmlApplicationContext中使用XmlBeanDefinitionReader类读取bean定义,在处理过程中实际使用DefaultBeanDefinitionDocumentReader.
DefaultBeanDefinitionDocumentReader创建BeanDefinitionParserDeletgate,它用来处理DefaultBeanfinitionDocumentReader遭遇到的XML事件。 BeanDefinitionParserDelegate使用DefaultNamespaceHandlerResolver获得与指定的 namespaceUri相符合的NamespaceHandlerResolver。
DefaultNamespaceHandlerResolver使用META-INF/spring.handles文件决定符合给定的 namespace URI的NamespaceHandler接口实现。像XmlBeanDefinitionReader读取XML配置文件一样,它为每一个遭遇的他的命名空间的元素调用NamespaceHandler实现。一旦元素被读取,BeanDefinitions调用BeanDefinition或者更新 BeanDefinitionHolder实例。BeanDefinition用来实例化被配置的bean。下面是默认的/META-INF /spring.handlers
Description: Default META-INF/spring.handlers File
http\://www.springframework.org/schema/util=
org.springframework.beans.factory.xml.UtilNamespaceHandler
http\://www.springframework.org/schema/aop=
org.springframework.aop.config.AopNamespaceHandler
http\://www.springframework.org/schema/lang=
org.springframework.scripting.config.LangNamespaceHandler
http\://www.springframework.org/schema/tx=
org.springframework.transaction.config.TxNamespaceHandler
http\://www.springframework.org/schema/jee=
org.springframework.ejb.config.JeeNamespaceHandler
http\://www.springframework.org/schema/p=
org.springframework.beans.factory.xml.SimplePropertyNamespaceHandler
这是一个简单的properties样式的文件,这里的key是命名空间URI,value是与命名空间对应的实现了 NamespaceHandler接口的类。实际上大部分实现都不是直接实现这个接口的,而是扩展自NamespaceHandlerSupport便捷类。
NamespaceHandlerSupport类允许你非常容易的实现NamespaceHandler并且允许你通过注册一个 BeanDefinitionParser去处理自定义schema的不同元素。一般会在init()方法中注册 BeanDefinitionParser(通常是一个静态嵌套类)。
Description: Typical Implementation of a Class that Extends NamespaceHandlerSupport
public class CustomNamespaceHandler extends NamespaceHandlerSupport {
private static class GreeterBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
protected Class getBeanClass(Element element) {
return GreeterFactoryBean.class;
}
protected void doParse(Element element,ParserContext parserContext, BeanDefinitionBuilder builder) {
// implementation of the doParse method.
}
public void init() {
registerBeanDefinitionParser("greeter", new GreeterBeanDefinitionParser());
}
}
}
Custom Schemas
现在你知道Spring如何处理配置文件的读取,你可以容易的创建自定义schema和合适的NamespaceHandler。下面书上显示的是这个例子的目录结构。懒得弄图了。
这个例子有九个文件:一个简单的Greeter接口和他的实现StdoutGreeter。一个Main类完成test,它使用 ClasspathXmlApplicationContext读取custom-context.xml文件。从命名空间处理这一点来看,最重要的文件是spring.handlers,spring.schemas,custom.xsd,GreeterFactoryBean和 CustomNamespaceHandler。让我们通过下面清单开始分析这个简单的例子,
Description: The custom.xsd Schema File
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsd:schema xmlns="http://prospring2.apress.com/namespaces/custom"
xmlns:xsd="http://www.w3.org/2001/XMLschema"
targetNamespace="http://com.apress.prospring2/ch07/custom"
elementFormDefault="qualified">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
<xsd:element name="greeter">
<xsd:annotation>
<xsd:documentation>...</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:attribute name="id" type="xsd:ID"/>
<xsd:attribute name="count" type="xsd:integer" use="required">
<xsd:annotation>
<xsd:documentation>...</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="message" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation>...</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
</xsd:schema>
这里我们定义了一个元素,greeter,他有三个必须的属性。id属性的类型是xsd:ID,count的类型是 xsd:integer,message的类型是xsd:string。通过严格的指定每个属性允许的类型,我们可以强迫任何使用我们的组件的开发者使用正确的数据类型。
注意schema loction是http://com.apress.prospring2/ch07/custom;这是一个URI,在程序运行是他可能无法访问。这就是为什么我们需要创建META-INF/spring.schemas文件,这个文件指定了URI相应的classpath位置,
Description: The spring.schemas File
http\://com.apress.prospring2/ch07/custom =\
/com/apress/prospring2/ch07/custom/custom.xsd
注意这个properties样式的文件中的key匹配schema的URI,value匹配custom.xsd文件在classpath中的位置。下面我们注册一个可以处理http://com.apress.prospring2/ch07/custom中的元素的 NamespaceHandler实现。我们用META-INF/spring.handlers文件做这件事情,
Description: The spring.handlers File
http\://com.apress.prospring2/ch07/custom =\
com.apress.prospring2.namespaces.custom.CustomNamespaceHandler
这个文件告诉Spring读取CustomNamespaceHandler并使用它处理custom schema中的元素。
在我们创建NamespaceHandler实现之前,我们必须创建GreeterFacotryBean。这是一个标准的Spring FactoryBean实现,用来根据配置创建合适的Greeter接口的实现的实例。
Description: Implementation of the GreeterFactoryBean
public class GreeterFactoryBean extends AbstractFactoryBean {
private String message;
private int count;
public void setMessage(String message) {
this.message = message;
}
public void setCount(int count) {
this.count = count;
}
protected Object createInstance() {
if (this.message == null) {
throw new IllegalArgumentException("'message' is required");
}
return new StdoutGreeter(this.count, this.message);
}
public Class getObjectType() {
return Greeter.class;
}
}
这个实现没有什么不好理解的地方:createInstance()方法使用message和count创建StdoutGreeter的实例,StdoutGreeter实现了Greeter接口。我们在CustomNamespaceHandler中使用这个bean factory。
Description: CustomNamespaceHandler Implementation
public class CustomNamespaceHandler extends NamespaceHandlerSupport {
private static class GreeterBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
protected Class getBeanClass(Element element) {
return GreeterFactoryBean.class;
}
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
builder.addPropertyValue("message", element.getAttribute("message"));
String countString = element.getAttribute("count");
try {
int count = Integer.parseInt(countString);
builder.addPropertyValue("count", count);
} catch (NumberFormatException ex) {
throw new RuntimeException(ex);
}
}
}
public void init() {
registerBeanDefinitionParser("greeter", new GreeterBeanDefinitionParser());
}
}
这个文件中有几个关键的地方。第一个地方是扩展子帮助类NamespaceHandlerSupport。所以我们只需要实现init()方法,在这里我们为所有的custom:greeter元素注册解析器;所有我们需要做的事情就是实现getBeanClass()方法和doParse()方法。Spring将创建getBeanClass()方法返回的类的实例并为所有在doParse()方法中调用 builder.addPropertyValue()添加的属性调用setter方法。在这个例子中,调用的方法是 GreeterFactoryBean.setMessage()和GreeterFactoryBean.setCount()。这同样是我们在 doParse()方法中完成的。我们知道这些属性的存在,因为schema验证进程决定他们必须存在。我们同样知道count的值是整形,但是我们只能从String中进行解析,因为element.getAttribute()方法返回String。因此,catch块永远不会被执行。
最后的问题是custom-context.xml和他的测试程序,
Description: The custom-context.xml File
<?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:custom="http://prospring2.apress.com/namespaces/custom"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://prospring2.apress.com/namespaces/custom
http://prospring2.apress.com/namespaces/custom.xsd">
<custom:greeter id="greeter" count="10" message="goo"/>
</beans>
这里我们有一个非常整洁的属性定义。
Description: Code Fragment Showing Usage of the Custom Namespace Handler
// code fragment of Main.java
ApplicationContext context =
new ClassPathXmlApplicationContext("classpath*:**/custom-context.xml");
Greeter greeter = (Greeter)context.getBean("greeter");
greeter.greet();
IDE Configuration
略…
Summary
In this chapter, you have seen how Spring 2.5 handles the new XML context files. Like previous releases of the Spring Framework, Spring 2.5 is fully backward compatible, so all your existing Spring 1.x document-type-definition–based (DTD-based) configuration will still work. But even though you can still use Spring 1.x DTD context files, you will get much more flexibility from using the new schema-based context files. In fact, some Spring 2.5 features are quite verbose to configure using the standard DTD-based approach. Good examples of this are the configuration of AspectJ beans and dynamic languages (for more information about AspectJ and Spring, see Chapter 6; for dynamic languages, see Chapter 14).
As well as understanding the standard namespaces provided by Spring, you now know how to create your own custom namespace. This is particularly helpful if you are developing a component of an application that many developers of your organization are going to use. If you write a custom namespace for its configuration, you can ensure that the beans will not be misconfigured.