XML-based configuration metadata(使用XML文件定义beans之间的依赖注入关系)
第一部分 编程思路概述
step1,在XML文件中定义各个bean之间的依赖关系。
<?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
http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean> <bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean> <!-- more bean definitions go here --> </beans>- 也可以在一个XML文件中引用其他的XML文件(其他用于定义beans之间的依赖关系的XML文件,也即IOC容器),如下面的代码:
-
<beans>
<import resource="services.xml"/><!-- 其他定义beans之间依赖关系的XML文件
并且 services.xml must be in the same directory or classpath location as the file doing the importing, while messageSource.xml and themeSource.xml must be in a resources location below the location of the importing file. -->
<import resource="resources/messageSource.xml"/>
<import resource="/resources/themeSource.xml"/> <bean id="bean1" class="..."/>
<bean id="bean2" class="..."/>
</beans>关于文件路径,有以下几点需要注意:
It is possible, but not recommended, to reference files in parent directories using a relative "../" path. Doing so creates a dependency on a file that is outside the current application. In particular, this reference is not recommended for "classpath:" URLs (for example, "classpath:../services.xml"), where the runtime resolution process chooses the "nearest" classpath root and then looks into its parent directory. Classpath configuration changes may lead to the choice of a different, incorrect directory.
You can always use fully qualified resource locations instead of relative paths: for example, "file:C:/config/services.xml" or "classpath:/config/services.xml". However, be aware that you are coupling your application’s configuration to specific absolute locations. It is generally preferable to keep an indirection for such absolute locations, for example, through "${…}" placeholders that are resolved against JVM system properties at runtime.
step2,上面step1中提及的定义了各个beans之间依赖关系的XML文件实际上就是一个IoC容器而已,下面要将该IoC容器实例化,只有将该IOC容器实例化之后,该容器中所定义的class才会被实例化成类对象(也称bean),才会根据bean之间的依赖关系将拥有依赖bean的对象组装起来
- 根据XML文件的存放位置的不同,有多种实例化IoC容器的方法
ApplicationContext context =
new ClassPathXmlApplicationContext(new String[] {"services.xml", "daos.xml"});
step3,使用IOC容器所管理的(实例化的、组装好的)bean
-
// create and configure beans
ApplicationContext context =
new ClassPathXmlApplicationContext(new String[] {"services.xml", "daos.xml"}); // retrieve configured instance
PetStoreService service = context.getBean("petStore", PetStoreService.class); // use configured instance
List<String> userList = service.getUsernameList();
-
第二部分 实际应用实例
2.1 实例一,ring framework的IOC容器管理service layer objects
- the service layer objects
(services.xml)
configuration 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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- services --> <bean id="petStore" class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl">
<property name="accountDao" ref="accountDao"/>
<property name="itemDao" ref="itemDao"/>
<!-- additional collaborators and configuration for this bean go here -->
</bean> <!-- more bean definitions for services go here --> </beans>the data access objects
daos.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="accountDao"
class="org.springframework.samples.jpetstore.dao.jpa.JpaAccountDao">
<!-- additional collaborators and configuration for this bean go here -->
</bean> <bean id="itemDao" class="org.springframework.samples.jpetstore.dao.jpa.JpaItemDao">
<!-- additional collaborators and configuration for this bean go here -->
</bean> <!-- more bean definitions for data access objects go here --> </beans>// create and configure beans,实例化IOC容器
ApplicationContext context =
new ClassPathXmlApplicationContext(new String[] {"services.xml", "daos.xml"}); // retrieve configured instance
PetStoreService service = context.getBean("petStore", PetStoreService.class); // use configured instance
List<String> userList = service.getUsernameList();
- the service layer objects
第三部分 附录
3.1 附录一,xml-based configuration下bean标签各个属性以及子标签详解
3.1.1xml文件的各个标签以及其子标签详解
3.1.2 xml 配置实例
-
3.1.2.1 <construtor-arg/>标签用法举例 (其实也是Constructor-based Dependency Injection)
- example1:
-
package x.y; public class Foo { public Foo(Bar bar, Baz baz) {
// ...
}<beans>
<bean id="foo" class="x.y.Foo">
<constructor-arg ref="bar"/>
<constructor-arg ref="baz"/>
</bean> <bean id="bar" class="x.y.Bar"/> <bean id="baz" class="x.y.Baz"/>
</beans> -
example2:
package examples; public class ExampleBean { // Number of years to calculate the Ultimate Answer
private int years; // The Answer to Life, the Universe, and Everything
private String ultimateAnswer; public ExampleBean(int years, String ultimateAnswer) {
this.years = years;
this.ultimateAnswer = ultimateAnswer;
} }<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg type="int" value="7500000"/>
<constructor-arg type="java.lang.String" value="42"/>
</bean> - example3,Use the
index
attribute to specify explicitly the index of constructor arguments. For example:package examples; public class ExampleBean { // Number of years to calculate the Ultimate Answer
private int years; // The Answer to Life, the Universe, and Everything
private String ultimateAnswer; public ExampleBean(int years, String ultimateAnswer) {
this.years = years;
this.ultimateAnswer = ultimateAnswer;
} }<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg index="0" value="7500000"/>
<constructor-arg index="1" value="42"/>
</bean> - example4,You can also use the constructor parameter name for value disambiguation【消除歧义】:package examples;
public class ExampleBean { // Number of years to calculate the Ultimate Answer
private int years; // The Answer to Life, the Universe, and Everything
private String ultimateAnswer;
public ExampleBean(int years, String ultimateAnswer) {
this.years = years;
this.ultimateAnswer = ultimateAnswer;
} }<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg name="years" value="7500000"/>
<constructor-arg name="ultimateAnswer" value="42"/>
</bean>
-
3.1.2.2 <property/>标签用法举例 (官网reference的7.4.2小节)
- example1
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<!-- results in a setDriverClassName(String) call -->
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
<property name="username" value="root"/>
<property name="password" value="masterkaoli"/>
</bean>上述代码可以进一步简化成如下形式,这两者是等效的:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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"> <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close"
p:driverClassName="com.mysql.jdbc.Driver"
p:url="jdbc:mysql://localhost:3306/mydb"
p:username="root"
p:password="masterkaoli"/> </beans><bean id="mappings"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <!-- typed as a java.util.Properties -->
<property name="properties">
<value>
jdbc.driver.className=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mydb
</value>
</property>
</bean>
- example1
-
-
3.1.2.3 <idref/>标签用法举例(官网reference的7.4.2小节)
-
example1
<bean id="theTargetBean" class="..."/> <bean id="theClientBean" class="...">
<property name="targetName">
<idref bean="theTargetBean" /><!--bean属性=该容器中另外一个bean的id属性的值-->
</property>
</bean>上面的代码和下面的代码等效:(通常建议使用上面的配置方法,不建议使用下面的配置方法)
<bean id="theTargetBean" class="..." /> <bean id="client" class="...">
<property name="targetName" value="theTargetBean" />
</bean>
-
-
3.1.2.4 <ref/>标签用法举例 (官网reference的7.4.2小节)
example1,使用ref标签的bean属性
-
example2,使用parent container中的某个bean作为依赖,就要使用到ref标签的parent属性
<!--parent container--> <!-- in the parent context -->
<bean id="accountService" class="com.foo.SimpleAccountService">
<!-- insert dependencies as required as here -->
</bean><!--child container
在child container中想要使用父容器中的某个bean--><!-- in the child (descendant) context -->
<bean id="accountService" <!-- bean name is the same as the parent bean -->
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target">
<ref parent="accountService"/> <!-- the parent bean 的id或者name属性的值-->
</property>
<!-- insert other configuration and dependencies as required here -->
</bean>
-
3.1.2.5 如果bean中某个属性书集合性质的怎么办(如某个属性是map/list/set/collection等等) (官网reference的7.4.2小节----Collections部分)
-
example1,
<bean id="moreComplexObject" class="example.ComplexObject">
<!-- results in a setAdminEmails(java.util.Properties) call -->
<property name="adminEmails">
<props>
<prop key="administrator">administrator@example.org</prop>
<prop key="support">support@example.org</prop>
<prop key="development">development@example.org</prop>
</props>
</property>
<!-- results in a setSomeList(java.util.List) call -->
<property name="someList">
<list>
<value>a list element followed by a reference</value>
<ref bean="myDataSource" />
</list>
</property>
<!-- results in a setSomeMap(java.util.Map) call -->
<property name="someMap">
<map>
<entry key="an entry" value="just some string"/>
<entry key ="a ref" value-ref="myDataSource"/>
</map>
</property>
<!-- results in a setSomeSet(java.util.Set) call -->
<property name="someSet">
<set>
<value>just some string</value>
<ref bean="myDataSource" />
</set>
</property>
</bean>The value of a map key or value, or a set value, can also again be any of the following elements:
bean | ref | idref | list | set | map | props | value | null
-
example2,
public class Foo { private Map<String, Float> accounts; public void setAccounts(Map<String, Float> accounts) {
this.accounts = accounts;
}
}<beans>
<bean id="foo" class="x.y.Foo">
<property name="accounts">
<map>
<entry key="one" value="9.99"/>
<entry key="two" value="2.75"/>
<entry key="six" value="3.99"/>
</map>
</property>
</bean>
</beans> - example2合并集合,子bean继承父bean,并且子bean中的集合合并父bean中的集合中所拥有的值
-
<beans>
<bean id="parent" abstract="true" class="example.ComplexObject">
<property name="adminEmails">
<props>
<prop key="administrator">administrator@example.com</prop>
<prop key="support">support@example.com</prop>
</props>
</property>
</bean>
<bean id="child" parent="parent">
<property name="adminEmails">
<!-- the merge is specified on the child collection definition -->
<props merge="true">
<prop key="sales">sales@example.com</prop>
<prop key="support">support@example.co.uk</prop>
</props>
</property>
</bean>
<beans>结果,child中的adminEmails属性(其实是一个map集合)中将会拥有以下元素:
administrator=administrator@example.com
sales=sales@example.com
support=support@example.co.uk注意:The
merge
attribute must be specified on the lower, inherited, child definition; 也即只能由子bean继承父bean中的集合,反之则不行,会抛出异常
-
example1,
-
3.1.2.6 值为null (官网reference的7.4.2小节----Null and empty string values部分)
- example1,
<bean class="ExampleBean">
<property name="email" value=""/>
</bean>The preceding example is equivalent to the following Java code:
exampleBean.setEmail("")
- example2,
<bean class="ExampleBean">
<property name="email">
<null/>
</property>
</bean>The above configuration is equivalent to the following Java code:
exampleBean.setEmail(null)
- example1,
-
3.1.2.7简化配置代码(p-namespace和c-namespace) (官网reference的7.4.2小节----XML shortcut with the p-namespace部分以及XML shortcut with the c-namespace部分)
-
关于p-namespace(简化配置代码) :用于简化<property>标签配置代码
- example2,
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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"> <bean name="john-classic" class="com.example.Person">
<property name="name" value="John Doe"/>
<property name="spouse" ref="jane"/>
</bean> <bean name="john-modern"
class="com.example.Person"
p:name="John Doe"
p:spouse-ref="jane"/> <!-- 这里-ref表示jane是一个bean--> <bean name="jane" class="com.example.Person">
<property name="name" value="Jane Doe"/>
</bean>
</beans> - example3
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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"> <bean name="classic" class="com.example.ExampleBean">
<property name="email" value="foo@bar.com"/>
</bean> <bean name="p-namespace" class="com.example.ExampleBean"
p:email="foo@bar.com"/>
</beans>
- example2,
-
关于c-namespace(简化配置代码) :用于简化<constructor-arg>标签配置代码
- example2,
<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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="bar" class="x.y.Bar"/>
<bean id="baz" class="x.y.Baz"/> <!-- traditional declaration -->
<bean id="foo" class="x.y.Foo">
<constructor-arg ref="bar"/>
<constructor-arg ref="baz"/>
<constructor-arg value="foo@bar.com"/>
</bean> <!-- c-namespace declaration -->
<bean id="foo" class="x.y.Foo" c:bar-ref="bar" c:baz-ref="baz" c:email="foo@bar.com"/> </beans> - example2,
- For the rare cases where the constructor argument names are not available (usually if the bytecode was compiled without debugging information), one can use fallback to the argument indexes:
<!-- c-namespace index declaration -->
<bean id="foo" class="x.y.Foo" c:_0-ref="bar" c:_1-ref="baz"/>
- example2,
-
关于p-namespace(简化配置代码) :用于简化<property>标签配置代码
-
3.1.2.8使用depends-on属性配置 (官网reference的7.4.3小节Using depends-on部分
depends-on=另一个bean的id或者name属性的值
使用depends-on指定的bean会优先被初始化,早于引用该bean的bean被初始化
- 使用depends-on指定的bean会首先被destroy,早于引用该bean的bean
- example1,
<bean id="beanOne" class="ExampleBean" depends-on="manager"/>
<bean id="manager" class="ManagerBean" /> -
example2,有多个值时用逗号、空格或者分号作为分隔符
<bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao">
<property name="manager" ref="manager" />
</bean> <bean id="manager" class="ManagerBean" />
<bean id="accountDao" class="x.y.jdbc.JdbcAccountDao" />
-
3.1.2.9 Lazy-initialized beans (官网reference的7.4.4小节Lazy-initialized beans
-
概述:默认情况下,在IOC容器中定义的beans,会在IOC容器初始化的时候就被创建出一个实例,这样可以更快地发现代码中可能出现的错误。如果开发者希望某些bean不在IOC容器初始化时(也即 when the
ApplicationContext is startup时
)就被创建,而是希望用到这些bean时才创建,那你就要通过配置代码tells the IoC container to create a bean instance when it is first requested, rather than at startup.However, when a lazy-initialized bean is a dependency of a singleton bean that is not lazy-initialized, the
ApplicationContext
creates the lazy-initialized bean at startup, because it must satisfy the singleton’s dependencies.
-
example1,
-
In XML, this behavior is controlled by the
lazy-init
attribute on the<bean/>
element; for example:<bean id="lazy" class="com.foo.ExpensiveToCreateBean" lazy-init="true"/>
<bean name="not.lazy" class="com.foo.AnotherBean"/>When the preceding configuration is consumed by an
ApplicationContext
, the bean namedlazy
is not eagerly pre-instantiated when theApplicationContext
is starting up, whereas thenot.lazy
bean is eagerly pre-instantiated.
-
-
example2,
-
You can also control lazy-initialization at the container level by using the
default-lazy-init
attribute on the<beans/>
element; for example:<beans default-lazy-init="true">
<!-- no beans will be pre-instantiated... -->
</beans>
-
-
-
3.1.2.10 自动化依赖关系配置/自动化依赖注入 (官网reference的7.4.5小节Autowiring collaborators
概述: 可以将自动化配置和普通非自动化配置结合使用,
-
自动化配置的模式有以下四种:
Mode Explanation no
(Default) No autowiring. Bean references must be defined via a
ref
element. Changing the default setting is not recommended for larger deployments, because specifying collaborators explicitly gives greater control and clarity. To some extent, it documents the structure of a system.byName
Autowiring by property name. Spring looks for a bean with the same name as the property that needs to be autowired. For example, if a bean definition is set to autowire by name, and it contains a master property (that is, it has a setMaster(..) method), Spring looks for a bean definition named
master
, and uses it to set the property.byType
Allows a property to be autowired if exactly one bean of the property type exists in the container. If more than one exists, a fatal exception is thrown, which indicates that you may not use byType autowiring for that bean. If there are no matching beans, nothing happens; the property is not set.
constructor
Analogous【类似的】 to byType, but applies to constructor arguments. If there is not exactly one bean of the constructor argument type in the container, a fatal error is raised.
With byType or constructor autowiring mode, you can wire arrays and typed-collections. In such cases all autowire candidates within the container that match the expected type are provided to satisfy the dependency. You can autowire strongly-typed Maps if the expected key type is String. An autowired Maps values will consist of all bean instances that match the expected type, and the Maps keys will contain the corresponding bean names.
-
设置了自动配置之后,还可以把个别的bean从自动配置机制中解放出来,即不让这些bean被自动注入依赖,而是用显式的依赖关系配置办法配置其依赖关系并且根据显式的配置信息执行依赖注入。具体实现方法是
方法一,可以set the
autowire-candidate
attribute of the<bean/>
element tofalse
;- 方法二,可以使用<beans/>标签的
default-autowire-candidates属性
.
-
3.1.2.11 Method injection,标签<lookup-method>的使用实例 (官网reference的7.4.6小节Method injection-----Lookup method injection
概述:有时候一个 singleton bean A needs to use non-singleton (prototype) bean B ,那么IOC容器实例化bean A和 B时就会遇到 问题,因为beanA是单实例的,所以A只会被实例化一次,所以A中的属性也只能被注入一次,所以每次调用A中的成员方法时,使用的都是同一个B实例,并没有达到Bean B是Protype的效果。为了使得每次调用singleton bean A 中的成员方法时,都重新注入protype的bean B,需要使用Method injection的方法编程
-
编程思路:(问题描述--- singleton bean A needs to use non-singleton (prototype) bean B)
实现
ApplicationContextAware
接口,making a getBean("B") call to the container ask for (a typically new) bean B instance every time bean A needs it.
编写beans依赖关系配置代码,如xml-based configuration中可以使用bean的子标签<lookup-method>进行配置
-
example1,
bean A-singleton bean-"如本例中的commandManager"
- bean B-protype bean-"如本例中的myCommand"
- 通过method injection的方法使得bean A中的每个方法被调用的时候,都能够得到一个新的bean B的实例(如果A需要的话)
-
<!-- a stateful bean deployed as a prototype (non-singleton) -->
<bean id="myCommand" class="fiona.apple.AsyncCommand" scope="prototype">
<!-- inject dependencies here as required -->
</bean> <!-- commandProcessor uses statefulCommandHelper -->
<bean id="commandManager" class="fiona.apple.CommandManager">
<lookup-method name="createCommand" bean="myCommand"/><!--name=class中的方法名,bean=另一个bean的id值-->
</bean>// a class that uses a stateful Command-style class to perform some processing
package fiona.apple; // Spring-API imports
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware; public class CommandManager implements ApplicationContextAware { private ApplicationContext applicationContext; public Object process(Map commandState) {
// grab a new instance of the appropriate Command
Command command = createCommand();
// set the state on the (hopefully brand new) Command instance
command.setState(commandState);
return command.execute();
} protected Command createCommand() {
// notice the Spring API dependency!
return this.applicationContext.getBean("command", Command.class);
} public void setApplicationContext(
ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}或者上述代码也可以简化成如下形式?(请再次查看reference documentation)
package fiona.apple; // no more Spring imports! public abstract class CommandManager { public Object process(Object commandState) {
// grab a new instance of the appropriate Command interface
Command command = createCommand();
// set the state on the (hopefully brand new) Command instance
command.setState(commandState);
return command.execute();
} // okay... but where is the implementation of this method?
protected abstract Command createCommand();
}
-
3.1.2.12 Method injection,标签<
replaced-method
>的使用实例(不常用) (官网reference的7.4.6小节Method injection-----Arbitrary method replacement相较于<lookup-method>方法进行method injection而言,这种方法不太常用,读者想要了解更多详细信息,可以查看相应的资料
-
3.1.2.13标签<bean>中scope属性的使用实例
参考资料: 1)官网reference的7.5小节 2)博客:spring framework中的bean
-