4 bean详解
4.1 bean作用域
引入:在Spring的IOC容器中,bean的创建方式默认采用单例模式;也就是说,创建再多的bean,也是同一个bean。
验证:
新建一个Student类
package beanDemo; public class Student { private String name; private Integer age; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } }
书写配置文件
<?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="Student" class="beanDemo.Student"></bean> </beans>
新建一个StudentDemo测试类
import beanDemo.Student; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class StudentDemo { @Test public void testStudent(){ ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml"); Student student1 = ioc.getBean(Student.class); Student student2 = ioc.getBean(Student.class); System.out.println(student1); System.out.println(student2); } }
说明:在Spring中可以通过配置bean标签的scope书写来指定bean的作用域范围,各取值含义如下表:
取值 | 含义 | 创建对象的时机 |
---|---|---|
singleton | 在IOC容器中,这个bean的对象始终为单实例 | IOC容器创建初始化时 |
prototype | 这个bean在IOC容器中有多个实例 | 获取bean时 |
提示:如果实在WebApplicationContext环境下还会有另外两个作用域,但不常用。
取值 | 含义 |
---|---|
request | 在一个请求范围内有效 |
session | 在一个会话范围内有效 |
4.2 bean的生命周期
生命周期:从创建到消亡的完整过程
bean生命周期:bean从创建到销毁的整体过程
说明:学过Vue3或者Servlet都能理解什么是生命周期;对于Vue3来说,生命周期钩子就是生命周期控制最重要的手段。对于bean的生命周期来说,大体可分为如下几个阶段:
- bean对象创建(调用无参构造器)
- 给bean对象设置属性
- bean对象初始化之前操作(由bean的后置处理器负责)
- bean对象初始化(需在配置bean时指定初始化方法)
- bean对象初始化之后操作(由bean的后置处理器负责)
- bean对象就绪可以使用
- bean对象销毁(需在配置bean时指定销毁方法)
- IOC容器关闭
提示:
- 若bean的作用域为单例时,生命周期的前三个步骤会在获取IOC容器时执行
- 若bean的作用域为多例时,生命周期的前三个步骤会在获取bean时执行
步骤演示:
创建一个User类
package beanDemo; public class User { private Integer id; private String username; private String password; private Integer age; public User(Integer id, String username, String password, Integer age) { this.id = id; this.username = username; this.password = password; this.age = age; } public User(){} public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @Override public String toString() { return "User{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + ", age=" + age + '}'; } public void initMethod(){ System.out.println("生命周期:初始化"); } public void destroyMethod(){ System.out.println("生命周期:销毁"); } }
书写配置文件
<?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="User" class="beanDemo.User" init-method="initMethod" destroy-method="destroyMethod"></bean> </beans>
创建一个测试类UserDemo
import beanDemo.User; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class UserDemo { @Test public static void main(String[] args) { ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml"); User user = ioc.getBean(User.class); System.out.println(user); } }
如果想关闭容器,可以使用2.2提到的
ConfigurableApplicationContext
接口中的close方法;修改UserDemo测试类import beanDemo.User; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class UserDemo { @Test public static void main(String[] args) { ConfigurableApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml"); User user = ioc.getBean(User.class); System.out.println(user); ioc.close(); } }
如果需要在生命周期初始化前后添加额外操作,需要实现BeanPostProcessor接口,且配置到IOC容器中;需要注意的是,bean后置处理器不是单独针对某一个bean生效,而是IOC容器中所有bean都会执行。
package beanDemo; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; public class MyBeanPostProcess implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("后置处理器的初始化前方法"); return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName); } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("后置处理器的初始化后方法"); return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName); } }
在容器中配置该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.xsd"> <bean id="User" class="beanDemo.User" init-method="initMethod" destroy-method="destroyMethod"></bean> <bean id="myBeanPostProcessor" class="beanDemo.MyBeanPostProcess"></bean> </beans>
- 执行
4.3 FactoryBean
说明:如名字一样,这类bean如同工厂模式一般屏蔽了对象的内部细节,只会把需要的部分作为展示。与普通的bean不同,配置一个 FactoryBean类型的bean,在获取bean的时候得到的并不是class属性中配置的这个类的对象,而是 getObject()方法的返回值;如果想要创建一个FactoryBean类型的Bean,我们必须实现该接口,并且重写其中三个方法中的两个,其中isSingleton方法是可选的。
- getObject():提供一个对象交给IOC管理
- getObjectType():设置所提供对象的类型
- isSingleton():所提供的对象是否单例
提示:将来我们整合Mybatis时,Spring就是通过FactoryBean机制来帮我们创建SqlSessionFactory对象的。
4.4 基于xml的自动装配
说明:IOC容器根据bean所依赖的资源在容器中自动查找并注入到bean中的过程称为自动装配。仔细品读这句话,我们可以发现它所说的bean依赖的资源,其实指的就是需要通过ref来引用的资源,如类类型。自动装配方式分为多种,如下:
- 按类型
- 按名称
- 按构造方法
- 不启用自动装配
依赖自动装配特征:
- 自动装配用于引用类型依赖注入,而不能用于简单类型的依赖注入
- 使用按类型装配时必须保障容器中具有相同类型的bean唯一,推荐使用
- 使用按名称装配时必须保障容器中具有指定名称的bean,因变量名与配置文件耦合高,不推荐使用
- 自动装配优先级低于setter注入和构造器注入,同时出现时自动装配配置失效
步骤演示:
- 我们采用经典的MVC三层架构来做这么一个演示
其中BookDao和BookService是两个接口,内容分别如下:
package dao; public interface BookDao { void save(); }
package service; public interface BookService { void save(); }
其中BookDaoImpl和BookServiceImpl是上述两个接口的实现类,而BookServiceImpl中使用了BookDaoImpl的对象。
package dao.impl; import dao.BookDao; public class BookDaoImpl implements BookDao { @Override public void save() { System.out.println("book dao save"); } }
package service.impl; import dao.BookDao; import service.BookService; public class BookServiceImpl implements BookService { private BookDao bookDao; public void setBookDao(BookDao bookDao) { this.bookDao = bookDao; } @Override public void save() { System.out.println("book service save"); bookDao.save(); } }
按照之前的做法,我们需要在配置文件中声明两个bean,然后在BookService对应的bean来通过ref引用BookDao的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.xsd"> <bean id="bookDao" class="dao.impl.BookDaoImpl"></bean> <bean id="bookService" class="service.impl.BookServiceImpl"> <property name="bookDao" ref="bookDao"></property> </bean> </beans>
学会了自动装配后,我们不需要通过ref引用了,只需使用autowire来自动装配即可;自动装配利用了BookServiceImpl中的set方法来进行注入,故set方法必须存在,否则无法进行自动装配。
<?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="bookDao" class="dao.impl.BookDaoImpl"></bean> <bean id="bookService" class="service.impl.BookServiceImpl" autowire="byType"/> </beans>
- 对于采用
按类型自动装配
来说,其必须要求BookDaoImpl的bean标签对应的class和项目文件中BookDaoImpl的路径相同,如果你在配置文件中使用了两个BookDaoImpl的bean,那么他们的类路径是相同的,此时类类型自动装配无法识别将哪个bean注入到BookServiceImpl的bean中。 - 对于采用
按名称自动装配
来说,其必须要求set方法使用的setBookDao把set去掉后必须和bean中的id名要相同。
4.5 容器
说明:这实际上是对前面的学习做一个回顾,让我们来看看之前时如何创建IOC容器的。
创建容器:
// 方式一:类路径加载配置文件
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
// 方式二:文件路径加载配置文件
ApplicationContext ioc2 = new FileSystemXmlApplicationContext("C:\\UserWorkstation\\Code\\Spring\\src\\main\\resources\\applicationContext.xml");
// 方式三:加载多个配置文件
ApplicationContext ioc = new ClassPathXmlApplicationContext("bean1.xml","bean2.xml");
4.6 核心容器总结
- beanFactory是IOC容器的顶层接口,初始化BeanFactory对象时,加载的bean延迟加载
- ApplicationContext接口是Spring容器的核心接口,初始化时bean立即加载
- ApplicationContext接口提供基础的bean操作相关方法,通过其他接口扩展其功能
- bean标签中的属性:
- 依赖注入相关: