第四章 在 IoC 容器中装配 Bean

4.1 Spring 配置概述

4.1.1 Spring 容器高层视图
  • Spring容器成功启动的必备条件

    • Spring 框架的类包都已经放到应用程序的类路径下
    • 应用程序为 Spring 提供完备的 Bean 配置信息
    • Bean 的类都已经放到应用程序的类路径下
  • Bean 在Spring中加载的过程

    • 1、Bean 配置信息定义了 Bean 的实现及依赖关系
    • 2、Spring 容器根据各种形式的 Bean 配置信息在容器内部建立 Bean 定义注册表
    • 3、然后根据注册表加载、实例化 Bean,并建立 Bean 和 Bean 的依赖关系
    • 4、最后将这些准备就绪到 Bean 放到 Bean 缓存池中,以供外层的应用程序进行调用
4.1.2 基于 XML 的配置
  • xml 文件头中声明的内容
    • 默认命名空间: 它没有空间名,用于Spring Bean 的定义
    • xsi命名空间: 这个命名空间用于为每个文档中命名空间指定相应的 Schema 样式文件,是标准组织定义的标准命名空间
    • aop命名空间: 这个命名空间是Spring 配置 AOP 的命名空间,是用户自定义的命名空间。

4.2 Bean 基本配置

4.2.1 装配一个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-4.3.xsd">
	
	<bean id="car" class="com.baobaotao.simple.Car" />
</beans>
4.2.2 Bean的命名
  • 配置一个 Bean 时,需要指定一个 id 或 name 属性作为 Bean 的名称
    id 和 name 都可以指定多个名字,之间用逗号、分号或者空格进行分割
<bean id="car" class="com.baobaotao.simple.Car" />
<bean name="#car1,123,$car" class="com.baobaotao.simple.Car" />
<bean class="com.baobaotao.simple.Car" />
  • 通过getBean(“car”)、getBean("#car1")、getBean(“123”)、getBean("$car") 、getBean(“com.baobaotao.simple.Car”)获取 Bean
    注意:
  • Spring配置文件不允许出现两个相同 id 的 < bean >;却可以出现两个相同 name 的 Bean;通过 getBean(beanName) 获取最后声明的 Bean,原因是后面的 Bean 覆盖前面 Bean。 故,尽量使用 id 而非 name 命名的 Bean。
  • 若没有 id 、name,则默认全路径为 Bean 命名。

4.3 依赖注入

4.3.1 属性注入
package com.baobaotao.ditype;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class Car {
	private int maxSpeed;
	private String brand;
	private double price;
	
}
<?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-4.3.xsd">
	
	<bean id="car" class="com.baobaotao.ditype.Car">
		<property name="maxSpeed"><value>200</value></property>
		<property name="brand"><value>红旗CA72</value></property>
		<property name="price"><value>20000.00</value></property>
	</bean>
</beans>
package com.baobaotao.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.baobaotao.ditype.Car;

public class ApplicationBeansTest {
	
	public static void main(String[] args) {
		ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
	   Car car = (Car) context.getBean("car");
	   System.err.println(car);
		
	}
}

注意:成员变量的命名规则 驼峰命名、全小写、首字母不可大写

4.3.2 构造函数注入
package com.baobaotao.ditype;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class Car {
	private int maxSpeed;
	private String brand;
	private double price;
	private String iDcode;
	
	//测试1
	public Car(String brand, double price) {
		this.brand = brand;
		this.price = price;
	}
	
	//测试2
	public Car(String brand, String corp, double price) {
		this.brand = brand;
		this.corp = corp;
		this.price = price;
	}
	//测试3
	public Car(String brand, String corp, int maxSpeed) {
		this.brand = brand;
		this.corp = corp;
		this.maxSpeed = maxSpeed;
	}
}
package com.baobaotao.ditype;

public class Office {

}
package com.baobaotao.ditype;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class Boss {
	private String name;
	private Car car;
	private Office office;
	
	//测试4
	public Boss(String name, Car car, Office office) {
		this.name = name;
		this.car = car;
		this.office = office;
	}
}
<!-- 测试1 -->
<bean id="car" class="com.baobaotao.ditype.Car">
	<constructor-arg type="double">
		<value>20000</value>
	</constructor-arg>
	<constructor-arg type="String">
		<value>红旗CA72</value>
	</constructor-arg>
</bean>

<!-- 测试2 -->	
<bean id="car" class="com.baobaotao.ditype.Car">
	<constructor-arg index="0" value="红旗CA72"/>
	<constructor-arg index="1" value="中国一汽"/>
	<constructor-arg index="2" value="20000"/>
</bean>

<!-- 测试3 -->
<bean id="car" class="com.baobaotao.ditype.Car">
	<constructor-arg index="0" type="String" value="红旗CA72"/>
	<constructor-arg index="1" type="String" value="中国一汽"/>
	<constructor-arg index="2" type="double" value="20000"/>
</bean>

<!-- 测试4 -->
<bean id="boss" class="com.baobaotao.ditype.Boss">
	<constructor-arg>
		<value>John</value>
	</constructor-arg>
	<constructor-arg>
		<ref bean="car"/>
	</constructor-arg>
	<constructor-arg>
		<ref bean="office"/>
	</constructor-arg>
</bean>
<bean id="car" class="com.baobaotao.ditype.Car">
	<constructor-arg index="0" type="String" value="红旗CA72"/>
	<constructor-arg index="1" type="String" value="中国一汽"/>
	<constructor-arg index="2" type="double" value="20000"/>
</bean>
<bean id="office" class="com.baobaotao.ditype.Office"/>
  • 循环依赖问题
package com.baobaotao.ditype;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class Car {
	private String brand;
	private Boss boss;
	
	public Car() {}
	
	public Car(String beand, Boss boss) {
		this.brand = beand;
		this.boss = boss;
	}
}
package com.baobaotao.ditype;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class Boss {
	private String name;
	private Car car;
	
	public Boss(String name, Car car) {
		this.name = name;
		this.car = car;
	}
}
<bean id="car" class="com.baobaotao.ditype.Car">
	<constructor-arg index="0" value="红旗CA72"/>
	<constructor-arg index="1" ref="boss"/>
</bean>

<bean id="boss" class="com.baobaotao.ditype.Boss">
	<constructor-arg index="0" value="John"/>
	<constructor-arg index="1" ref="car"/>
</bean>

该种方法无法启动 Spring Ioc容器,因为循环依赖;解决方法:将构造函数注入方式调整为属性注入方式

4.3.3 工厂方法注入(大可不适用)
package com.baobaotao.ditype;

public class CarFactory {

	//非静态工厂方法
	public Car createhongQiCar() {
		Car car = new Car();
		car.setBrand("红旗CA72");
		return car;
	}
	
	//静态工厂方法
	public static Car createhongQiCarStatic() {
		Car car = new Car();
		car.setBrand("红旗CA72");
		return car;
	}
	
	public static void main(String[] args) {
		ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
		Car car = (Car) context.getBean("car5");
	   System.err.println(car);
	}
}

<!-- 动态工厂类配置 -->
<!-- @1工厂类Bean -->
<bean id="carFactory" class="com.baobaotao.ditype.CarFactory" />
<!-- factory-bean 指定@1处的工厂类Bean:factory-method 指定工厂类Bean 创建该 Bean 的工厂方法  -->
<bean id="car5" factory-bean="carFactory" factory-method="createhongQiCar"/>

<!-- 静态工厂类配置 -->
<bean id="car6" class="com.baobaotao.ditype.CarFactory" factory-method="createhongQiCarStatic"/>
4.3.4 选择注入方式的考量
  • 构造函数可以保证一些重要的属性在Bean实例化时就设置好,避免了因为一些重要属性没有提供,导致一个无用Bean实例的情况;
  • 不需要为每个属性提供Setter方法,减少了类的方法个数
  • 可以更好的封装变量,不需要为每个属性指定Setter方法,避免外部错误的调用。更多的开发者可能更倾向于使用属性注入方式,他们反对构造函数注入的理由是:
    • 如果一个类的属性众多,构造函数的签名将变成一个庞然大物,可读性很差;
    • 灵活性不强,在有些属性是可选的情况下,如果通过构造函数注入,也需要为可选的参数提供一个null值
    • 如果有多个构造函数,需要考虑配置文件和具体构造函数匹配歧义的问题,配置上相对复杂
    • 构造函数不利于类的继承和扩展,因为子类需要引用到父类复杂的构造函数

4.4 注入参数详解

4.4.1 字面值

所谓"字面值"一般是指可用字符串表示的值,这些值可以通过< value >元素标签进行注入。

XMl特殊处理标签

  • XML的5个特殊字符(&、<、>、"、’、。)
  • 处理方法
    • 以 <![CDATA[ ]]> 使XML解析器将标签中的字符串当作普通的文本对待
    • 使用XML转移序列特殊的符
特殊符号转义序列
<& lt;
>& gt;
&& amp;
"& quot;
& apos;

注意:一般情况下,XML解析器会忽略内部字符串的前后空格,但Spring却不忽略元素标签内部字符串的前后空格。

<bean id="car" class="com.baobaotao.ditype.Car">
	<property name="maxSpeed">
		<value>200</value>
	</property>
	<property name="brand">
		<!-- <value><![CDATA[红旗&CA72]]></value> -->
		<value>红旗&amp;CA72</value>
	</property>
</bean>
4.4.2 引用其他 Bean
  • < ref> 元素可以通过一下三个属性引用容器中其他Bean
    • bean: 通过该属性可以引用同一容器或父容器的 Bean,这是最常见的形式
    • local: 通过改属性只能引用统一配置文件中定义的 Bean,它可以利用XML解析器自动检验引用的合法性,以便在开发编写配置时能够及时发现并纠正配置的错误
    • parent: 引用父容器中的 Bean,如< ref parnet=“car”>的配置说明car 的 Band 是父容器的中的Bean

beans1.xml

<!-- 在父容器中定义的car -->
<bean id="car" class="com.baobaotao.ditype.Car">
	<property name="brand" value="红旗CA72"/>
	<property name="maxSpeed" value="200"/>
	<property name="price" value="20000.00"/>
</bean>

beans2.xml

<!-- 该Bean 和父容器的 car Bean 具有相同的id -->
<bean id="car" class="com.baobaotao.ditype.Car">
	<property name="brand" value="吉利CT5"/>
	<property name="maxSpeed" value="100"/>
	<property name="price" value="20000.00"/>
</bean>
<bean id="boss" class="com.baobaotao.ditype.Boss">
	<property name="car">
		<!-- <ref parent="car"/> 引用父容器中的car,而非同xml中定义的 Bean,如果采用< ref bean="car"/> 将应用本容器中的car -->
		<!-- <ref parent="car"/> -->
		<ref bean="car"/>
	</property>
</bean>
package com.baobaotao.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.baobaotao.ditype.Boss;
import com.baobaotao.ditype.Car;
import com.baobaotao.ditype.CarFactory;

public class ApplicationBeansTest {
	
	public static void main(String[] args) {
		//父容器
		ClassPathXmlApplicationContext pFactory = new ClassPathXmlApplicationContext(new String[] {"beans1.xml"});
		//指定pFactory为该容器的父容器
		ApplicationContext factory = new ClassPathXmlApplicationContext(new String[] {"beans2.xml"}, pFactory);
		
		Boss boss = (Boss) factory.getBean("boss");
		System.err.println(boss);
	}
}
4.4.3 内部 Bean
  • 如果 car Bean 只被 boss Bean 引用而不被容器中任何其他的 Bean引用,则可以将 car 以内部的方式注入到 Boss 中
<bean id="boss" class="com.baobaotao.ditype.Boss">
	<property name="car">
		<bean class="com.baobaotao.ditype.Car">
			<property name="maxSpeed" value="200"></property>
			<property name="price" value="2000.0"></property>
		</bean>
	</property>
</bean>
  • 内部 Bean 即使提供了 id、name、scope 属性,也会被忽略。即不能被其他 Bean 引用,只能在声明除为外部Bean提供实例注入
4.4.4 null值
  • 对 car 的 brand 属性注入一个 null 值
<bean id="car" class="com.baobaotao.ditype.Car">
	<property name="brand">
		<null/>
	</property>
</bean>
4.4.5 级联属性
package com.baobaotao.ditype;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class Boss {
	...
	private Car car = new Car();
}
<bean id="boss" class="com.baobaotao.ditype.Boss">
	<property name="car.brand" value="吉利CT50"></property>
</bean>
  • Spring 没有对级联属性的层级数进行限制,只要配置的 Bean拥有对应于级联属性的类结构,就可以配置任意层级的级联属性;定义了具有三级结构的级联属性。
4.4.6 集合类型属性

java.util 包中的集合类是最常用的数据结构类型,主要包括 List、Set、Map、Properties,Spring 为这些集合类型属性提供了专门的配置元素标签

  • List、Set、Map、Properties 示例
package com.baobaotao.ditype;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class Boss {
	private List<String> favorites = new ArrayList<String>();
	private Map<String, String> jobs = new HashMap<String, String>();
	private Set<String> hashSet = new HashSet<String>();
	private Properties mails = new Properties();
	
}
<bean id="boss" class="com.baobaotao.ditype.Boss">
	<property name="favorites">
		<list>
			<value>看报</value>
			<value>赛车</value>
			<value>高尔夫</value>
		</list>
	</property>
	<property name="jobs">
		<map>
			<entry>
				<key><value>AM</value></key>
				<value>会见客户</value>
			</entry>
			<entry>
				<key><value>PM</value></key>
				<value>公司内部会议</value>
			</entry>
		</map>
	</property>
	<property name="hashSet">
		<set>
			<value>牛</value>
			<value>娜</value>
		</set>
	</property>
	<property name="mails">
		<props>
			<prop key="jobMail">john-office@baobaotao.com</prop>
			<prop key="lifeMail">john-life@baobaotao.com</prop>
		</props>
	</property>
</bean>
  • 集合合并
<bean id="parentBoss" abstract="true" class="com.baobaotao.ditype.Boss">
	<property name="favorites">
		<list>
			<value>看报</value>
			<value>赛车</value>
			<value>高尔夫</value>
		</list>
	</property>
</bean>

<bean id="chiIdBoss" parent="parentBoss">
	<property name="favorites">
		<list merge="true">
			<value>爬山</value>
			<value>游泳</value>
		</list>
	</property>
</bean>
通过 util 命名空间配置集合类型的 Bean
  • 如果你希望配置一个集合类型的 Bean,而非一个集合类型的属性,则可以通过 util 命名空间进行配置。首先需要在 Spring 配置文件头中引入 util 命名空间的声明
<?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:util="http://www.springframework.org/schema/util"
     xsi:schemaLocation="http://www.springframework.org/schema/beans
     	http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
     	http://www.springframework.org/schema/util
     	http://www.springframework.org/schema/util/spring-util-3.0.xsd">
   
</beans> 
<util:list id="favoriteList1" list-class="java.util.LinkedList">
	<value>dream1</value>
	<value>dream1</value>
	<value>dream1</value>
</util:list>

<util:set id="favoriteSet1" set-class="java.util.HashSet">
	<value>set1</value>
	<value>set2</value>
</util:set>

<util:map id="favoriteMap1" map-class="java.util.HashMap">
	<entry>
		<key><value>key1</value></key>
		<value>value1</value>
	</entry>
	<entry>
		<key><value>key2</value></key>
		<value>value2</value>
	</entry>
</util:map>
4.4.7 简化配置方式
  • 字面值属性

    • 字面值属性 < property name=“maxSpeed” value=“200”>
    • 构造函数参数 < constructor-arg type=“java.lang.String” value=“红旗CA72”/>
    • 集合元素 < map>< entry key=“AM” value=“会见客户”/>
  • 引用对象属性

    • 字面值属性 < property name=“car” ref=“car”>
    • 构造函数参数 < constructor-arg ref=“car”/>
    • 集合元素 < map>< entry key-ref=“keyBean” value-ref=“valueBean”/>
  • 使用 p 命名空间

<?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:util="http://www.springframework.org/schema/util"
     xmlns:p="http://www.springframework.org/schema/p"
     xsi:schemaLocation="http://www.springframework.org/schema/beans
     	http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
     	http://www.springframework.org/schema/util
     	http://www.springframework.org/schema/util/spring-util-3.0.xsd">
     	
     	<bean id="car" class="com.baobaotao.ditype.Car" 
			p:brand="红旗&amp;CaA72"
			p:maxSpeed="200"
			p:price="20000.0"/>
		
</beans> 
4.4.8 自动装配

< bean>元素提供了一个指定自动装配类型的属性:autowire="<自动装配类型>" Spring 提供了4种自动装配类型

自动装配类型说明
byName根据名称进行自动匹配。假设 Boss 有一个名为 car 的属性,如果容器中刚好有一个名为 car 的 Bean ,Spring 就会自动将其装配给 Boss 的 car 属性
byType根据类型进行自动匹配。假设 Boss 有一个 Car 类型的属性,如果容器中刚好有一个 Car 类型的 Bean ,Spring 就会自动将其装配给 Boss 的 car 属性
constructor于 byType 类似,只不过它是针对构造函数注入而言的,如果Boss有一个构造函数,构造函数包含一个 Car 类型的入参,如果容器中有一个 Car 类型的 Bean,则Spring 将自动把这个 Bean 作为 Boss 构造函数的入参,如果容器中没有找到和构造函数入参匹配类型的Bean,Spring 将抛出异常
autodetect根据 Bean 的自省机制决定采用 byType 还是 constructor 进行自动装配:如果Bean提供了默认的构造函数,则采用 byType;否则采用constructor

4.5 方法注入

4.5.1 lookup 方法注入
package com.baobaotao.injectfun;

import com.baobaotao.ditype.Car;

public interface MagicBoss {

	Car getCar();
}
<bean id="car" class="com.baobaotao.ditype.Car" 
	p:brand="红旗&amp;CaA72"
	p:maxSpeed="200"
	p:price="20000.0" 
	scope="prototype"/>
	
<bean id="magicBoss" class="com.baobaotao.injectfun.MagicBoss">
	<lookup-method name="getCar" bean="car"/>
</bean>
package com.baobaotao.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.baobaotao.ditype.Boss;
import com.baobaotao.ditype.Car;
import com.baobaotao.ditype.CarFactory;
import com.baobaotao.injectfun.MagicBoss;

public class ApplicationBeansTest {
	
	public static void main(String[] args) {
		ApplicationContext factory = new ClassPathXmlApplicationContext("beans.xml");
		MagicBoss car = (MagicBoss) factory.getBean("magicBoss");
		System.err.println(car.getCar());
	}
}
4.5.1 方法替换
  • 调用方法 使 Boss2 中的 方法 reimplement() 方法,替换 Boos1 中的 getCar() 方法
package com.baobaotao.injectfun;

import com.baobaotao.ditype.Car;

public class Boss1 {

	public Car getCar() {
		Car car = new Car();
		car.setBrand("宝马Z4");
		return car;
	}
}

package com.baobaotao.injectfun;

import java.lang.reflect.Method;

import org.springframework.beans.factory.support.MethodReplacer;

import com.baobaotao.ditype.Car;

public class Boss2 implements MethodReplacer{

	public Object reimplement(Object arg0, Method arg1, Object[] arg2) {
		Car car = new Car();
		car.setBrand("美洲豹");
		return car;
	}
}

<bean id="boss1" class="com.baobaotao.injectfun.Boss1">
	<replaced-method name="getCar" replacer="boss2"/>
</bean>

<bean id="boss2" class="com.baobaotao.injectfun.Boss2"></bean>

4.6 < Bean >之间的关系

  • 不但可以通过 < ref > 引用另一个 Bean,建立起 Bean 和 Bean 之间的依赖关系,相似的,< bean >元素标签之间也可以建立类似的关系,完成一些特殊的功能
4.6.1 继承
  • 如果有多个类拥有相同的方法和属性,则我们可以引入一个父类,在父类中定义这些共同的方法和属性,以消除重复的代码。
<!-- 定义为抽象 bean -->
<bean id="abstractCar" class="com.baobaotao.ditype.Car"
	p:brand="红旗CaA72"
	p:maxSpeed="200"
	p:price="20000.0" 
	p:color="黑色"
	scope="prototype"/>
	
<!-- 继承于 abstractCar -->
<bean id="car3" p:color="红色" parent="abstractCar"/>
<bean id="car4" p:color="白色" parent="abstractCar"/>
4.6.2 依赖
  • 一般情况下,可以使用< ref >元素标签建立对其它 Bean 的依赖关系, Spring 负责管理这些 Bean 的关系,当实例化一个 Bean 时, Spring 保证该 Bean 所依赖的其他 Bean 已经初始化。
package com.baobaotao.entity;

public class SystemSettings {
	public static int SESSION_TIMEOUT = 30;
	public static int REFRESH_CYCLE = 60;	
}
package com.baobaotao.entity;

public class SysInit {

	public SysInit() {
		SystemSettings.SESSION_TIMEOUT = 10;
		SystemSettings.REFRESH_CYCLE = 100;
	}
}
package com.baobaotao.entity;

import java.util.Timer;
import java.util.TimerTask;

public class CacheManager {

	public void cacheManager() {
		System.err.println( SystemSettings.REFRESH_CYCLE);
	}
}
  • Spring 运行用户通过 depends-no 属性指定 Bean 前置依赖的 Bean,签之依赖的 Bean 会在本 Bean 实例化之前传值。如果前置依赖于多个Beans,则可以通过逗号,空格或分号的方式创建 Bean 的名称
<bean id="manager" class="com.baobaotao.entity.CacheManager" depends-on="sysInit" />
<bean id="sysInit" class="com.baobaotao.entity.SysInit" />
4.6.3 引用
  • Spring 提供了一个< idref >元素标签,通过该标签引用另一个< bean > 的名字。
<bean id="car" class="com.baobaotao.ditype.Car"
	p:color="红色"/>
<bean id="boss" class="com.baobaotao.ditype.Boss" p:car-ref="car" />

4.7 整合多个配置文件

  • 方法一:一个 XML 配置文件可以通过 < import> 组合多个外部的配置文件,resource 属性支持 Spring 标准的资源路径

  • beans1.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"
     xmlns:util="http://www.springframework.org/schema/util"
     xmlns:p="http://www.springframework.org/schema/p"
     xsi:schemaLocation="http://www.springframework.org/schema/beans
     	http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
     	http://www.springframework.org/schema/util
     	http://www.springframework.org/schema/util/spring-util-3.0.xsd">
	
	<bean id="car1" class="com.baobaotao.ditype.Car"
		p:brand="红旗CA72"
		p:color="红色"
		p:maxSpeed="200"
		p:price="20000.0" />
</beans>
  • beans2.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"
     xmlns:util="http://www.springframework.org/schema/util"
     xmlns:p="http://www.springframework.org/schema/p"
     xsi:schemaLocation="http://www.springframework.org/schema/beans
     	http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
     	http://www.springframework.org/schema/util
     	http://www.springframework.org/schema/util/spring-util-3.0.xsd">
	
	<import resource="classpath:beans1.xml"/>
	
	<bean id="car2" class="com.baobaotao.ditype.Car"
		p:brand="红旗CA72"
		p:color="红色"
		p:maxSpeed="200"
		p:price="20000.0"/>
	<bean id="boss1" class="com.baobaotao.ditype.Boss"
		p:car-ref="car1"/>
	<bean id="boss2" class="com.baobaotao.ditype.Boss"
		p:car-ref="car2"/>
</beans>

方法二:通过 new String[]{“beans1.xml”,“beans1.xml”}

package com.baobaotao.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.baobaotao.ditype.Boss;
import com.baobaotao.ditype.Car;

public class ApplicationBeansTest {
	
	public static void main(String[] args) {
		ApplicationContext factory = new ClassPathXmlApplicationContext(new String[] {"beans1.xml", "beans2.xml"});
		Car car1 = (Car) factory.getBean("car1");
		Boss boss2 = (Boss) factory.getBean("boss2");
		System.err.println(car1);
		System.err.println(boss2);
	}
}

4.8 Bean 作用域

类 别说 明
singleton在 Spring IoC 容器中仅存在一个 Bean 实例, Bean 以单实例的方式存在
prototype每次从容器中调用 Bean 时,都返回一个新的实例,即每次调用getBean()时,相当于执行 new XxxBean() 的操作
request每次 HTTP 请求都会创建一个新的 Bean。该作用域仅适用于 WebApplicationContext环境
session同一个 HTTP Session 共享一个
globalSession在 Spring IoC 容器中仅存在一个 Bean 实例, Bean 以单实例的方式存在
4.8.1 singleton 作用域

无状态或者状态不可变的类适合使用单实例模式;

  • @1 处的 car Bean 声明为 singleton (因为默认是 singleton,所以可以显示不指定),在容器中有 3 个其他的 Bean 引用了 car Bean,如 @2、@3、@3所示。在容器内部 boss1、boss2、boss3 的 car 属性都指向同一个Bean
  • 任何通过容器的 getBean(“car”) 方法返回的实例也指向同一个 Bean.
<bean id="car" class="com.baobaotao.ditype.Car"
	p:color="红色" scope="singleton"/>   @1
<bean id="boss1" class="com.baobaotao.ditype.Boss" p:car-ref="car" /> @2
<bean id="boss2" class="com.baobaotao.ditype.Boss" p:car-ref="car" /> @3
<bean id="boss3" class="com.baobaotao.ditype.Boss" p:car-ref="car" /> @4
  • Spring 的 ApplicationContext 容器在启动时,自动实例化所有singleton 的 Bean 并缓存于容器中。好出有二
    • 提前实例化,及早发现一些潜在的配置问题
    • Bean 以缓存的方式保存,当使用到该 Bean 时就无需再实例化,加快了效率。
  • 如果用户不希望再容器启动时提前实例化 singleton 的 Bean,可以通过lazy-init 属性控制
    • 当该 Bean 被其他需要提前实例化的 Bean 引用到,Spring 也将忽略延迟实例化的设置
<bean id="boss1" class="com.baobaotao.ditype.Boss" p:car-ref="car" lazy-init="true"/>
4.8.2 prototype 作用域
  • boss1、boss2、boss3 所引用的都是一个新的 car 实例,每次通过容器的 getBean(“car”) 方法返回的也是新的Car 实例
  • 默认Spring 容器启动时不实例化 prototype 的 Bean。此外,Spring 容器将 prototype 的 Bean 交给调用者后,就不再管理它的生命周期
<bean id="car" class="com.baobaotao.ditype.Car"
	p:color="红色" scope="prototype"/>   @1
<bean id="boss1" class="com.baobaotao.ditype.Boss" p:car-ref="car" /> @2
<bean id="boss2" class="com.baobaotao.ditype.Boss" p:car-ref="car" /> @3
<bean id="boss3" class="com.baobaotao.ditype.Boss" p:car-ref="car" /> @4
4.8.3 Web 应用环境相关的 Bean 作用域
  • 使用 Spring 的 WebApplicationContext,则还可以使用另外 3 种 Bean 的作用域:request、session和 globalSession。当然必须再 Web 容器中进行额外的配置。

  • Web 容器中进行配置

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
 ... 
  <listener>
  	<listener-class>
  		org.springframework.web.context.request.RequestContextListener
  	</listener-class>
  </listener>
</web-app>
  • 再整合 Spring 容器时使用 ContextLoaderListener,它实现了 ServletContextListener 接听接口,ServletContextListener 只负责接听 Web 容器启动和关闭的事件。
  • RequestContextListener 实现 ServletReqeustListener 监听器接口,该监听器监听 HTTP 请求事件, Web 服务器接收的每一次请求都会通知该监听器。
  • Spring 容器启动和关闭操作由 Web 容器的启动和关闭事件触发,但如果 Spring 容器中的 Bean 需要 request、sessios 和 globalSession 作用域的支持,Spring容器本身就必须获取 Web 容器的 HTTP 请求事件,以 HTTP 请求的事件“驱动” Bean作用域的控制逻辑。

request 作用域

  • request 作用域的 Bean 对应一个 HTTP 请求和生命周期
<bean id="car" class="com.baobaotao.ditype.Car" scope="request"/>
  • 每次 HTTP 请求调用到 car Bean 时,Spring 容器创建一个新的 car Bean,请求处理完毕后,销毁这个 Bean。

session作用域

<bean id="car" class="com.baobaotao.ditype.Car" scope="session"/>
  • car Bean 的作用域横跨整个 HTTP Session, Session 中所有 HTTP 请求都共享一个 car Bean,当 HTTP Session 结束后,实例才被销毁。

globalSession 作用域

<bean id="car" class="com.baobaotao.ditype.Car" scope="globalSession "/>
  • globalSession 作用域类似于 session 作用域,不过仅再 Portlet 的 Web 应用中使用。Portlet 规范定义了全局 Session 的概念,它被组成 portlet Web 应用的所有的子 Portet 共享。如果不在 Portet Web 应用环境下,globalSession 自然就等价于 session 作用域了。
4.8.4 作用域依赖问题
<?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:util="http://www.springframework.org/schema/util"
     xmlns:p="http://www.springframework.org/schema/p"
     xmlns:aop="http://www.springframework.org/schema/aop"<!-- 声明 aop Schema -->
     xsi:schemaLocation="http://www.springframework.org/schema/beans
     	http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
     	http://www.springframework.org/schema/util
     	http://www.springframework.org/schema/util/spring-util-3.0.xsd
     	http://www.springframework.org/schema/aop
     	http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
	
	<bean name="car" class="com.baobaotao.ditype.Car" scope="request">
		<aop:scoped-proxy/><!-- 创建代理 -->
	</bean>
	<bean id="boss" class="com.baobaotao.ditype.Boss">
		<property name="car" ref="car"></property><!-- 引用 web 相关作用域的 car Bean -->
	</bean>
</beans>
  • car Bean 是 request 作用域,他被 singleton 作用域的 boss Bean 引用。 为了 boss能够从适当作用域中获取 car Bean 的引用,需要使用 Spring AOP 的配置标签,则需要再文档声明头中定义 aop 命名空间。
  • 当 boss Bean 再 Web 环境下,调用 car Bean 时, Spring AOP 将启用动态dialing智能判断 boss Bean 位于那个HTTP请求线程中,并从对应的HTTP请求线程域中获取对应的 car Bean
  • boss Bean 的作用域时 singleton,再Spring 容器中始终只有有个实例,而 car Bean 的作用域为request,所以每个调用到 car Bean 的那些HTTP请求都会创建一个car Bean。Spring 通过动态代理技术,能够让 boss Bean 应用到对应HTTP 请求的 car Bean。

4.9 FactoryBean

  • Spring 通过反射机制利用< Bean > 的class 属性指定实现类实例化 Bean。

  • 从Spring 3.0 开始,FactoryBean 开始支持泛型,即接口声明改为 FactoryBean< T>的形式;该接口中定义了以下3个接口方法

    • T getObject() 返回由FactoryBean 创建的 Bean 实例, 如果 isSingleton() 返回 true,则该实例会放到Spring 容器的单实例缓存池中。
    • boolean isSingleton() 确定由 FactoryBean 创建的 Bean的作用域时 singleton 还是prototype;
    • Class<?> getObjectType() 返回 FactoryBean 创建 Bean 的类型。
package com.baobaotao.fb;

import org.springframework.beans.factory.FactoryBean;

import com.baobaotao.ditype.Car;

public class CarFactoryBean implements FactoryBean<Car>{

	private String carInfo;
	public String getCarInfo() {
		return carInfo;
	}
	
	//@1 接收逗号分割的属性设置信息
	public void setCarInfo(String carInfo) {
		this.carInfo = carInfo;
	}
	
	//@2 实例化 Car Bean
	@Override
	public Car getObject() throws Exception {
		Car car = new Car();
		String[] infos = carInfo.split(",");
		car.setBrand(infos[0]);
		car.setMaxSpeed(Integer.valueOf(infos[1]));
		car.setPrice(Double.valueOf(infos[2]));
		return car;
	}

	//@3 返回Car 的类型
	@Override
	public Class<Car> getObjectType() {
		return Car.class;
	}

	public boolean isSingleton() {
		return false;
	}
}
<bean name="car" class="com.baobaotao.fb.CarFactoryBean"
	p:carInfo="红旗CA72,200,20000.00"/>
package com.baobaotao.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.baobaotao.ditype.Car;
import com.baobaotao.fb.CarFactoryBean;

public class ApplicationBeansTest {
	
	public static void main(String[] args) throws Exception {
		
		ApplicationContext factory = new ClassPathXmlApplicationContext("beans.xml");
		//该方法返回 Car Bean对象  getBean("car')
		//Car cfb = (Car) factory.getBean("car");
		
		//若希望获取CarFactoryBean 自身的实例 则需再 beanName 前加 ‘&’   getBean("&car')
		CarFactoryBean cfb = (CarFactoryBean) factory.getBean("&car");
		System.err.println(cfb.getCarInfo());
		Car car = cfb.getObject();
		System.err.println(car);
		System.err.println(cfb.getObjectType());
		System.err.println(cfb.isSingleton());
	}
}

4.10 基于注解的配置

4.10.1 使用注解定义 Bean
  • @Component:
  • @Repository:用于对DAO 实现类进行标注
  • @Service:用于对Service 实现类进行标注
  • @Contoller:用于对Controller 实现类进行标注
4.10.2 使用注解配置信息启动Spring 容器
<?xml version="1.0" encoding="UTF-8"?>
<!-- @1 声明context 命名空间 -->
<beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:context="http://www.springframework.org/schema/context"
     xsi:schemaLocation="http://www.springframework.org/schema/beans
     	http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
     	http://www.springframework.org/schema/context
     	http://www.springframework.org/schema/context/spring-context-3.0.xsd">
	
	<!-- @2 扫描类包以应用注解定义的 Bean -->
	<context:component-scan base-package="com.baobaotao.ditype"/>
</beans>
  • 在@1 处声明context 命名空间,在@2 处即可通过context 命名空间的 component-scan 的base-package 属性指定一个扫描的基类包,Spring 容器将会扫描这个基类包里的所有类,并从类的注解信息中获取 Bean 的定义信息。
  • 如果仅希望扫描特定的类而非基包下的所有类,那么可以使用 resource-pattern 属性过滤出特定的类,如:
<context:component-scan base-package="com.baobaotao" resource-pattern="anno/*.class"/>
  • 这里我们将基包设置为 com.baobaotao,默认情况下 resource-pattern 属性的值为 “**/.class”,即基类包里的所有类。这里我们设置为 “anno/.class”,则Spring 仅会扫描基包里 anno 子包中的类。
  • 通过resource-pattern 属性仅可以按资源名称对几包中的类进行过滤,如果仅使用resource-pattern,你会发现很多时候它并不满足你的要求,如果仅过滤基类包中实现了XxxService 接口的类或标注了某个特定注解的类等。
    • 这些需求可以通过 < context:component-scan >的过滤子元素实现
    • < context:include-filter > 表示要包含的目标类,而< context:exclude-filter > 表示要排除在外的目标类。
<context:component-scan base-package="com.baobaotao.ditype" resource-pattern="anno/*.class"/>
<context:component-scan base-package="com.baobaotao">
	<context:include-filter type="regex" expression="com\.baobaotao\.anno.*"/>
	<context:exclude-filter type="aspectj" expression="com.baobaotao..*Controller+"/>
</context:component-scan>
类 别示例说 明
annotationcom.baobaotao.XxxAnnotation所有标注了 XxxAnnotation 的类。该类型采用目标类是否标注了某个注解进行过滤
assignablecom.baobaotao.XxxService所有继承或扩展 XxxService 的类。该类型采用目标类是否继承或扩展某个特定类进行过滤
aspectjcom.baobaotao…*Service+所有类名以 Service 结束的类及继承或扩展他们的类。该类型采用 AspectJ 表达式经写过滤
regexcom.baobaotao.anno…*所有com.baobaotao.anno 类包下的类。该类型采用正式表达式根据目标类的类名进行过滤
customcom.baobaotao.XxxTypeFilter采用 XxxTypeFile 通过代码的方式根据过滤规则。该类必须实现 org.springframework.core.type.TypeFilter 接口
4.10.3 自动装配 Bean
  • 使用@Autoeired 进行自动注入 其属性 required
package com.baobaotao.anno.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.baobaotao.anno.dao.LogDao;
import com.baobaotao.anno.entity.UserBo;

//@1 定义一个Service的Bean
@Service
public class LogonService {

	// @2 分别注入 LogDao 及 UserDao 的 Bean
	@Autowired(required=false)
	private LogDao logDao;
	
	public void logInUser() {
		UserBo user = new UserBo();
		System.out.println(user);
	}
}

  • @Autowired 默认按类型匹配的方式,在容器查找匹配的 Bean,当有且仅有一个匹配的 Bean 时,Spring 将其注入到@Autowired 标注的变量中
  • 如果容器中没有一个和标注变量类型匹配的 Bean,Spring 容器启动时将包 NoSuchBeanDefinitionException的异常。如果希望Spring 即使找不到匹配的 Bean 完成注入也不要报异常时,那么可以使用 @Autowired(required=false)

使用 @Qualifier 指定注入 Bean 的名称

  • 容器中有一个以上匹配的 Bean 时,则可以通过 Qualifier 注解限定 Bean 的名称
package com.baobaotao.anno.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import com.baobaotao.anno.dao.LogDao;
import com.baobaotao.anno.dao.UserDao;

@Service
public class LogonService {

	@Autowired(required=false)
	private LogDao logDao;
	
	// 注入名为userDao,类型为 UserDao 的 Bean
	@Autowired
	@Qualifier("userDao")
	private UserDao userDao;
}

对类方法进行标注

  • @Autowired 可以对成员变量以及方法的入参进行标注
package com.baobaotao.anno.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import com.baobaotao.anno.dao.LogDao;
import com.baobaotao.anno.dao.UserDao;

@Service
public class LogonService {
	private LogDao logDao;
	private UserDao userDao;
	
	//自动将LogDao 传给方法入参
	@Autowired
	public void setLogDao(LogDao logDao) {
		this.logDao = logDao;
		System.out.println("logDao:" +logDao);
	}
	
	//自动将名为 userDao 的 Bean 传给方法入参
	@Autowired
	@Qualifier("userDao")
	public void getUserInfo(UserDao userDao) {
		this.userDao = userDao;
		System.out.println("userDao:" + userDao);
	}

	//Spring 自动选择匹配入参类型的 Bean 进行注入
	@Autowired
	public void getUserInfo(UserDao userDao,  LogDao logDao) {
		this.userDao = userDao;
		System.out.println("userDao:" + userDao + " LogDao:" + logDao);
	}

	// 如果一个方法拥有多个入参,默认情况下,Spring 自动选择匹配入参类型的 Bean 进行注入, Sprig 允许对方法入参标注@Qualifier 以指定注入 Bean 的名称
	@Autowired
	public void getUserInfo(@Qualifier("userDao")UserDao userDao,  LogDao logDao) {
		this.userDao = userDao;
		System.out.println("userDao:" + userDao + " LogDao:" + logDao);
	}
}

对集合类进行标注

  • 如果对类中集合类的变量或方法入参进行@Autowired标注,Spring 会将容器中类型匹配的所有Bean 都自动注入进来
package com.baobaotao.anno.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class MyComponent {

	@Autowired(required=false)
	private List<Plugin> plugin;
	
	public List<Plugin> getPlugins(){
		return plugin;
	}
}
package com.baobaotao.anno.service;

public interface Plugin {
	
}
package com.baobaotao.anno.service.impl;

import org.springframework.stereotype.Component;

import com.baobaotao.anno.service.Plugin;

@Component
public class OnePlugin implements Plugin{

}
package com.baobaotao.anno.service.impl;

import org.springframework.stereotype.Component;

import com.baobaotao.anno.service.Plugin;

@Component
public class TwoPlugin implements Plugin{

}
4.10.4 Bean 作用范围及生命过程方法
  • 通过注解配置的 Bean 和通过 < bean >配置的 Bean 一样,默认的作用范围都是 singleton, Spring为注解配置提供了一个@Scope 的注解, 可通过它显示指定 Bean 的作用范围;
package com.baobaotao.ditype;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Scope("prototype")
@Component
public class Car {
	...
}
  • 使用< bean >进行配置时,我们可以通过 init-method 和 destory-method 属性指定 Bean 的初始化及容器销毁前执行的方法。
  • @PostConstruce 和 @PreDestroy注解,在Spring 中它们相当于 init-method 和 destory-method 属性的功能,不过使用注解时,可以在一个 Bean 中定义多个:
package com.baobaotao.anno.entity;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class Boss {
	private Car car;
	
	public Boss() {
		System.out.println("construce...");
	}

	@Autowired
	public void setCar(Car car) {
		System.out.println("execute in setCar");
		this.car = car;
	}
	
	@PostConstruct
	private void init1() {
		System.out.println("execute in init1");
	}
	
	@PostConstruct
	private void init2() {
		System.out.println("execute in init2");
	}
	
	@PreDestroy
	private void destory1() {
		System.out.println("execute in destory1");
	}
	
	@PreDestroy
	private void destory2() {
		System.out.println("execute in destory2");
	}
}
package com.baobaotao.anno;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {

	public static void main(String[] args) {
		ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
		((ClassPathXmlApplicationContext)ctx).destroy();
	}
}
  • 输出信息
    • construce …
    • execute in setCar
    • execute in init1
    • execute in init2
    • execute in destory1
    • execute in destory2
  • 说明 Spring 先调用 Boss 的构造函数示例化 Bean,再执行@Autowired 进行自动注入,然后分别执行标注了 @PostConstruct 的方法,当容器关闭时,则分别执行标注了 @PreDestroy 的方法

4.11 基于 Java 类的配置

4.11.1 使用 Java 类提供 Bean 定义信息
  • 普通的 POJO 只要标注 @Configuration 注解,就可以为 Spring 容器提供 Bean 定义的信息了,每个标注了 @Bean 的类方法都相当于提供一个Bean 的定义信息。
package com.baobaotao.conf;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.baobaotao.anno.dao.LogDao;
import com.baobaotao.anno.dao.UserDao;
import com.baobaotao.anno.dao.impl.LogDaoImpl;
import com.baobaotao.anno.dao.impl.UserDaoImpl;
import com.baobaotao.anno.service.LogonService;

//@1 将一个POJO标注为定义 Bean 的配置类
@Configuration
public class AppConf {

	//@2 以下两个方法定义了两个 Bean,并提供了 Bean 的示例化逻辑
	@Bean
	public UserDao userDao() {
		return new UserDaoImpl();
	}
	
	@Bean
	public LogDao logDao() {
		return new LogDaoImpl();
	}
	
	//@3 定义了 logonService 的 Bean
	@Bean
	public LogonService logonService() {
		LogonService logonService = new LogonService();
		//@4 将@2和@3处定义的 Bean 注入到logonService Bean 中
		logonService.setLogDao(logDao());
		logonService.getUserInfo(userDao(), logDao());
		return logonService;
	}
}
  • @1 处在AppConf 类的定义处标注了 @Configuration 注解,说明这个类可用于为Spring 提供Bean 的定义信息。类的方法处可以标注 @Bean 注解, Bean 的类型由方法返回值类型决定,名称默认和方法名相同,也可通过入参显示指定 Bean 名称,如@Bean(name=“userDao”)。之间在@Bean 所标注的方法中提供 Bean 的实例化逻辑。
  • 在@2 处 userDao() 和 logDao() 方法定义了一个 UserDao 和一个 LogDao的 Bean,他们的Bean名称分别是 userDao 和 logDao。 在@3 处,定义了一个logonService Bean,并且在@4处注入@2 处所定义的两个Bean。
package com.baobaotao.conf;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.baobaotao.anno.dao.LogDao;
import com.baobaotao.anno.dao.UserDao;
import com.baobaotao.anno.dao.impl.LogDaoImpl;
import com.baobaotao.anno.dao.impl.UserDaoImpl;

@Configuration
public class DaoConfig {

	@Bean
	public UserDao userDao() {
		return new UserDaoImpl();
	}
	
	@Bean
	public LogDao logDao() {
		return new LogDaoImpl();
	}
}
  • 由于 @Configuration 注解类本身已经标注了 @Component 注解,所以任何标注了 @Configuration 的类,本身也相当于标注了 @Component,即他们可以像普通的Bean 一样被注入到其他 Bean 中。DaoConfig 标注了 @Configuration 注解后就成为一个Bean,它可以被自动注入 ServiceConfig中:
package com.baobaotao.conf;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;

import com.baobaotao.anno.service.LogonService;

@Configuration
public class ServiceConfig {

	//@1 像普通 Bean 一样注入 DaoConfig
	@Autowired
	private DaoConfig daoConfig;
	
	public LogonService logonService() {
		LogonService logonService = new LogonService();
		//@2 像普通 Bean 一样,调用 Bean 相关的方法
		logonService.setLogDao(daoConfig.logDao());
		logonService.getUserInfo(daoConfig.userDao(), daoConfig.logDao());
		return logonService;
	}	
}
  • 调用 daoConfig 的 logDao() 和 userDao() 方法,就相当于将 DaoConfig 配置类中定义的 Bean 注入进来。Spring 会对配置类所有标注@Bean的方法进行AOP增强,将 daoConfig.userDao() 方法时,不是简单的执行 DaoConfig 类中定义的方法逻辑,而是从Spring 容器中返回相应 Bean 的单例。
  • 在@Bean 处,添加@Scope 注解以控制Bean 的作用范围。如果在@Bean 处标注了@Scope(“prototype”),则每次调用 daoConfig.logDao() 都会返回一个新的 LogDao Bean:
package com.baobaotao.conf;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;

import com.baobaotao.anno.dao.LogDao;
import com.baobaotao.anno.dao.impl.LogDaoImpl;

@Configuration
public class DaoConfig2 {

	@Scope("prototype")
	@Bean
	public LogDao logDao() {
		return new LogDaoImpl();
	}
}
4.11.2 使用基于 Java 类的配置信息启动 Spring 容器
  • 直接通过@Configuration 类启动Spring 容器
package com.baobaotao.anno;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import com.baobaotao.anno.service.LogonService;
import com.baobaotao.conf.AppConf;

public class JavaConfigTest {

	public static void main(String[] args) {
		/*  //测试方法一:
		ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConf.class);
		LogonService logonService = ctx.getBean(LogonService.class);
		logonService.printHello();*/
		
		//测试方法二:
		AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
		ctx.register(DaoConfig.class);
		ctx.register(ServiceConfig.class);
		ctx.refresh();
		LogonService logonService = ctx.getBean(LogonService.class);
		logonService.printHello();
	}
}
  • 测试二 方法通过代码一个一个注册配置类,也可以通过@Import将多个配置类组装到一个配置类中,这样仅需要注册这个组装好的配置类就可以启动容器了:
package com.baobaotao.conf;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

import com.baobaotao.anno.service.LogonService;

@Configuration
@Import(DaoConfig.class)
public class ServiceConfig {

	//@1 像普通 Bean 一样注入 DaoConfig
	@Autowired
	private DaoConfig daoConfig;
	
	@Bean
	public LogonService logonService() {
		LogonService logonService = new LogonService();
		//@2 像普通 Bean 一样,调用 Bean 相关的方法
		logonService.setLogDao(daoConfig.logDao());
		logonService.getUserInfo(daoConfig.userDao(), daoConfig.logDao());
		return logonServ
	}	
}
  • 通过 XML 配置文件引用 @Configuration 的配置
<?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:context="http://www.springframework.org/schema/context"
     xsi:schemaLocation="http://www.springframework.org/schema/beans
     	http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
     	http://www.springframework.org/schema/context
     	http://www.springframework.org/schema/context/spring-context-3.0.xsd">
	
	<!-- @1 通过上下文扫描加载到 AppConf 的配置类 -->
	<context:component-scan base-package="com.baobaotao.anno"
		resource-pattern="AppConf.class"/>
</beans>
  • 通过 Configuration 配置类引用 XML 配置信息
<bean id="logDao" class="com.baobaotao.anno.dao.impl.LogDaoImpl"/>
<bean id="userDao" class="com.baobaotao.anno.dao.impl.UserDaoImpl"/>
package com.baobaotao.conf;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;

import com.baobaotao.anno.dao.LogDao;
import com.baobaotao.anno.dao.UserDao;
import com.baobaotao.anno.service.LogonService;

@Configuration
@ImportResource("classpath:beans2.xml")
public class LogonAppConfig {

	@Bean
	@Autowired
	public LogonService logonService(UserDao userDao, LogDao logDao) {
		LogonService logonService = new LogonService();
		logonService.setLogDao(logDao);
		logonService.getUserInfo(userDao, logDao);
		return logonService;
	}
}

4.12 不同配置方式比较

基于 XML 配置基于注解配置基于Java 类配置
Bean 定义在 XML 文件中同通过 < bean > 元素定义 Bean 如:< bean class=“com.hm.UserDao” >在 Bean 实现类处通过标注 @component 或者衍型类(@Repository、@Service和@Controller)定义 Bean在标注了 @Configuration 的Java类中,通过在类方法上标注 @Bean 定义一个Bean。方法必须提供 Bean 的实例化逻辑
Bean 名称通过 < bean > 的id活name属性定义,如:< bean id=“userDao” class=“com.hm.UserDao” > 默认名称为:com.hm.UserDao#0通过注解的 value 属性定义,如 @Component(“userDao”)。默认名称为小写字母打头的类名(不带包名): userDao通过@Bean 的 name 属性定义,如 @Bean(“userDao”),默认名称为方法名
Bean 注入通过 < property > 子元素或通过 p 命名空间的动态属性,如 p:userDao-ref="userDao"进行注入通过在成员变量或方法入参处标注 @Autowired,按类型匹配自动注入。还可以配合使用 @Qualifief 按名称匹配方法注入比较灵活,可以通过在方法处通过 @Autowired 使方法入参绑定 Bean,然后在方法中通过代码注入,还可以同通过调用配置类的 @Bean 方法进行注入
Bean 生命过程方法通过 < bean > 的 init-method 和 destroy-method 属性指定 Bean 实现类的方法名。最多只能指定一个初始化反法和一个销毁方法通过在目标方法上标注 @PostConstruce 和 @preDestroy 注解指定初始化或销毁方法,可以定义任意多个方法通过 @Bean 的 initMethod 或 destoryMethod 指定一个初始化或销毁方法 对于初始化方法来说,可以直接在方法内部通过代码的方式灵活定义初始化逻辑
Bean 作用范围通过 < bean > 的 scope 属性指定,如:< bean class=“com.hm.UserDao” scope=“prototype” >通过在类定义处标注 @Scope 指定,如 @Scope(“prototype”)通过在Bean方法定义处标注 @Scope 指定
Bean 延迟初始化通过 < bean > 的 lazy-init 属性指定,默认为 default,继承于 < beans > 的default-lazy-init 设置,该值默认为 false通过在类定义处标注 @Lazy 指定,如 @Lazy(true)通过在 Bean 方法定义处标注 @lazy 指定
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值