Spring学习(二)| Bean配置

1. 什么是IoC

控制反转:控制权的转移,应用程序本身不负责依赖对象的创建和维护,而是由外部容器负责创建和维护
依赖注入:指在启动Spring容器(IoC)加载bean配置的时候,完成对变量的赋值行为

  1. 找IoC容器
  2. 容器返回对象
  3. 使用对象

2. 配置Bean

2.1 配置形式

2.1.1 基于XML文件的形式

(文章下面内容中,有关于配置方法的均是使用xml配置文件的形式进行配置)

2.1.2 基于注解的方式

2.1.2.1 组件扫描
  • 组件扫描(component scanning): Spring 能够从 classpath 下自动扫描, 侦测和实例化具有特定注解的组件.
  • 特定组件包括:
注解 作用
@Component 基本注解, 标识了一个受 Spring 管理的组件
@Respository 标识持久层组件
@Service 标识服务层(业务层)组件
@Controller 标识表现层组件
  • 对于扫描到的组件, Spring 有默认的命名策略: 使用非限定类名, 第一个字母小写(如类名“UserService”,则扫描到bean名字为“userService”). 也可以在注解中通过 value 属性值标识组件的名称

  • 当在组件类上使用了特定的注解之后, 还需要在 Spring 的配置文件中声明 <context:component-scan>(需要使用 context 命名空间) :

    1. base-package 属性指定一个需要扫描的基类包,Spring 容器将会扫描这个基类包里及其子包中的所有类.
    2. 当需要扫描多个包时, 可以使用逗号分隔.
    3. 如果仅希望扫描特定的类而非基包下的所有类,可使用 resource-pattern 属性过滤特定的类
    4. <context:include-filter> 子节点表示要包含的目标类
    5. <context:exclude-filter> 子节点表示要排除在外的目标类
    6. <context:component-scan> 下可以拥有若干个 <context:include-filter><context:exclude-filter> 子节点

    Eg:

    <!-- 指定 Spring IOC 容器扫描的包 -->
    <context:component-scan base-package="com.spring.beans.XXX"></context:component-scan>
    
2.1.2.2 组件装配
  • @Autowired 注解自动装配具有兼容类型的单个 Bean 属性

    1. 构造器, 普通字段(即使是非 public), 一切具有参数的方法都可以应用@Authwired 注解
    2. 默认情况下, 所有使用 @Authwired 注解的属性都需要被设置(若是其他的类的话应该被配置成 bean ). 当 Spring 找不到匹配的 Bean 装配属性时, 会抛出异常, 若某一属性允许不被设置, 可以设置 @Authwired 注解的 required 属性为 false(此时,若是打印该没配置的类的话,结果为null)
    3. 默认情况下, 当 IOC 容器里存在多个类型兼容的 Bean 时, 通过类型的自动装配将无法工作. 此时可以在 @Qualifier 注解里提供 Bean 的名称. Spring 允许对方法的入参标注 @Qualifiter 已指定注入 Bean 的名称
    @Component("XXXXX")		//此处可以修改注册到 IOC容器中的 Bean 名称,默认:person
    public class Person {
    
    	@Autowired
    	@Qualifier("XXX")	//此处可以要求 需要注入的bean的名称
    	private Car car;
    	
    	public void driver() {
    		System.out.println("Person driver a car...");
    		car.run();
    	}
    }
    

2.2 Bean的配置方式

2.2.1 通过全类名(反射)

<!-- 
	配置bean  
	class: bean 的全类名,通过反射的方式在 IOC 容器中创建 Bean, 所以要求 Bean 中必须有无参数的构造器
	id: 标识容器中的 Bean, id,唯一
-->
<bean id="car" class="com.spring.demo.Car">
	<property name="name" value="Audi"></constructor-arg>
</bean>

2.2.2 静态工厂方法

  • 调用静态工厂方法创建 Bean是将对象创建的过程封装到静态方法中. 当客户端需要对象时, 只需要简单地调用静态方法, 而不同关心创建对象的细节.
  • 要声明通过静态方法创建的 Bean, 需要在 Bean 的 class 属性里指定拥有该工厂的方法的类, 同时在 factory-method 属性里指定工厂方法的名称. 最后, 使用 <constrctor-arg> 元素为该方法传递方法参数.

StaticCarFactory.java

/**
 * 静态工厂方法:直接调用某一个类的静态工厂方法就可以返回 Bean 的实例
 *
 */
public class StaticCarFactory {
	private static Map<String, Car> cars = new HashMap<String, Car>();
	
	static {
		cars.put("Audi", new Car("Audi", 200));
		cars.put("Ford", new Car("Ford", 300));
	}
	
	//静态工厂方法
	public static Car getCar(String name) {
		return cars.get(name);
	}
}

beans-factory.xml

<!-- 通过静态工厂方法来配置 bean, 注意不是配置静态工厂方法实例,而是配置 bean 实例 -->
<!-- 
	class 属性: 指向静态工厂方法的全类名
	factory-method: 指向静态工厂方法的名字
	constructor-arg: 如果工厂方法需要传入参数,则使用 constructor-arg 来配置参数
	-->
<bean id="car1"
	class="com.spring.beans.factory.StaticCarFactory"
	factory-method="getCar">
	<constructor-arg value="Audi"></constructor-arg>
</bean>

2.2.3 实例工厂方法

  • 实例工厂方法: 将对象的创建过程封装到另外一个对象实例的方法里. 当客户端需要请求对象时, 只需要简单的调用该实例方法而不需要关心对象的创建细节.
  • 要声明通过实例工厂方法创建的 Bean
    1. 在 bean 的 factory-bean 属性里指定拥有该工厂方法的 Bean
    2. factory-method 属性里指定该工厂方法的名称
    3. 使用 construtor-arg 元素为工厂方法传递方法参数

InstanceCarFactory.java

/**
 * 实例工厂方法:实例工厂的方法,即现需要创建工厂本身,再调用工厂的实例方法来返回 bean 的实例
 *
 */
public class InstanceCarFactory {
	
	private Map<String, Car> cars = null;

	public InstanceCarFactory() {
		cars = new HashMap<String, Car>();
		cars.put("Audi", new Car("Audi", 200));
		cars.put("Ford", new Car("Ford", 300));
	}
	
	public Car getCar(String name) {
		return cars.get(name);
	}
	
}

beans-factory.xml

<!-- 配置工厂的实例 -->
<bean id="instanceCarFactory" class="com.spring.beans.factory.InstanceCarFactory"></bean>

<!-- 通过实例工厂方法来配置 bean -->
<!-- 
	factory-bean : 指向实例工厂方法的bean
	factory-method: 指向静态工厂方法的名字
	constructor-arg: 如果工厂方法需要传入参数,则使用 constructor-arg 来配置参数
	-->
<bean id="car2" factory-bean="instanceCarFactory" factory-method="getCar">
	<constructor-arg value="Ford"></constructor-arg>
</bean>

2.2.4 通过 FactoryBean 配置

  • Spring 中有两种类型的 Bean, 一种是普通Bean, 另一种是工厂Bean, 即FactoryBean.
  • 工厂 Bean 跟普通Bean不同, 其返回的对象不是指定类的一个实例, 其返回的是该工厂 Bean 的 getObject 方法所返回的对象

CarFactoryBean.java

// 自定义的 FactoryBean 需要实现 FactoryBean 接口
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, 500);
	}

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

	
	@Override
	public boolean isSingleton() {
		return true;
	}
}

beans-beanfactory.xml

<!--
	通过 FactoryBean 来配置 Bean 的实例
	class: 指向 FactoryBean 的全类名
	property: 配置 FactoryBean 的属性 
	
	但实际返回的实例确实 FactoryBean 的 getObject() 方法返回的实例!
	-->
<bean id="car" class="com.spring.beans.factorybean.CarFactoryBean">
	<property name="brand" value="BMW"></property>
</bean>

2.3 IOC 容器 BeanFactory & ApplicationContext 概述

  • 在 Spring IOC 容器读取 Bean 配置创建 Bean 实例之前, 必须对它进行实例化. 只有在容器实例化后, 才可以从 IOC 容器里获取 Bean 实例并使用.

  • Spring 提供了两种类型的 IOC 容器实现.

    1. BeanFactory: IOC 容器的基本实现.
    2. ApplicationContext: 提供了更多的高级特性. 是 BeanFactory 的子接口.
  • BeanFactory 是 Spring 框架的基础设施,面向 Spring 本身;ApplicationContext 面向使用 Spring 框架的开发者,几乎所有的应用场合都直接使用 ApplicationContext 而非底层的 BeanFactory,无论使用何种方式, 配置文件时相同的.

2.4 从IOC 容器中获取Bean实例

// 利用 id 定位到 IOC 容器中的 Bean
HelloWorld helloWorld = (HelloWorld) ctx.getBean("helloWorld");

//利用类型返回 IOC 容器中的 Bean, 但要求 IOC 容器必须只能有一个该类型的 Bean
HelloWorld helloWorld = ctx.getBean(HelloWorld.class);

2.5 依赖注入的方式

2.5.1 属性注入

属性注入: 即通过 setter() 方法注入Bean 的属性值或依赖的对象

  • 属性注入使用 <property> 元素, 使用 name 属性指定 Bean 的属性名称,value 属性或 <value> 子节点指定属性值
  • 属性注入是实际应用中最常用的注入方式
<bean id="helloWorld" class="com.spring.demo.HelloWorld">
	<property name="name" value="Spring"></property>
</bean>

2.5.2 构造器注入

  • 通过构造方法注入Bean 的属性值或依赖的对象,它保证了 Bean 实例在实例化后就可以使用。
  • 构造器注入在 元素里声明属性, 中没有 name 属性
  • 按索引匹配入参
<bean id="car" class="com.spring.demo.Car">
	<constructor-arg value="Audi" index="0"></constructor-arg>
	<constructor-arg value="ShangHai" index="1"type="java.Lang.String"></constructor-arg>
	<constructor-arg value="200000" index="2"></constructor-arg>
</bean>
  • 按类型匹配入参
<bean id="car" class="com.spring.demo.Car">
	<constructor-arg value="Audi" type="java.Lang.String"></constructor-arg>
	<constructor-arg value="ShangHai" type="java.Lang.String"></constructor-arg>
	<constructor-arg value="200000" type="double"></constructor-arg>
</bean>

2.6 注入属性值的细节

2.6.1 字面值

  • 可用字符串表示的值,可以通过 <value> 元素标签或 value 属性进行注入。
  • 若字面值中包含特殊字符,可以使用 <![CDATA[]]> 把字面值包裹起来。

2.6.2 引用其它 Bean

  • 可以使用 propertyref 属性建立 bean 之间的引用关系

2.6.3 内部 Bean

  • 当 Bean 实例仅仅给一个特定的属性使用时, 可以将其声明为内部 Bean. 内部 Bean 声明直接包含在<property> 或 元素里, 不需要设置任何 id 或 name 属性
  • 内部 Bean 不能使用在任何其他地方

2.6.4 集合属性

2.6.4.1 List
  • 在 Spring中可以通过一组内置的 xml 标签(例如: <list>, <set> 或 ) 来配置集合属性
  • 数组的定义和 List 一样, 都使用 <list>
<bean id="person" class="com.spring.demo.Person">
	<property name="cars">
		<list>
			<ref bean="car"/>
			<ref bean="car2"/>
			<ref bean="car3"/>
		</list>
	</property>
</bean>
2.6.4.2 Map
  • Java.util.Map 通过 标签定义, <map> 标签里可以使用多个 <entry> 作为子标签. 每个条目包含一个键和一个值. 必须在 <key> 标签里定义键
  • 简单常量使用 key 和 value 来定义; Bean 引用通过 key-ref 和 value-ref 属性定义
<!-- 配置 Map 属性值 -->
<bean id="person" class="com.spring.demo.Person">
	<property name="name" value="Rose"></property>
	<property name="age" value="28"></property>
	<property name="cars">
		<map>
			<entry key="A" value-ref="car1">
			<entry key="B" value-ref="car2">
			<entry key="C" value-ref="car3">
		<map/>
	</property>

</bean>
2.6.4.3 Properties
  • 使用 <props> 定义 java.util.Properties, 该标签使用多个 <prop> 作为子标签. 每个 <prop> 标签必须定义 key 属性
<!-- 配置 Properties 属性值 -->
<bean id="dataSource" class="com.spring.demo.DataSource">
	<property name="properties">
		<props>
			<prop key="user">root</prop>
			<prop key="passwd">123456</prop>
			<prop key="jdbcUrl">jdbc:mysql://test</prop>
			<prop key="driverClass">com.mysql.jdbc.Driver</prop>
		</props>
	</property>

</bean>
2.6.4.4 使用 utility scheme 定义集合
  • 使用基本的集合标签定义集合时, 不能将集合作为独立的 Bean 定义, 导致其他 Bean 无法引用该集合, 所以无法在不同 Bean 之间共享集合.
  • 注意:需要导入 util 命名空间
<!-- 声明集合类型的 bean -->
<util:list id="cars">
	<ref bean="car"/>
	<ref bean="car2"/>
</util:list>

<bean id="user2" class="com.atguigu.spring.helloworld.User">
	<property name="userName" value="Rose"></property>
	<!-- 引用外部声明的 list -->
	<property name="cars" ref="cars"></property>
</bean>
2.6.4.5 使用 p 命名空间
  • 注意:需要导入 p 命名空间
<!-- 原来版本 -->
<bean id="user3" class="com.atguigu.spring.helloworld.User">
	<property name="cars" ref="cars"></property>
	<property name="userName" value="Titannic"></property>
</bean>

<!-- 使用 p 命名空间 -->
<bean id="user3" class="com.atguigu.spring.helloworld.User"
	p:cars-ref="cars" 
	p:userName="Titannic">
</bean>

2.7 自动装配(autowire属性)

  • Spring IOC 容器可以自动装配 Bean. 需要做的仅仅是在 <bean> 的 autowire 属性里指定自动装配的模式
  • byType(根据类型自动装配): 若 IOC 容器中有多个与目标 Bean 类型一致的 Bean. 在这种情况下, Spring 将无法判定哪个 Bean 最合适该属性, 所以不能执行自动装配.
  • byName(根据名称自动装配): 必须将目标 Bean 的名称和属性名设置的完全相同.
<bean id="car" class="com.spring.autowire.Car"
	p:brand="Audi"
	p:price="200000"
></bean>

<bean id="person" class="com.spring.autowire.Person"
	p:name="Tom"
	autowire="byName"
></bean>

2.8 bean 之间的关系:继承;依赖

2.8.1 继承

  • 若只想把父 Bean 作为模板, 可以设置 <bean> 的 abstract 属性为 true, 这样 Spring 将不会实例化这个 Bean, 只能用来被继承配置
  • 抽象 Bean 可以不指定 Class 属性
<bean id="address" class="com.spring.beans.relation.Address"
	p:city="BeiJing" p:street="WuDaoKou"
></bean>

<!-- 继承父 Bean 的配置 -->
<bean id="address2" class="com.spring.beans.relation.Address"
	parent="address" p:street="DaKiSi"
></bean>

2.8.2 依赖

  • Spring 允许用户通过 depends-on 属性设定 Bean 前置依赖的Bean,前置依赖的 Bean 会在本 Bean 实例化之前创建好
  • 如果前置依赖于多个 Bean,则可以通过逗号,空格或的方式配置 Bean 的名称
<!-- 要求配置Persion时,必须有一个关联的 car, 换句话说 person 这个 bean 依赖于 Car 这个 Bean -->
<bean id="car" class="com.spring.beans.relation.Car"
	p:price="200000"
></bean>

<bean id="address2" class="com.spring.beans.relation.Address"
	p:name="Tom" depends-on="car"
></bean>

2.9 bean 的作用域(scope 属性)

  • 在 Spring 中, 可以在 <bean> 元素的 scope 属性里设置 Bean 的作用域.
  • 默认情况下, Spring 只为每个在 IOC 容器里声明的 Bean 创建唯一一个实例, 整个 IOC 容器范围内都能共享该实例:所有后续的 getBean() 调用和 Bean 引用都将返回这个唯一的 Bean 实例.该作用域被称为 singleton, 它是所有 Bean 的默认作用域.
  • singleton(默认值):容器初始化时创建了实例 bean 实例
  • prototype:在每次请求调用 getBean() 时,才创建一个新的实例

2.9 使用外部属性文件

  • 在配置文件里配置 Bean 时, 有时需要在 Bean 的配置里混入系统部署的细节信息(例如: 文件路径, 数据源配置信息如用户密码等). 而这些部署细节实际上需要和 Bean 配置相分离
  • Spring 提供了一个 PropertyPlaceholderConfigurerBeanFactory 后置处理器, 这个处理器允许用户将 Bean 配置的部分内容外移到属性文件中. 可以在 Bean 配置文件里使用形式为 ${var} 的变量, PropertyPlaceholderConfigurer 从属性文件里加载属性, 并使用这些属性来替换变量

Eg:

<!-- 导入属性文件 : 注意需要使用contex命名空间-->
<context:property-placeholder location="classpath:db.properties"/>

<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>

db.properties属性文件:

user=root
password=123456
driverClass=com.mysql.jdbc.Driver
jdbcUrl=jdbc:mysql:///test

2.10 IOC 容器中 Bean 的生命周期

2.10.1正常情况的生命周期

  • Spring IOC 容器可以管理 Bean 的生命周期, Spring 允许在 Bean 生命周期的特定点执行定制的任务.
  • Spring IOC 容器对 Bean 的生命周期进行管理的过程:
    1. 通过构造器或工厂方法创建 Bean 实例
    2. 为 Bean 的属性设置值和对其他 Bean 的引用
    3. 调用 Bean 的初始化方法
    4. Bean 可以使用了
    5. 当容器关闭时, 调用 Bean 的销毁方法
  • 在 Bean 的声明里设置 init-methoddestroy-method 属性, 为 Bean 指定初始化和销毁方法.
<bean id="car" class="com.spring.beans.cycle.Car"
	init-method="init"
	destroy-method="destroy">
	<property name="brand" value="Audi"></property>
</bean>

2.10.2 添加 Bean 后置处理器

  • 实现 BeanPostProcessor 接口 中相应的方法
  • 配置配置 bean 的后置处理器,不需要配置 id, IOC 容器自动识别是一个 BeanPostProcessor
  • Spring IOC 容器对 Bean 的生命周期进行管理的过程:
    1. 通过构造器或工厂方法创建 Bean 实例
    2. 为 Bean 的属性设置值和对其他 Bean 的引用
    3. 将 Bean 实例传递给 Bean 后置处理器的 postProcessBeforeInitialization 方法
    4. 调用 Bean 的初始化方法
    5. 将 Bean 实例传递给 Bean 后置处理器的 postProcessAfterInitialization 方法
    6. Bean 可以使用了
    7. 当容器关闭时, 调用 Bean 的销毁方法

猜你喜欢

转载自blog.csdn.net/ljcaidn/article/details/83420979
今日推荐