一、Spring Bean的配置
由 Spring IoC 容器管理的对象称为 Bean,Bean 根据 Spring 配置文件中的信息被创建。所谓配置Bean就是告诉Spring的IOC容器将要去管理的对象。
1.传统方式
采用XML文件的形式进行配置,applicationContext.xml
Person类:
public class Person{
private String name;
private int age;
private double money;
一系列的构造、get()、set()方法等等.......
}
applicationContext.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标签:让Spring创建一个对象并放置在IOC容器内,一个bean标签就对应某个类的一个对象
标签属性:
id: 该Bean对象的唯一标识符,bean的名称,不可重复,在IOC容器中必须是唯一的,无论后面拆分配置文件还是使用注解,都要求id不能重复;若id没有指定,Spring自动将类的全局限定类名作为bean的名字;
class: 需要创建对象的类型的全局限定名,Spring通过反射机制创建该类的对象(要求:该类必须拥有无参构造方法)
-->
<bean id="person1" class="com.newcapec.bean.Person"/>
<bean id="person2" class="com.newcapec.bean.Person">
</beans>
BeanTest1测试类
public class BeanTest1 {
@Test
public void testBeanXML(){
//获得 Spring 中定义的 Bean 对象
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
//通过id名称,获取各自的实例对象
Person p1 = (Person) ac.getBean("person1");
Person p2 = (Person) ac.getBean("person2");
//测试p1和p2是不是指向同一个地址
System.out.println(p1 == p2);//false,每个Bean都各自对应一个对象
Person p3 = (Person) ac.getBean("person1");
System.out.println(p1 == p3);//true
}
}
2.Java 注解方式
3.Java 配置类方式
二、Spring Bean的实例化
1.通过构造方法实例化Bean
Spring IoC容器能使用默认的空构造方法,也能使用有参的构造方法,底层通过反射机制来实例化Bean对象。
2.通过工厂实例化Bean
- 调用静态工厂方法创建bean是将对象创建的过程封装到静态方法中。当客户端需要对象时,只需要简单地调用静态方法,而无需关心创建对象的细节;
- 要声明通过静态方法创建的bean,需要在bean的class属性里指定拥有该工厂的方法的类,同时在factory-method属性里指定工厂方法的名称。最后,使用元素为该方法传递方法参数;
Cat类:
public class Cat {
private String name;
private int age;
public Cat() {
}
public Cat(String name, int age) {
this.name = name;
this.age = age;
}
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;
}
@Override
public String toString() {
return "Cat{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
CatStaticFactory.java:
/**
* 一个用于创建cat对象的静态工厂
*/
public class CatStaticFactory {
/**
* 提供一个创建对象的静态方法
*/
public static Cat getInstance(String name, int age){
return new Cat(name, age);
}
}
applicationContext.xml:
<!-- 静态工厂-->
<bean id="cat1" class="com.newcapec.factory.CatStaticFactory" factory-method="getInstance">
<constructor-arg value="汤姆猫"/>
<constructor-arg value="2"/>
</bean>
3.通过FactoryBean实例化Bean
- Spring中有两种类型的bean,一种是普通bean,另一种是工厂bean,即FactoryBean;
- 工厂bean跟普通bean不同,其返回的对象不是指定类的一个实例,其返回的是该工厂bean的getObject方法所返回的对象;
CatFactoryBean.java:
public class CatFactoryBean implements FactoryBean<Cat> {
private String name;
private int age;
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
/**
* 获取对象
*/
@Override
public Cat getObject() throws Exception {
return new Cat(name, age);
}
/**
* 生成对象的Class类型
*/
@Override
public Class<?> getObjectType() {
return Cat.class;
}
/**
* 设置该对象是否为单例模式
*/
@Override
public boolean isSingleton() {
return true;
}
}
applicationContext.xml:
<!-- FactoryBean配置bean -->
<bean id="cat3" class="com.newcapec.factory.CatFactoryBean">
<property name="name" value="加菲猫"/>
<property name="age" value="5"/>
</bean>
4. 元素常用的属性或子元素
在 XML 配置的 元素中可以包含多个属性或子元素,常用的属性或子元素如下表所示:
三、Spring容器
IoC
思想是基于 IoC 容器
实现的,IoC 容器底层其实就是一个 Bean 工厂,Spring 框架为我们提供了两种不同类型的 IoC 容器,它们分别是 BeanFactory
和 ApplicationContext
。
1.BeanFactory
- BeanFactory 是 IoC 容器的基本实现,也是 Spring 提供的最简单的 IoC 容器。
- 它提供了 IoC 容器最基本的功能,由
org.springframework.beans.factory.BeanFactory
接口定义。 - BeanFactory 采用
懒加载(lazy-load)机制
,容器在加载配置文件时并不会立即创建对象,只有在获取(使用)这个对象时才会创建。
2.ApplicationContext
- ApplicationContext 是 BeanFactory 接口的子接口,是对 BeanFactory 的扩展。
- ApplicationContext 在 BeanFactory 的基础上增加了许多企业级的功能,例如 AOP(面向切面编程)、国际化、事务支持等。
3.ApplicationContext的主要实现类
ClassPathXmlApplicationContext
:从类路径上加载配置文件;FileSystemXmlApplicationContext
:从文件系统中加载配置文件;WebApplicationContext
:专门为Web应用而准备的,它允许从相对于Web根目录的路径中完成初始化工作;
4.从容器中获取Bean
getBean(String name)
方法,通过Bean的id名称从容器中获取Bean对象;getBean(Class requiredType)
方法,通过Bean的Class类型从容器中获取Bean对象;getBean(String name, Class requiredType)
方法,通过Bean的id和Class类型从容器中获取Bean对象;
注意:当IOC容器中存放着多个同类型对象时,不能通过Class类型来获取Bean对象。
BeanTest2测试类
public class BeanTest2 {
@Test
public void testGetBean() {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
//1.参数为字符串类型
Person p1 = (Person) ac.getBean("person1");
//2.参数为Class类型,缺点:当IOC容器中存在多个此类型对象时,抛出异常
Person person = ac.getBean(Person.class);
//3.参数为字符串类型+Class类型
Person p2 = ac.getBean("person2", Person.class);
}
}
四、依赖注入
1.基于属性注入
- 在 Spring 实例化 Bean 的过程中,IoC 容器首先会调用默认的构造方法(无参构造方法)实例化 Bean(Java 对象),然后通过 Java 的反射机制调用这个 Bean 的 setXxx() 方法,将属性值注入到 Bean 中。
- 使用 Setter 注入的方式进行属性注入,大致步骤如下:
(1)在 Bean 中提供一个默认的无参构造函数(如果没有带参构造函数),并为所有需要注入的属性提供一个 setXxx() 方法;
(2)在 Spring 的 XML 配置文件中,使用 及其子元素 对 Bean 进行定义;
(3)在 元素内使用 元素对各个属性进行赋值,使用name属性指定Bean的属性名称,value属性或子标签指定属性值。属性注入是实际应用中最常用的注入方式。
applicationContext.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">
<!-- 通过setter方法注入属性值 -->
<bean id="person" class="com.newcapec.bean.Person">
<!--property标签:表示通过属性的set方法为属性赋值,也叫做依赖注入
属性:
name: 对象中的属性名称
value: 属性值 -->
<property name="name" value="张三"/>
<property name="age" value="20"/>
<property name="money"><value>3600.5</value></property>
</bean>
</beans>
2.基于构造方法注入
- 使用构造函数实现属性注入大致步骤如下:
(1)在 Bean 中添加一个有参构造函数,构造函数内的每一个参数代表一个需要注入的属性;
(2)在Spring 的 XML 配置文件中,通过 及其子元素 对 Bean 进行定义;
(3)在 元素内使用 元素,对构造函数内的属性进行赋值,Bean 的构造函数内有多少个参数,就需要使用多少个 元素。 - Car类:
public class Car {
private String name;
private String type;
private double price;
private int doors;
public Car(String name, String type, double price, int doors) {
this.name = name;
this.type = type;
this.price = price;
this.doors = doors;
}
public Car(String name, String type, int doors) {
this.name = name;
this.type = type;
this.doors = doors;
}
public Car(String name, String type, double price) {
this.name = name;
this.type = type;
this.price = price;
}
public Car(String n, String t) {
this.name = n;
this.type = t;
}
@Override
public String toString() {
return "Car{" +
"name='" + name + '\'' +
", type='" + type + '\'' +
", price=" + price +
", doors=" + doors +
'}';
}
}
- applicationContext.xml:
注意:如果该类中有多个构造方法,可以通过index、type或name对构造方法进行精确选取。
<?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="car1" class="com.newcapec.bean.Car">
<!-- constructor-arg : 表示创建该类型的对象时,使用的构造方法的参数
属性:
value : 构造方法的参数值
index : 构造方法参数的索引
type : 构造方法参数的类型
name : 构造方法参数的名称 -->
<constructor-arg value="宝马"/>
<constructor-arg value="轿车"/>
<constructor-arg value="360000"/>
<constructor-arg value="4"/>
</bean>
<!-- 按索引匹配构造方法参数 -->
<bean id="car2" class="com.newcapec.bean.Car">
<constructor-arg value="越野" index="1"/>
<constructor-arg value="奔驰" index="0"/>
<constructor-arg value="4" index="3"/>
<constructor-arg value="560000" index="2"/>
</bean>
<!-- 按类型匹配构造方法参数 -->
<bean id="car3" class="com.newcapec.bean.Car">
<constructor-arg value="大众" type="java.lang.String"/>
<constructor-arg value="商务车" type="java.lang.String"/>
<constructor-arg value="290000" type="double"/>
</bean>
<!-- 按参数名称匹配构造方法参数-->
<bean id="car4" class="com.newcapec.bean.Car">
<constructor-arg value="电动车" name="t"/>
<constructor-arg value="特斯拉" name="n"/>
</bean>
</beans>
3.Autowire自动装配
- 我们把 Spring 在 Bean 与 Bean 之间建立依赖关系的行为称为“装配”。 Spring 的 IOC 容器虽然功能强大,但它本身不过只是一个空壳而已,它自己并不能独立完成装配工作。还需要我们主动将 Bean 放进去,并告诉它 Bean 和 Bean 之间的依赖关系,它才能按照我们的要求完成装配工作。
- Spring 的自动装配功能可以让 Spring 容器依据某种规则(自动装配的规则,有五种),为指定的 Bean 从应用的上下文(AppplicationContext 容器)中查找它所依赖的 Bean,并自动建立 Bean 之间的依赖关系。
- Spring 的自动装配功能能够有效地简化 Spring 应用的 XML 配置,因此在配置数量相当多时采用自动装配降低工作量。
- Spring 框架式默认不支持自动装配,要想使用自动装配,则需要对 Spring XML 配置文件中 元素的 autowire 属性进行设置。
- Spring 共提供了 五种自动装配规则,它们分别与 autowire 属性的 5 个取值对应,具体说明如下表:
五、Bean的作用域
- 默认情况下,所有的 Spring Bean 都是单例的,也就是说在整个 Spring 应用中,Bean 的实例只有一个。
- 我们可以在 元素中添加 scope 属性来配置 Spring Bean 的作用范围。例如,如果每次获取 Bean 时,都需要一个新的 Bean 实例,那么应该将 Bean 的 scope 属性定义为 prototype,如果 Spring 需要每次都返回一个相同的 Bean 实例,则应将 Bean 的 scope 属性定义为 singleton。
- Spring 5 共提供了 6 种 scope 作用域,如下表:
1.Singleton
singleton
是 Spring 容器默认的作用域。当 Bean 的作用域为 singleton 时,Spring IoC 容器中只会存在一个共享的 Bean 实例。这个 Bean 实例将存储在高速缓存中,所有对于这个 Bean 的请求和引用,只要 id 与这个 Bean 定义相匹配,都会返回这个缓存中的对象实例。- 如果一个 Bean 定义的作用域为 singleton ,那么这个 Bean 就被称为 singleton bean。在 Spring IoC 容器中,singleton bean 是 Bean 的默认创建方式,可以更好地重用对象,节省重复创建对象的开销。
- 在 Spring 配置文件中,可以使用 元素的 scope 属性,将 Bean 的作用域定义成 singleton,其配置方式如下所示:
<!-- singleton -->
<bean id="book" class="com.newcapec.bean.Book" p:id="101" p:name="西游记" p:price="98.5" p:author="吴承恩" scope="singleton"/>
2.Prototype
- 如果一个 Bean 定义的作用域为 prototype,那么这个 Bean 就被称为 prototype bean。对于 prototype bean 来说,Spring 容器会在每次请求该 Bean 时,都创建一个新的 Bean 实例。
- 从某种意义上说,Spring IoC 容器对于 prototype bean 的作用就相当于 Java 的 new 操作符。它只负责 Bean 的创建,至于后续的生命周期管理则都是由客户端代码完成的。
- 在 Spring 配置文件中,可以使用 元素的 scope 属性将 Bean 的作用域定义成 prototype,其配置方式如下所示:
<!-- prototype -->
<bean id="book" class="com.newcapec.bean.Book" p:id="101" p:name="西游记" p:price="98.5" p:author="吴承恩" scope="prototype"/>
六、Bean的生命周期
- 在传统的 Java 应用中,Bean 的生命周期很简单,使用 Java 关键字 new 进行 Bean 的实例化后,这个 Bean 就可以使用了。一旦这个 Bean 长期不被使用,Java 自动进行垃圾回收。
- Spring 中 Bean 的生命周期较复杂,SpringIOC容器可以管理Bean的生命周期,Spring允许在Bean生命周期的特定点执行定制的任务,
大致可以分为以下 5 个阶段:
Bean 的实例化
——>Bean 属性赋值
——>Bean 的初始化
——>Bean 的使用
——>Bean 的销毁
- Spring 根据 Bean 的作用域来选择 Bean 的管理方式,
(1) 对于 singleton 作用域的 Bean 来说,Spring IoC 容器能够精确地控制 Bean 何时被创建、何时初始化完成以及何时被销毁;
(2) 对于 prototype 作用域的 Bean 来说,Spring IoC 容器只负责创建,然后就将 Bean 的实例交给客户端代码管理,Spring IoC 容器将不再跟踪其生命周期。
1. Spring的生命周期流程
Spring Bean 的完整生命周期从创建 Spring IoC 容器开始,直到最终 Spring IoC 容器销毁 Bean 为止,其具体流程如下图所示。
2.Bean 生命周期的整个执行过程
- Spring 启动,查找并加载需要被 Spring 管理的 Bean,对 Bean 进行实例化。
- 对 Bean 进行属性注入。
- 如果 Bean 实现了
BeanNameAware
接口,则 Spring 调用 Bean 的 setBeanName() 方法传入当前 Bean 的 id 值。 - 如果 Bean 实现了
BeanFactoryAware 接
口,则 Spring 调用 setBeanFactory() 方法传入当前工厂实例的引用。 - 如果 Bean 实现了
ApplicationContextAware
接口,则 Spring 调用 setApplicationContext() 方法传入当前 ApplicationContext 实例的引用。 - 如果 Bean 实现了
BeanPostProcessor
接口,则 Spring 调用该接口的预初始化方法 postProcessBeforeInitialzation() 对 Bean 进行加工操作,此处非常重要,Spring 的 AOP 就是利用它实现的。 - 如果 Bean 实现了
InitializingBean
接口,则 Spring 将调用 afterPropertiesSet() 方法。 - 如果在配置文件中通过 init-method 属性指定了初始化方法,则调用该初始化方法。
- 如果 BeanPostProcessor 和 Bean 关联,则 Spring 将调用该接口的初始化方法 postProcessAfterInitialization()。此时,Bean 已经可以被应用系统使用了。
- 如果在 中指定了该 Bean 的作用域为 singleton,则将该 Bean 放入 Spring IoC 的缓存池中,触发 Spring 对该 Bean 的生命周期管理;如果在 中指定了该 Bean 的作用域为 prototype,则将该 Bean 交给调用者,调用者管理该 Bean 的生命周期,Spring 不再管理该 Bean。
- 如果 Bean 实现了 DisposableBean 接口,则 Spring 会调用 destory() 方法销毁 Bean;如果在配置文件中通过 destory-method 属性指定了 Bean 的销毁方法,则 Spring 将调用该方法对 Bean 进行销毁。