1. 装配Bean的可选方案
Spring容器负责创建应用程序中的bean并通过DI来协调这些对象之间的关系。
但需要告诉Spring要创建哪些Bean并且如何将其装配在一起。 Spring有三种主要的装配机制:
- 在XML中进行显式配置;
- 在java中进行显示配置(通过注解);
- 隐式的bean发现机制和自动装配。
这篇博文讲述XML高级装配。
2. XML自动装配
在上一篇博文中是通过XML手动配置的,但较麻烦,Spring为了提高开发效率,提供自动装配,来简化配置。Spring默认不支持自动装配;要想使用,需要修改Spring配置文件中<bean>
标签的autowire属性。
Spring支持:
- byName :根据当前bean的setter风格的属性名和bean的名字(id)进行自动装配。
- byType:根据当前bean的属性的类型和bean的类型进行自动装配,若IOC容器中有多个类型匹配的bean,发生冲突。
- constructor(不推荐使用):使用构造方法进行自动装配,根据构造方法参数的byType方式。
- autodetect(已被弃用):自动选择:如果对象没有无参构造方法,那么自动选择constructor的自动装配方式进行构造注入。如果对象含有无参数的构造方法,那么自动选择byType的自动装配方式进行setter注入。
2.1 byName自动装配
先写两个bean:
- Car类
public class Car {
private String brand;
public Car() { }
public Car(String brand) {
this.brand = brand;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
@Override
public String toString() {
return "Car{" +
"brand='" + brand + '\'' +
'}';
}
}
- Person类
public class Person {
private String name;
private Car car;
public Person(){}
public Person(String name, Car car) {
this.name = name;
this.car = car;
}
public Car getCar() {
return car;
}
public void setCar(Car car) {
this.car = car;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", car=" + car +
'}';
}
}
- SpringXML配置
<?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:p="http://www.springframework.org/schema/p"
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 http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<bean id="car" class="demo.Car" p:brand="Audi"></bean>
<!--手动装配-->
<!--<bean id="person" class="demo.Person" p:name="chen" p:car-ref="car"></bean>-->
<!--byName自动装配-->
<bean id="person" class="demo.Person" p:name="chen" autowire="byName"></bean>
</beans>
- 测试
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans-autowire.xml");
Person person = (Person) context.getBean("person");
System.out.println(person);
}
}
会在控制台打印出Person{name='chen', car=Car{brand='Audi'}}
。
2.2 byType自动装配
- SpringXML配置
<?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:p="http://www.springframework.org/schema/p"
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 http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<!--修改car bean的配置中id属性-->
<bean id="carcarcarcar" class="demo.Car" p:brand="Audi"></bean>
<!--手动装配-->
<!--<bean id="person" class="demo.Person" p:name="chen" p:car-ref="car"></bean>-->
<!--byType自动装配-->
<bean id="person" class="demo.Person" p:name="chen" autowire="byType"></bean>
</beans>
修改了car bean配置中id属性之后,也能通过byType
来装配。
在控制打印出相同的结果Person{name='chen', car=Car{brand='Audi'}}
。
2.3 constructor自动装配
- SpringXML配置
<?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:p="http://www.springframework.org/schema/p"
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属性-->
<bean id="carcarcarcar" class="demo.Car" p:brand="Audi"></bean>
<bean id="namenamename" class="java.lang.String" c:value="chen"></bean>
<bean id="person" class="demo.Person" autowire="constructor"></bean>
</beans>
修改id与bean的setter不同后,即
<bean id="carcarcarcar" class="demo.Car" c:brand="Audi"></bean>
<bean id="namenamename" class="java.lang.String" c:value="chen"></bean>
修改id后依旧可以通过constructor
自动装配获取,可以验证是通过构造方法的参数byType自动装配的。
3. Bean之间的关系
这里的关系是指bean 的配置之间的关系,而不是指实际意义上类与类之间的继承与依赖。
3.1 继承(parent)
使用bean的parent属性指定继承bean的配置
1.子bean可以覆盖从父bean继承的配置
2.将父bean作为模板,可以设置<bean>
的abstract属性为true,Spring不会实例化这个bean
3.autowire、abstract等属性不会被继承
4.可以忽略父bean的class属性,让子bean指定自己的类,从而共享相同的属性配置,但此时abstract必须设为true
- SpringXML配置
<?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:p="http://www.springframework.org/schema/p"
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="car1" class="demo.Car" p:brand="BMW" abstract="true"></bean>
<bean id="car2" class="demo.Car" parent="car1"></bean>
<bean id="person" class="demo.Person" p:name="chen" p:car-ref="car2"></bean>
</beans>
这段代码就出现了两个属性abstract="true"
和parent="car1"
。
3.2 依赖(depend-on)
不指定 depends-on 属性,IOC 容器默认实例化的顺序是按照 bean 在配置文件中的顺序来实例化的。
指定后,依赖的bean会在本bean实例化之前创建。可以指定多个依赖bean。
- SpringXML配置
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- depends-on :person这个bean必须依赖于car,若依赖多个bean,通过逗号和空格来指定-->
<bean id="person" class="demo.Person" p:name="chen" p:car-ref="car2 car1"></bean>
<bean id="car1" class="demo.Car" p:brand="BMW"></bean>
<bean id="car2" class="demo.Car" parent="car1"></bean>
</beans>
4. Bean的作用域(scope)
定义在bean的一个属性scope,默认为singleton
,如:
<bean id="car" class="demo.Car" scope="singleton"></bean>
作用域 | 描述 |
---|---|
singleton | 单例模式,在整个Spring IoC容器中,使用singleton定义的Bean将只有一个实例 |
prototype | 原型模式,每次通过容器的getBean方法获取prototype定义的Bean时,都将产生一个新的Bean实例 |
request | 对于每次HTTP请求,使用request定义的Bean都将产生一个新实例,即每次HTTP请求将会产生不同的Bean实例。只有在Web应用中使用Spring时,该作用域才有效 |
session | 对于每次HTTP Session,使用session定义的Bean都将产生一个新实例。同样只有在Web应用中使用Spring时,该作用域才有效 |
globalSession | 每个全局的HTTP Session,使用session定义的Bean都将产生一个新实例。典型情况下,仅在使用portlet context的时候有效。同样只有在Web应用中使用Spring时,该作用域才有效 |
5. 使用外部属性文件
当出现:
- 参数在某些阶段是常量
- 参数在不同阶段可能需要改变
对于这种情况Spring可以引入<content:property-placeholder>
元素。
下面用过连接数据库的例子。
在context命名空间(通过xmlns:context="http://www.springframework.org/schema/context"
声明)
- SpringXML配置
<context:property-placeholder location="classpath:db.properties"/>
另一种:
- SpringXML配置
<bean id="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>db.properties</value>
</list>
</property>
</bean>
如果location中有多个文件,将依次加载,但如果前面一个文件的属性名和后面的一个文件属性名相同,最终取得的是后面文件的值。
所以需要避免不同文件中的属性名重名。
- db.properties配置文件
#jdbc配置
user=root
password=root
driverClass=com.mysql.jdbc.Driver
jdbcUrl=jdbc:mysql:///library
- SpringXML配置
<!-- 使用外部化属性文件的属性 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="${user}" ></property>
<property name="password" value="${password}"></property>
<property name="driverClass" value="${driverClass}"></property>
<property name="jdbcUrl" value="${jdbcUrl}"></property>
</bean>
这里出现了类似于EL表达式的${}形式。
Spring容器仅允许最多定义一个PropertyPlaceholderConfigurer
或 <content:property-placeholder>
其余的会被Spring忽略。