1. 装配Bean的可选方案
Spring容器负责创建应用程序中的bean并通过DI来协调这些对象之间的关系。
但需要告诉Spring要创建哪些Bean并且如何将其装配在一起。 Spring有三种主要的装配机制:
- 在XML中进行显式配置;
- 在java中进行显示配置(通过注解);
- 隐式的bean发现机制和自动装配。
这篇博客讲述通过XML装配Bean。
2. XML装配Bean
- 写一个简单的bean
public class Person{
private String name;
public Person(){}
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void sayHello(){
System.out.println(name+" is saying Hello Spring!");
}
}
这是一个我们想装配的bean。
2.1 属性注入
下面写一段代码:
- 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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="person" class="demo.Person">
<property name="name" value="chen"></property>
</bean>
</beans>
通过<bean>
来声明bean,bean的类通过class属性来指定,而id属性来唯一标识一个名字。当Spring发现这个<bean>
元素时,它将会调用其无参构造器来创建bean。然后将chen
字面值注入到这个bean的name属性。这就是属性注入。
还有一种方法,通过更加简洁的p-命名空间。为了用p-命名空间,需要在XML文件中文件中与其他的命名空间一起对其声明(一般IDE会帮我们做好这件事):xmlns:p="http://www.springframework.org/schema/p"
。
然后按照<bean id="person" class="demo.Person" p:name="chen"></bean>
来进行属性注入。
public class Main {
public static void main(String[] args) {
//原来的创建Person类实例的方法
//Person person = new Person();
//person.setName("chen");
//person.sayHello();
//通过Spring容器获取Person类实例
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Person person = (Person) context.getBean("person");//通过id来获取Person
person.sayHello();
}
}
现在Person类用有了一个Car类,不能进行字面值注入了,这个时候引入ref引用
- 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;
}
}
- XML的配置
<bean id="car" class="demo.Car">
<constructor-arg name="brand" value="BMW"></constructor-arg>
</bean>
<bean id="person" class="demo.Person" p:name="chen">
<property name="car" ref="car"></property>
</bean>
对于p-命名空间,也有p:car-ref,如:<bean id="person" class="demo.Person" p:name="chen" p:car-ref="car"></bean>
。
2.2 构造器注入
在上面的那个例子,可以看到在XML配置car的时候出现了<constructor-arg name="brand" value="BMW"></constructor-arg>
,这就是构造器注入。当然,与属性注入相似,也可以用c-命名空间(通过xmlns:c="http://www.springframework.org/schema/c"
声明)。
代码如下:
- XML配置
<bean id="car" class="demo.Car" c:brand="BMW">
</bean>
<bean id="person" class="demo.Person" c:name="chen" c:car-ref="car"></bean>
那如果Person所拥有的一个Car的List集合,即:
- Person类
public class Person {
private String name;
private List<Car> cars;
public Person(){}
public Person(String name, List<Car> cars) {
this.name = name;
this.cars = cars;
}
public List<Car> getCars() {
return cars;
}
public void setCars(List<Car> cars) {
this.cars = cars;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void sayHello(){
System.out.println(name+" is saying Hello Spring!");
}
}
这个时候引出在<constructor-arg>
下的<list>
,至于<set>,<map>,<props>
会在后面继续讨论。
如果字面值包含特殊字符可以使用<![CDATA[]]>
包裹起来。
使用构造器注入的属性值可以通过type指定参数参数类型;如:type="java.lang.String"
指明该属性值为String类型。
- XML配置
<bean id="car1" class="demo.Car" c:brand="BMW"></bean>
<!-- 使用构造器注入属性值可以通过type指定参数参数类型,以区分重载的构造器-->
<bean id="car2" class="demo.Car">
<constructor-arg name="brand" value="Audi" type="java.lang.String"></constructor-arg>
</bean>
<bean id="car3" class="demo.Car" c:brand="Benz"></bean>
<bean id="person" class="demo.Person">
<constructor-arg type="java.lang.String">
<!-- 字面值包含特殊字符,如:<chen>,可以使用<![CDATA[]]> 包裹起来-->
<value><![CDATA[<chen>]]></value>
</constructor-arg>
<constructor-arg>
<list>
<ref bean="car1"></ref>
<ref bean="car2"></ref>
<ref bean="car3"></ref>
</list>
</constructor-arg>
</bean>
如果在配置每个person bean的时候,都有相同的list,那我们可以将这些共同代码提取出来,即引入util-命名空间(通过xmlns:util="http://www.springframework.org/schema/util"
声明)。
- XML配置
<bean id="car1" class="demo.Car" c:brand="BMW"></bean>
<bean id="car2" class="demo.Car">
<constructor-arg name="brand" value="Audi" type="java.lang.String"></constructor-arg>
</bean>
<bean id="car3" class="demo.Car" c:brand="Benz"></bean>
<!-- 配置单例的集合bean,以供多个bean进行引用-->
<util:list id="cars">
<ref bean="car1"></ref>
<ref bean="car2"></ref>
<ref bean="car3"></ref>
</util:list>
<bean id="person" class="demo.Person">
<constructor-arg name="name" value="chen" type="java.lang.String"></constructor-arg>
<constructor-arg name="cars" ref="cars"></constructor-arg>
</bean>
2.3 工厂方法
上面两种都是通过反射的机制来配置bean,需要在bean配置中指明全类名。
而在工厂方法模式中, Spring不会直接利用反射机制创建bean对象, 而是会利用反射机制先找到Factory类,然后利用Factory再去生成bean对象。
而工厂方法分为两类:
- 静态工厂方法
- 实例工厂方法
工厂方法较其他方法用处较少,一般用于整合第三方框架的时候用到。
2.4 FactoryBean
FactoryBean 用法可以与工厂方法有点类似,我们同样需要写工厂类,只不过这个类需要实现FactoryBean接口。
- CarFactoryBean 类
public class CarFactoryBean implements FactoryBean<Car>{
private String brand;
public void setBrand(String brand) {
this.brand = brand;
}
@Override
public Car getObject() throws Exception {
return new Car(brand);
}
@Override
public Class<?> getObjectType() {
return Car.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
- XML配置
<!--
通过FactoryBean来配置bean的实例
class:指向FactoryBean的全类名
property:配置factory的属性
但实际返回的实例是FactoryBean的getObject()方法
-->
<bean id="car" class="demo.CarFactoryBean">
<property name="brand" value="BWM"></property>
</bean>
- 测试
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans-factoryBean.xml");
Car car = (Car) context.getBean("car");
System.out.println(car);
}
}