配置bean
配置形式:基于xml文件的方式;基于注解的方式
Bean的配置方式:通过全类名(反射)、通过工厂方法(静态工厂方法&实例工厂方法)、FactoryBean
IOC容器:BeanFactory&ApplicationContext概述
依赖注入的方式:属性注入;构造器注入
一、基于xml文件的形式
<!--
配置bean
class:bean的全类名,通过反射的方式在IOC容器中创建Bean,所以要求Bean中必须要有无参数的构造器
id:通过id获取bean,标识容器中的bean,并且唯一
-->
<bean id="hello" class="beans.HelloWorld">
<property name="name" value="Spring">
</property>
</bean>
在spring IOC容器读取Bean配置创建Bean实例之前,必须对它进行实例化,只有在容器实例化后,才可以从IOC容器里获取Bean实例并使用
spring提供了两种类型的IOC容器实现
- BeanFactory:IOC容器的基本实现(BeanFactory是spring框架的基础设施,面向spring本身)
- ApplicationContext:提供了更多的高级特性,是BeanFactory的子接口(ApplicationContext面向使用spring框架的开发者,几乎所有的应用场合都直接使用ApplicationContext而非底层的BeanFactory)
- 无论使用何种方式,配置文件时相同的
ApplicationContext的主要实现类:
- ClassPathXmlApplicationContext:从类路径下加载配置文件
- FileSystemXmlApplicationContext:从文件系统中加载配置文件
可以通过ApplicationContext类来查看结构
ConfigurableApplicationContext扩展于ApplicationContext,新增加两个主要方法:refresh()和close(),让ApplicationContext具有启动、刷新和关闭上下文的能力
ApplicationContext在初始化上下文时就实例化所有的bean
WebApplicationContext是专门为WEB应用而准备的,它允许从相对于WEB根目录的路径中完成初始化工作
// 创建spring的容器对象
// ApplicationContext代表IOC容器
// ClassPathXmlApplicationContext:是ApplicationContext接口的实现类,该实现类从类路径下来加载配置
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
// 从IOC容器中获取bean实例
HelloWorld he = (HelloWorld)ctx.getBean("hello");
//调用方法
he.hello();
拓展:从IOC容器获取bean实例不仅可以通过id获取,也可以通过类类型来获取
HelloWorld he = ctx.getBean(HelloWorld.class);
缺点:当IOC容器中有多个相同的该类型的bean时,不能使用该方式获取bean
二、依赖注入的方式
spring支持3中依赖注入的方式
- 属性注入(最常用的注入方式)
- 构造器注入
- 工厂方法注入(很少使用,不推荐)
1.属性注入
通过setter方法注入Bean的属性值或依赖的对象,使用<property>元素,使用name属性指定bean的属性名称,value属性或<value>子节点指定属性值
2.构造方法注入
通过构造方法注入bean的属性值或依赖的对象,它保证bean实例在实例化后就可以使用,构造器注入在<constructor-arg>元素里声明属性,<constructor-arg>中没有name属性
现在有一个Car类
package beans;
public class Car
{
private String brand;
private String corp;
private int price;
private int maxSpeed;
// 只初始化了三个属性
public Car(String brand, String corp, int price)
{
super();
this.brand = brand;
this.corp = corp;
this.price = price;
}
@Override
public String toString()
{
return "Car [brand=" + brand + ", corp=" + corp + ", price=" + price +
", maxSpeed=" + maxSpeed + "]";
}
}
然后在ApplicationContext.xml中配置bean
<!-- 通过构造方法来配置bean的属性 -->
<!-- 这里的index为参数顺序的索引,不写index默认按标签顺序注入-->
<bean id="car" class="beans.Car">
<constructor-arg value="Audi" index="0"></constructor-arg>
<constructor-arg value="Shanghai" index="1"></constructor-arg>
<constructor-arg value="300000" index="2"></constructor-arg>
</bean>
<!-- 通过构造方法来配置bean的属性 -->
<!-- 也可以通过类中的构造方法的参数类型来注入 -->
<bean id="car" class="beans.Car">
<constructor-arg value="Audi" type="java.lang.String"></constructor-arg>
<constructor-arg value="Shanghai" type="java.lang.String"></constructor-arg>
<!-- 这里的类型为int,但是写法和String类型写法一样,注入时会自动转为int类型 -->
<constructor-arg value="300000" type="int"></constructor-arg>
<!--也可以这样写-->
<constructor-arg type="int">
<value>1000</value>
</constructor-arg>
</bean>
然后获取bean并调用得到结果
Car car = ctx.getBean(Car.class);
System.out.println(car);
通过构造方法注入时可以用字符串表示的值注入,也可以通过<value>元素标签或value属性进行注入。基本数据类型及其封装类、String等类型都可以采用字面值的注入的方式,若字面值中包含特殊字符可以使用<![CDATA[]]>把字面值包裹起来。
<constructor-arg value="<Shanghai>" type="java.lang.String"></constructor-arg>
例如:"<>"是必须加上<![CDATA[]]>的,应写作
<constructor-arg type="java.lang.String">
<value><![CDATA[<shanghai^>]]></value>
</constructor-arg>
引用其他Bean
- 组成应用程序的Bean经常需要相互协作以完成应用程序的功能,要使Bean能够相互访问,就必须在Bean配置文件中指定对Bean 的引用
- 在Bean的配置文件中,可以通过<ref>元素或ref属性为Bean的属性或构造器参数指定对Bean 的引用
- 也可以在属性或构造器包含Bean的声明,这样的Bean称为内部Bean
现在有一个Person类
package beans;
public class Person
{
private String name;
private int age;
private Car car;
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public int getAge()
{
return age;
}
public void setAge(int age)
{
this.age = age;
}
public Car getCar()
{
return car;
}
public void setCar(Car car)
{
this.car = car;
}
@Override
public String toString()
{
return "Person [name=" + name + ", age=" + age + ", car=" + car + "]";
}
}
在配置中加入以下配置就可以建立Car类和Person类的引用关系
<bean id="person" class="beans.Person">
<property name="name" value="tom"></property>
<property name="age" value="24"></property>
<property name="car" ref="car"></property>
<!--两种写法等价-->
<property name="car">
<ref bean="car"/>
</property>
</bean>
获取bean后运行结果为
也可以配置内部Bean
<bean id="person" class="beans.Person">
<property name="name" value="tom"></property>
<property name="age" value="24"></property>
<!-- 内部bean ,不能被外部引用,只能在内部使用-->
<property name="car">
<bean id="car" class="beans.Car">
<constructor-arg value="Ford"></constructor-arg>
<constructor-arg value="Changan"></constructor-arg>
<constructor-arg value="20000" type="double"></constructor-arg>
</bean>
</property>
</bean>
运行结果为
null值和级联属性
- 可以使用专用的<null/>元素标签为Bean的字符串或其它对象类型的属性注入null值
例如
<bean id="person" class="beans.Person">
<constructor-arg value="Jerry"></constructor-arg>
<constructor-arg value="25"></constructor-arg>
<constructor-arg ><null/></constructor-arg>
</bean>
获取bean运行后结果
- 和struts、Hibernate等框架一样,spring支持级联属性的配置
例如
<bean id="person" class="beans.Person">
<constructor-arg value="Jerry"></constructor-arg>
<constructor-arg value="25"></constructor-arg>
<constructor-arg ref="car"></constructor-arg>
<!--为级联属性赋值-->
<property name="car.maxSpeed" value="300"></property>
</bean>
运行也是可以得到结果的,这里不做展示
集合属性
- 在spring中可以通过一组内置的xml标签(例如:<list>,<set>或<map>来配置集合属性)
- 配置java.util.List类型的属性,需要指定<list>标签,在标签里包含一些元素,这些标签可以通过<value>指定简单的常量值,通过<ref>指定对其他Bean的引用,通过<bean>指定内置Bean的定义,通过<null/>指定空元素,甚至可以内嵌其他集合
- 数组的定义和List一样,都使用<list>
- 配置java.util.Set需要使用<set>标签,定义元素的方法与List一样
当car是一个集合时,在Person类里把car改为List类型
配置如下
<bean id="car1" class="beans.Car">
<constructor-arg value="Audi" type="java.lang.String"></constructor-arg>
<constructor-arg value="wuhan" type="java.lang.String"></constructor-arg>
<constructor-arg value="100000" type="int"></constructor-arg>
</bean>
<bean id="car2" class="beans.Car">
<constructor-arg value="Baoma" type="java.lang.String"></constructor-arg>
<constructor-arg value="Wuhan" type="java.lang.String"></constructor-arg>
<constructor-arg value="200000" type="int"></constructor-arg>
</bean>
<bean id="car3" class="beans.Car">
<constructor-arg value="Luland" type="java.lang.String"></constructor-arg>
<constructor-arg value="Beijing" type="java.lang.String"></constructor-arg>
<constructor-arg value="300000" type="int"></constructor-arg>
</bean>
<bean id="person1" class="beans.Person">
<property name="name" value="Mike"></property>
<property name="age" value="27"></property>
<property name="car">
<list>
<ref bean="car1"/>
<ref bean="car2"/>
<ref bean="car3"/>
</list>
</property>
</bean>
获取Bean运行后得到结果
- Java.util.Map通过<map>标签定义,<map>标签里可以使用多个<entry>作为子标签,每个条目包含一个键和一个值
同样的,car类型改为Map类型,配置如下
<!-- 配置Map属性值 -->
<bean id="newPerson" class="beans.NewPerson">
<property name="name" value="Rose"></property>
<property name="age" value="28"></property>
<property name="car">
<map>
<entry key="AA" value-ref="car1"></entry>
<entry key="BB" value-ref="car2"></entry>
</map>
</property>
</bean>
获取bean后运行得到结果
- 必须在<key>标签里定义键
- 因为键和值的类型没有限制,所以可以自由地为它们指定<value>,<ref>,<bean>或<null/>元素
- 可以将Map的键和值作为<entry>的属性定义:简单常量使用key和value来定义,Bean引用通过key-ref和value-ref属性定义
- 使用<props>定义java.util.Properties,该标签使用多个<prop>作为子标签,每个<prop>标签必须定义key属性
添加一个DataSource类
package beans;
import java.util.Properties;
public class DataSource
{
private Properties properties;
public Properties getProperties()
{
return properties;
}
public void setProperties(Properties properties)
{
this.properties = properties;
}
@Override
public String toString()
{
return "DataSource [properties=" + properties + "]";
}
}
Bean配置如下
<!-- 配置properties属性值 -->
<bean id="dataSource" class="beans.DataSource">
<property name="properties">
<props>
<prop key="user">root</prop>
<prop key="password">123456</prop>
<prop key="jdbcUrl">jdbc:mysql://test</prop>
<prop key="driverClass">com.mysql.jdbc.Driver</prop>
</props>
</property>
</bean>
运行得到结果
使用utility scheme定义集合
- 使用基本的集合标签定义集合时,不能将集合作为独立的Bean定义,导致其他Bean无法引用该集合,所以无法在不同的Bean,所以无法在不同的Bean之间共享集合
- 可以使用util schema里的集合标签定义独立的集合Bean,需要注意的是,必须在<beans>根元素里添加util schema定义
配置如下
<!-- 配置单例的集合Bean,以供多个bean进行引用 ,需要导入util命名空间-->
<util:list id="car">
<ref bean="car1"/>
<ref bean="car2"/>
</util:list>
<bean id="person4" class="beans.Person">
<property name="name" value="Jack"></property>
<property name="age" value="29"></property>
<property name="car" ref="car"></property>
</bean>
获取bean,运行后得到结果
使用p命名空间
- 为了简化XML文件的配置,越来越多的XML文件采用属性而非子元素配置信息。
- spring 从2.5版本开始引入了一个新的p命名空间,可以通过<bean>元素属性的方式配置Bean的属性
- 使用p命名空间后,基于XML的配置方式将进一步简化
配置如下
<!-- 通过p命名空间为bean的属性赋值,需要先导入p命名空间,相对于传统的方式更加简洁 -->
<bean id="person5" class="beans.Person"
p:age="30" p:name="Queen" p:car-ref="car">
</bean>
获取bean运行得到结果
3.通过调用工厂方法创建Bean
a.静态工厂方法
- 实例工厂方法:将对象的创建过程封装到另外一个对象实例的方法里,当客户端需要请求对象时,只需要简单的调用该实例方法而不需要关心对象的创建细节
- 要声明通过工厂方法创建的Bean
- 在bean的factory-bean属性里指定拥有该工厂方法的Bean
- 在factory-method属性里指定该工厂方法的名称
- 使用constructor-arg元素为工厂方法传递参数
现在有一个类StaticCarFactory
package factory;
import java.util.HashMap;
import java.util.Map;
/**
* 静态工厂方法:直接调用某一个类的静态方法就可以返回Bean的实例
* @author dacryon
*
*/
public class StaticCarFactory
{
private static Map<String,Car> cars = new HashMap<String,Car>();
static
{
cars.put("audi", new Car("audi",300000));
cars.put("ford", new Car("ford",400000));
}
//静态工厂方法
public static Car getCar(String name)
{
return cars.get(name);
}
}
bean配置如下
<!-- 通过静态方法工厂来配置Bean,注意不是配置静态工厂方法实例,而是配置Bean实例 -->
<!--
class属性:指向静态工厂方法的全类名
factory-method:指向静态工厂方法的名字
constructor-arg:如果工厂方法需要传入参数,则使用constructor-arg来配置参数
-->
<bean id="car1" class="factory.StaticCarFactory" factory-method="getCar">
<constructor-arg value="audi"></constructor-arg>
</bean>
运行后得到结果
b.实例工厂方法
- 实例工厂方法:将对象的创建过程封装到另外一个对象实例的方法里,当客户端需要请求对象时,只需要简单的调用该实例方法而不需要关心对象的创建细节
- 要声明通过实例工厂方法创建的Bean
- 在bean的factory-bean属性里指定拥有该工厂方法的Bean
- 在factory-method属性里指定该工厂方法的名称
- 使用constructor元素为工厂方法传递参数
现在有一个类 InstanceCarFactory
package factory;
import java.util.HashMap;
import java.util.Map;
/**
* 实例工厂方法:实例工厂的方法,即现需要创建工厂本身,在调用工厂的实例方法来返回Bean的实例
* @author dacryon
*
*/
public class InstanceCarFactory
{
private Map<String,Car> cars = null;
public InstanceCarFactory()
{
cars = new HashMap<String,Car>();
cars.put("audi", new Car("audi",300000));
cars.put("ford", new Car("ford",400000));
}
public Car getCar(String brand)
{
return cars.get(brand);
}
}
配置如下
<!-- 通过实例工厂方法来配置bean -->
<!--
class属性:指向实例工厂方法的全类名
factory-method:指向实例工厂方法的名字
constructor-arg:如果工厂方法需要传入参数,则使用constructor-arg来配置参数
-->
<bean id="car2" factory-bean="carFactory" factory-method="getCar">
<constructor-arg value="ford"></constructor-arg>
</bean>
获取bean运行后得到结果
4.通过FactoryBean配置Bean
添加一个类CarFactoryBean
package factoryBean;
import org.springframework.beans.factory.FactoryBean;
/**
* 自定义的FactoryBean需要实现FactoryBean接口
* @author dacryon
*
*/
public class CarFactoryBean implements FactoryBean<Car>
{
private String brand;
public void setBrand(String brand)
{
this.brand = brand;
}
/**
* 返回Bean的对象
*/
@Override
public Car getObject() throws Exception
{
// TODO Auto-generated method stub
return new Car("brand",500000);
}
/**
* 返回Bean的类型
*/
@Override
public Class<?> getObjectType()
{
// TODO Auto-generated method stub
return Car.class;
}
@Override
public boolean isSingleton()
{
// TODO Auto-generated method stub
return true;
}
}
配置如下
<!--
通过FactoryBean来配置Bean的实例
class:指向FactoryBean的全类名
property:配置FactoryBean的属性,但实际返回的实例确实FactoryBean的getObject()方法返回的实例
-->
<bean id="car" class="factoryBean.CarFactoryBean">
<property name="brand" value="BMW"></property>
</bean>
获取bean实例运行得到结果