装配Bean
来源:互联网 发布:黑暗之魂淘宝 编辑:程序博客网 时间:2024/04/29 19:14
装配Bean
在 Spring IOC 容器读取 Bean 配置创建 Bean 实例之前, 必须对它进行实例化. 只有在容器实例化后, 才可以从 IOC 容器里获取 Bean 实例并使用.
Spring 提供了两种类型的 IOC 容器实现.
- BeanFactory: IOC 容器的基本实现.
- ApplicationContext: 提供了更多的高级特性. 是 BeanFactory 的子接口.
- BeanFactory 是 Spring 框架的基础设施,面向 Spring 本身;ApplicationContext 面向使用 Spring 框架的开发者,几乎所有的应用场合都直接使用 ApplicationContext 而非底层的 BeanFactory
- 无论使用何种方式, 配置文件时相同的.
ApplicationContext 的主要实现类:
- ClassPathXmlApplicationContext:从 类路径下加载配置文件
- FileSystemXmlApplicationContext: 从文件系统中加载配置文件
ConfigurableApplicationContext 扩展于 ApplicationContext,新增加两个主要方法:refresh() 和 close(), 让 ApplicationContext 具有启动、刷新和关闭上下文的能力。 ApplicationContext
在初始化上下文时就实例化所有单例的 Bean
。
WebApplicationContext 是专门为 WEB 应用而准备的,它允许从相对于 WEB 根目录的路径中完成初始化工作。
装配形式
- 基于XML文件的形式
- 基于注解的方式
Bean的配置方式
通过全类名
<bean class="com.helloworld.HelloWorld"></bean>
因为没有给出ID,所以这个Bean将会根据全限定类名来进行命名。在这个例子中,ID为
com.helloworld.HelloWorld#0
,我们通过ID来引用Bean。因为是全限定类名,猜想Spring是通过反射来创建Bean的,所以我们的Bean需要有一个无参数的构造器。
通过工厂方法
静态工厂方法
调用静态工厂方法创建 Bean是将对象创建的过程封装到静态方法中. 当客户端需要对象时, 只需要简单地调用静态方法, 而不同关心创建对象的细节.
要声明通过静态方法创建的 Bean, 需要在 Bean 的 class 属性里指定拥有该工厂的方法的类, 同时在 factory-method 属性里指定工厂方法的名称. 最后, 使用 <constrctor-arg>
元素为该方法传递方法参数.
public class StaticCarFactory { static HashMap<String, Car> map = new HashMap<>(); static{ map.put("Audi", new Car("Audi", 300000.0)); map.put("Ford", new Car("Ford", 500000.0)); } public static Car getCar(String name) { return map.get(name); }}<!-- 通过静态工厂方法来配置Bean,注意不是配置静态工厂方法实例,而是配置Bean实例 --><!-- class:指向静态工厂的全类名 factory-method:返回Bean的方法 constructor-arg:如果工厂方法需要传入参数,使用constructor-arg --><bean id="car1" class="com.factory.StaticCarFactory" factory-method="getCar"> <constructor-arg value="Audi"></constructor-arg></bean>
实例工厂方法
实例工厂方法: 将对象的创建过程封装到另外一个对象实例的方法里. 当客户端需要请求对象时, 只需要简单的调用该实例方法而不需要关心对象的创建细节.
要声明通过实例工厂方法创建的 Bean
- 在 bean 的 factory-bean 属性里指定拥有该工厂方法的 Bean
- 在 factory-method 属性里指定该工厂方法的名称
- 使用 construtor-arg 元素为工厂方法传递方法参数
public class InstanceCarFactory { HashMap<String, Car> map = new HashMap<>(); { map.put("Audi", new Car("Audi", 300000.0)); map.put("Ford", new Car("Ford", 500000.0)); } public Car getCar(String name) { return map.get(name); }}<!-- 配置工厂实例 --><bean id="CarFactory" class="com.factory.InstanceCarFactory"></bean><!-- 实例工厂导入 --><bean id="car2" class="com.factory.InstanceCarFactory" factory-bean="CarFactory" factory-method="getCar"> <constructor-arg value="Ford"></constructor-arg></bean>
FactoryBean
Spring
中有两种类型的 Bean
, 一种是普通Bean
, 另一种是工厂Bean
, 即FactoryBean
. 工厂 Bean
跟普通Bean
不同, 其返回的对象不是指定类的一个实例, 其返回的是该工厂 Bean
的 getObject
方法所返回的对象
通过FactoryBean来配置Bean实例,Class指向FactoryBean的全类名,但实际返回的是getObject方法返回的实例!
public class CarFactoryBean implements FactoryBean<Car> { private String brand; public void setBrand(String brand) { this.brand = brand; } //返回bean实例 @Override public Car getObject() throws Exception { return new Car(brand, 300000.0); } //返回bean对象 @Override public Class<?> getObjectType() { return Car.class; } //是否是单例模式 @Override public boolean isSingleton() { return true; }}<bean id="car" class="com.factorybean.CarFactoryBean" > <property name="brand" value="BMW"></property></bean>
依赖注入的方式
构造器注入
通过构造方法注入Bean
的属性值或依赖的对象,它保证了 Bean
实例在实例化后就可以使用。
构造器注入在 <constructor-arg>
元素里声明属性, <constructor-arg>
中没有 name 属性。
<constructor-arg>
元素
举例:
特别地,当使用构造器注入时,相应的Bean
类需要有对应的构造函数。
<!-- 通过构造方法来配置Bean 可以按【顺序,位置】或【索引】或者【类型匹配】参数 也接受引用 --><constructor-arg ref="car1"/><constructor-arg values="Audi"/><constructor-arg index="0" values="Audi"/><constructor-arg value="Linken" type="java.lang.String"/>
p-命名空间
举例: Spring 2.5
所引入的。导入p-命名空间
实现Bean的装配。
<!-- 通过P命名空间为Bean的属性赋值,需要先导入P命名空间 ,相对于传统的配置方式很简洁 --><bean id="ren" class="com.collections.Person" p:name="RenLei" p:age="23" p:cars-ref="mycars"></bean>
c-命名空间
Spring 3.0
所引入的。导入c-命名空间
实现Bean的装配。
<!-- 同样也接受引用[c:name-ref]--><bean id="car4" class="com.helloworld.Car" c:name="Ford" c:price="1230000.0" c:speed="120" ></bean>
属性注入
使用属性的setter
方法,利用<property>
标签设置。同样,可以对字面量,集合属性赋值。属性注入是实际应用中最常用的注入方式。
<property name="name" value="WangHao"/><!-- 可以使用Property的ref属性建立Bean之间的联系 --><property name="car" ref="car1"/><property name="car" > <ref bean="car1"/></property><!-- 为级联属性赋值,属性需要先初始化 --><property name="car.speed" value="1234"/><!-- property里创建内部Bean --><property name="car"> <!-- 引用内部Bean,不能被外部引用,ID不需要写 --> <bean class="com.helloworld.Car"> <constructor-arg type="java.lang.String"><null/></constructor-arg> <constructor-arg value="50000.0" type="double"></constructor-arg> <constructor-arg value="500" type="int"></constructor-arg> </bean></property><!-- 装配集合Map --><property name="map"> <map> <entry key="AA" value-ref="car1"></entry> <entry key="BB" value-ref="car2"></entry> </map></property>
工厂方法注入
很少使用,不推荐使用
补充
关于字面值
字面值:可用字符串表示的值,可以通过
<value>
元素标签或value
属性进行注入。
基本数据类型及其封装类、String
等类型都可以采取字面值注入的方式
若字面值中包含特殊字符,可以使用<![CDATA[]]>
把字面值包裹起来。
引用其他Bean
组成应用程序的 Bean
经常需要相互协作以完成应用程序的功能. 要使 Bean
能够相互访问, 就必须在 Bean
配置文件中指定对 Bean
的引用。
在 Bean
的配置文件中, 可以通过 <ref>
元素或 ref 属性为 Bean 的属性或构造器参数指定对 Bean
的引用。
也可以在属性或构造器里包含 Bean
的声明, 这样的 Bean
称为内部 Bean
。
内部Bean
当 Bean 实例仅仅给一个特定的属性使用时, 可以将其声明为内部 Bean. 内部 Bean 声明直接包含在 <property>
或 <constructor-arg>
元素里, 不需要设置任何 id
或 name
属性。
内部
Bean
不能使用在任何其他地方。
null值
可以使用专用的 <null/>
元素标签为 Bean
的字符串或其它对象类型的属性注入 null
。
举例:
<property name="name"> <null/></property>
级联属性
和 Struts
、Hiberante
等框架一样,Spring
支持级联属性的配置。
<!-- 为级联属性赋值,属性需要先初始化 --><property name="car.speed" value="1234"></property>
装配集合
这是c-命名空间
和p-命名空间
无法做到的。
<constructor-arg value="cars" > <!-- 为集合属性赋值,Set和数组以及List原理相同 --> <list> <ref bean="car1"/> <ref bean="car2"/> <ref bean="car3"/> <bean class="com.helloworld.Car"> <constructor-arg type="java.lang.String"><null/></constructor-arg> <constructor-arg value="50200.0" type="double"></constructor-arg> <constructor-arg value="520" type="int"></constructor-arg> </bean> </list></constructor-arg>
我们看到,在<constructor-arg>
装配集合属性,用的是引用,当然也可以用<value>
又在集合里创建Bean实例。
Properties
继承Hashtable<Object,Object>
。下面这个例子是配置这样的一个Properties
。
//POJOpublic 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 + "]"; }}<!-- 配置Properties属性值 --> <bean id="proper" class="com.collections.DataSource"> <property name="properties"> <props> <prop key="user">root</prop> <prop key="password">102559</prop> <prop key="database">mysql:jdbc://test</prop> <prop key="drive">com.mysql.jdbc.Driver</prop> </props> </property> </bean>
util
导入util-命名空间
可以更便捷的配置集合属性。
<!-- 配置单例的集合Bean,以便多个Bean使用,需要导入util命名空间 --><util:list id="mycars"> <ref bean="car1"/> <ref bean="car2"/> <ref bean="car3"/></util:list>
util-命名空间
的元素
<util:constant>
引用某个类型的public static域,并将其暴露为Bean <util:list>
创建一个java.util.List类型的Bean,其中包含值或应用 <util:map>
创建一个java.util.Map类型的Bean,其中包含值或应用 <util:properties>
创建一个java.util.Properties类型的Bean <util:property-path>
引用一个Bean的属性,并将其暴露为Bean <util:set>
创建一个java.util.Set类型的Bean,其中包含值或应用自动装配
Spring IOC
容器可以自动装配 Bean
。需要做的仅仅是在 <bean>
的 autowire
属性里指定自动装配的模式。
- byType
(根据类型自动装配): 若 IOC
容器中有多个与目标 Bean
类型一致的 Bean
。 在这种情况下, Spring
将无法判定哪个 Bean
最合适该属性, 所以不能执行自动装配。
- byName
(根据名称自动装配): 必须将目标 Bean
的名称和属性名设置的完全相同. constructor
(通过构造器自动装配): 当 Bean
中存在多个构造器时, 此种自动装配方式将会很复杂。(不推荐使用)
autowire 属性要么根据类型自动装配, 要么根据名称自动装配, 不能两者兼而有之。
举例:
Person有三个属性:
- private String name;
- private Car car;
private Address address;
创建了
Bean Car
和Bean Address
两个Bean
。在创建Person Bean
时,指定autowire
属性,Spring IOC
会自动装配Bean
。使用byName
,实例名 必须完全匹配;使用byType
,实例必须要求只有一个。
<bean id="address2" class="com.autowire.Address" p:city="HuBei" p:street="NanJingDongLu"></bean><bean id="car2" class="com.autowire.Car" p:brand="Audi" p:price="100000.0"></bean><!-- 可以使用autowire属性指定自动装配的方式 byName:名称必须完全匹配 byType:只能有一个实例 --><bean id="person" class="com.autowire.Person" p:name="WangHao" autowire="byType"></bean>
Bean依赖&继承
- 继承
Spring
允许继承bean
的配置, 被继承的bean
称为父bean
. 继承这个父Bean
的Bean
称为子Bean
- 子
Bean
从父Bean
中继承配置, 包括Bean
的属性配置
子Bean
也可以覆盖从父 Bean 继承过来的配置 - 父
Bean
可以作为配置模板, 也可以作为 Bean 实例. 若只想把父Bean
作为模板, 可以设置<bean>
的abstract
属性为true
, 这样Spring
将不会实例化这个Bean
- 并不是
<bean>
元素里的所有属性都会被继承. 比如:autowire
,abstract
等. - 也可以忽略父
Bean
的class
属性, 让子Bean
指定自己的类, 而共享相同的属性配置. 但此时abstract
必须设为true
举例:
配置上的继承。
<!-- 抽象的Bean不能被实例化 如果不指定Class,则Bean必须是抽象的--><bean id="address" class="com.autowire.Address" p:city="HuBei" p:street="RenMingLu" abstract="true"></bean><!-- 继承Bean后并覆盖了其中的某个属性 --><bean id="address2" p:street="NanJingDongLu" parent="address"></bean>
- 依赖
- Spring 允许用户通过 depends-on 属性设定 Bean 前置依赖的Bean,前置依赖的 Bean 会在本 Bean 实例化之前创建好
- 如果前置依赖于多个 Bean,则可以通过逗号,空格或的方式配置 Bean 的名称
<!-- bean之间的依赖 --><bean id="person2" class="com.autowire.Person" p:name="WangJun" depend-on="car2" p:address-ref="address2"></bean>
Bean的声明周期
来看一个栗子:
有一个类Book,分别在构造器,setter方法,init方法,destory方法标记输出:
public class Book { public Book() { System.out.println("Book Constructor..."); } private String name; public void setName(String name) { this.name = name; System.out.println("Setting..."); } public void init() { System.out.println("Init..."); } public void destory() { System.out.println("Destory..."); } @Override public String toString() { return "Book [name=" + name + "]"; }}
创建一个类,实现BeanPostProcessor
,在postProcessAfterInitialization
和postProcessBeforeInitialization
同样标记输出语句。这两个方法传入分别的Bean
和Bean
的名称,返回的也是Bean
。Bean
后置处理器允许在调用初始化方法前后对 Bean
进行额外的处理。Bean
后置处理器对 IOC
容器里的所有 Bean
实例逐一处理, 而非单一实例。其典型应用是: 检查 Bean
属性的正确性或根据特定的标准更改 Bean
的属性.
@Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("postProcessAfterInitialization: " + bean + ", " + beanName); return bean; } @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("postProcessBeforeInitialization: " + bean + ", " + beanName); return bean; }
配置Bean:
<bean id="book" class="other.Book" init-method="init" destroy-method="destory"> <property name="name" value="NowCoder"></property></bean><bean class="other.MyBeanPostProcessor"></bean>
输出:
Book Constructor...Setting...postProcessBeforeInitialization: Book [name=NowCoder], bookInit...postProcessAfterInitialization: Book [name=NowCoder], bookBook [name=NowCoder]八月 18, 2017 4:10:38 下午 org.springframework.context.support.ClassPathXmlApplicationContext doClose信息: Closing org.springframework.context.support.ClassPathXmlApplicationContext@17f052a3: startup date [Fri Aug 18 16:10:36 CST 2017]; root of context hierarchyDestory...
结论:
Spring IOC
容器对Bean
的生命周期进行管理的过程:
- 通过构造器或工厂方法创建
Bean
实例 - 为
Bean
的属性设置值和对其他Bean
的引用 - 将
Bean
实例传递给Bean
后置处理器的postProcessBeforeInitialization
方法 - 调用
Bean
的初始化方法 - 将
Bean
实例传递给Bean
后置处理器的postProcessAfterInitialization
方法 Bean
可以使用了- 当容器关闭时, 调用
Bean
的销毁方法
- 装配Bean
- Bean装配
- 装配Bean
- 装配bean
- 装配Bean
- 装配bean
- 装配Bean
- 装配bean
- 装配Bean
- 装配bean
- Spring Bean装配-自动装配
- 装配Bean:JavaConfig@Bean@Configuration
- spring 基本Bean装配
- 1 装配Bean
- Spring Bean 初级装配
- Spring Bean 高级装配
- Ioc - bean自动装配
- bean装配过程
- struts2.5.12版本使用action通配符提示找不到action的问题
- java 并发
- 对MySQL中JSON数据类型的操作和分析
- HDU3068[最长回文]--Manacher
- 四、c++中的算法--变动性算法---赋值
- 装配Bean
- HDU 4561连续最大积(模拟题)
- 四、c++中的算法--变动性算法---替换
- 关于Clone克隆对象的讲解
- bzoj 2330(差分约束)
- 四、c++中的算法--变动性算法---逆转
- c# DataTime.toString()的显示精度问题
- hadoop初识之七:linux 中eclipse 读写HDFS文件
- UI 一一 自定义不等高cell (storyboard方式)