bean
bean获取
bean有下面三种获取方式:
方式一: 使用bean名称获取
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
方式二: 使用bean名称获取并指定类型
BookDao bookDao = ctx.getBean("bookDao", BookDao.class);
方式三: 使用bean类型获取, 传入的类型, 该类型的bean只能有一个
BookDao bookDao = ctx.getBean(BookDao.class);
bean配置
bean基础配置
在上面快速入门的案例中, 我们已经完成了bean基础配置的学习
类别 | 描述 |
---|---|
名称 | bean |
类型 | 标签 |
所属 | beans标签 |
功能 | 定义Spring核心容器管理的对象 |
格式 | <beans> <bean/> <bean></bean> </beans> |
属性列表 | id:bean的id,使用容器可以通过id值获取对应的bean,在一个容器中id值唯一 class:bean的类型,即配置的bean的全路径类名 |
范例 | <bean id=“bookDao” class=“com.itheima.dao.impl.BookDaoImpl”/> <bean id=“bookService” class=“com.itheima.service.impl.BookServiceImpl”></bean> |
bean别名配置
开发者直接的命名习惯是不一样的, 因此bean可以取别名, 一个bean可以有多个名称:
bean标签中有一个name属性用于配置别名
类别 | 描述 |
---|---|
名称 | name |
类型 | 属性 |
所属 | bean标签 |
功能 | 定义bean的别名,可定义多个,多个名称之间使用逗号(,)分号(;)空格( )分隔 |
范例 | <bean id=“bookDao” name=“dao bookDaoImpl” class=“com.itheima.dao.impl.BookDaoImpl”/> <bean name=“service,bookServiceImpl” class=“com.itheima.service.impl.BookServiceImpl”/> |
演示代码:
在配置文件中, 为bean取service和service2两个别名
<?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="com.chenyq.dao.impl.BookDaoImpl"/>
<!--通过name属性取别名-->
<bean id="bookService" name="service, service2" class="com.chenyq.service.impl.BookServiceImpl">
<property name="bookDao" ref="bookDao"/>
</bean>
</beans>
在代码中, 现在我们可以通过id: bookService, 以及别名: service和service2获取到bean
public class App {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
// 通过别名获取bean
BookService bookService = (BookService) ctx.getBean("service");
BookService bookService2 = (BookService) ctx.getBean("service2");
bookService.save();
bookService2.save();
}
}
我们还可以给bookDao的bean取别名, 再让bookService进行DI时参照别名
<?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">
<!--取别名dao-->
<bean id="bookDao" name="dao" class="com.chenyq.dao.impl.BookDaoImpl"/>
<bean id="bookService" name="service, service2" class="com.chenyq.service.impl.BookServiceImpl">
<!--ref属性参照dao这个别名, 同样是可以的-->
<property name="bookDao" ref="dao"/>
</bean>
</beans>
注意: 获取bean无论是通过id还是name获取,如果无法获取到,将抛出异常NoSuchBeanDefinitionException
bean作用范围配置
默认情况下bean创建是单例的, 创建多次bean指向同一内存地址, 也就是说多次创建出来的是同一个对象
若想要创建非单例, 那么就要通过scope配置
类别 | 描述 |
---|---|
名称 | scope |
类型 | 属性 |
所属 | bean标签 |
功能 | 定义bean的作用范围,可选范围如下: singleton:单例(默认); prototype: 非单例 |
范例 | <bean id=“bookDao” class=“com.itheima.dao.impl.BookDaoImpl” scope=“prototype” /> |
示例代码
<!--设置非单例-->
<bean id="bookDao" class="com.chenyq.dao.impl.BookDaoImpl" scope="prototype"/>
适合交给容器进行管理的bean
表现层对象
业务层对象
数据层对象
工具对象
不适合交给容器进行管理的bean
封装实体的域对象
bean实例化
bean是如何创建出来的, 和我们自己创建一个对象的方式有区别吗?
bean实例化通常有下面几种方式, 下面我们来一一学习
bean实例化的方式一: 构造方法(常用)
bean本质上就是对象,创建bean使用的是无参构造方法完成, 所有我们需要提供可访问的构造方法
public class BookDaoImpl implements BookDao {
public BookDaoImpl() {
System.out.println("book constructor is running ...");
}
public void save() {
System.out.println("book dao save ...");
}
}
无参构造方法如果不存在,将抛出异常BeanCreationException
实例化bean的方式二: 静态工厂(早期方式, 了解)
例如我们有下面这样一个工厂类, 工程类中的getBookDao方法用来造对象
public class BookDaoFactory {
public static BookDao getBookDao() {
return new BookDaoImpl();
}
}
public class App {
public static void main(String[] args) {
BookDao bookDao = BookDaoFactory.getBookDao();
bookDao.save();
}
}
我们可以配置该类的bean
<!--注意配置静态工厂类, 需要通过factory-method属性指定使用工厂类的哪一个方法-->
<bean id="bookFactory"
class="com.chenyq.factory.BookDaoFactory"
factory-method="getBookDao"
/>
再通过配置的bean进行实例化
public class App2 {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookDao bookDao = (BookDao) ctx.getBean("bookFactory");
bookDao.save();
}
}
实例化bean的方式三: 实例工厂(了解)
例如我们有下面一个实例工厂, 使用实例工厂创建的方式如下, 与静态工厂相比多了一步实例化工程对象
public class BookDaoFactory {
public BookDao getBookDao() {
return new BookDaoImpl();
}
}
public class App2 {
public static void main(String[] args) {
BookDaoFactory bookFactory = new BookDaoFactory();
BookDao bookDao = bookFactory.getBookDao();
bookDao.save();
}
}
使用实例工厂实例化bean, 首先需要将工厂的bean造出来, 在通过工厂的bean将工厂要创建对象的bean造出来
<!--实例工厂实例化bean-->
<bean id="bookFactory" class="com.chenyq.factory.BookDaoFactory"/>
<bean id="bookDao" factory-method="getBookDao" factory-bean="bookFactory"/>
public class App2 {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookDao bookDao = (BookDao) ctx.getBean("bookFacDao");
bookDao.save();
}
}
实例化bean方式三的改良: 使用FactoryBean实例化(重点掌握)
但是上面方法中, 工厂bean是为了创建方法bean而存在的, 是为了配合使用并无实际意义, 并且方法名不固定, 每次都需要配置
因此Spring针对第三种方式进行了改良
我们使用这个方法, 创建一个工程时需要实现FactoryBean接口, 并重写方法
public class BookDaoFactoryBean implements FactoryBean<BookDao> {
/**
代替原始实例工厂中创建对象的方法
返回要创建的对象即可
*/
public BookDao getObject() throws Exception {
return new BookDaoImpl();
}
/**
该方法返回工厂创建对象的类型
*/
public Class<?> getObjectType() {
return BookDao.class;
}
}
使用FactoryBean, 我们的配置就会变得简单
<!--FactoryBean实例化bean-->
<bean id="bookFacDao" class="com.chenyq.factory.BookDaoFactoryBean"/>
实现FactoryBean接口工厂创建出来的对象, 默认是单例的;
如果想要设置非单例模式, 我们需要重写isSingleton方法,该方法返回一个布尔值, true代表单例, false代表非单例
public class BookDaoFactoryBean implements FactoryBean<BookDao> {
public BookDao getObject() throws Exception {
return new BookDaoImpl();
}
public Class<?> getObjectType() {
return BookDao.class;
}
/**
返回true单例
返回false非单例
*/
public boolean isSingleton() {
return false;
}
}
bean生命周期
生命周期:从创建到消亡的完整过程
bean生命周期:bean从创建到销毁的整体过程
bean生命周期控制:在bean创建后到销毁前做一些事情
bean控制生命周期有两种方式:
配置方式
接口方式(了解)
配置方式控制生命周期:
提供生命周期的控制方法
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println("book dao save...");
}
/**
表示bean初始化调用的方法
*/
public void init() {
System.out.println("init...");
}
/**
表示bean销毁之前调用的方法
*/
public void destory() {
System.out.println("destory...");
}
}
提供方法后, 我们还需要配置生命周期控制的方法, 通过init-method和destroy-method属性配置
<!--配置生命周期控制的方法-->
<bean id="bookDao"
class="com.chenyq.dao.impl.BookDaoImpl"
init-method="init"
destroy-method="destory"
/>
接口方式控制生命周期
Spring觉得这样配置任意一个方法就是生命周期太过于随意; 因此Spring提供了InitializingBean和DisposableBean两个接口的方式控制生命周期, 每个接口都提供一个方法, 我们重写其方法即可, 这样还省去了两次配置
public class BookDaoImpl implements BookDao, InitializingBean, DisposableBean {
public void save() {
System.out.println("book dao save...");
}
public void destroy() throws Exception {
System.out.println("destroy...");
}
public void afterPropertiesSet() throws Exception {
System.out.println("init...");
}
}
下面我们来了解一下, bean在初始化到销毁过程中经历了哪些阶段:
初始化容器
- 创建对象(内存分配), 可以理解为new在做的事情
- 执行构造方法
- 执行属性注入(set操作), 设置属性的操作是优先于初始化生命周期执行的
- 执行bean初始化方法, 也就是执行初始化的生命周期方法
使用bean:
- 执行业务操作
关闭/销毁容器:
- 关闭/销毁容器之前, 执行bean销毁生命周期方法
关闭容器的方式:
手工关闭容器
- ConfigurableApplicationContext接口close()操作, 该方法属于暴力关闭
ublic static void main(String[] args) {
// 使用ClassPathXmlApplicationContext获取IoC容器
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
bookDao.save();
// 使用close方法关闭容器
ctx.close();
}
注册关闭钩子,在虚拟机退出前会先关闭容器再退出虚拟机
- ConfigurableApplicationContext接口registerShutdownHook()操作
public static void main(String[] args) {
// 使用ClassPathXmlApplicationContext获取IoC容器
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
// 使用registerShutdownHook方法注册关闭钩子, 此代码可以放在任意一样
ctx.registerShutdownHook();
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
bookDao.save();
}