学习目标
上一篇文章我们介绍了什么是Spring,以及Spring的一些核心概念,并且快速快发一个Spring项目,实现IOC和DI,今天具体来讲解IOC
能够说出IOC的基础配置和Bean作用域
了解Bean的生命周期
能够说出Bean的实例化方式
一、Bean的基础配置
问题导入
问题1:在<bean>
标签上如何配置别名?
问题2:Bean的默认作用范围是什么?如何修改?
1 Bean基础配置【重点】
配置说明
2 Bean别名配置
配置说明
注意事项:
获取bean无论是通过id还是name获取,如果无法获取到,将抛出异常NoSuchBeanDefinitionException
NoSuchBeanDefinitionException: No bean named 'studentDaoImpl' available
代码演示
【第0步】创建项目名称为10_2_IOC_Bean
的maven项目
【第一步】导入Spring坐标
<dependencies>
<!--导入spring的坐标spring-context,对应版本是5.2.10.RELEASE-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.15</version>
</dependency>
<!-- 导入junit的测试包 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.28</version>
</dependency>
</dependencies>
【第二步】导入Student实体类
@Data
@ToString
@AllArgsConstructor
public class Student {
private String name;
private String address;
private Integer age;
private Integer status;
}
【第三步】定义Spring管理的类(接口)
-
StudentDao接口和StudentDaoImpl实现类
package com.zbbmeta.dao;
public interface StudentDao {
/**
* 添加学生
*/
void save();
}
package com.zbbmeta.dao.impl;
import com.zbbmeta.dao.StudentDao;
public class StudentDaoImpl implements StudentDao {
@Override
public void save() {
System.out.println("DAO: 添加学生信息到数据库...");
}
}
-
StudentService接口和StudentServiceImpl实现类
package com.zbbmeta.service;
public interface StudentService {
/**
* 添加学生
*/
void save();
}
package com.zbbmeta.service.impl;
import com.zbbmeta.dao.StudentDao;
import com.zbbmeta.dao.impl.StudentDaoImpl;
import com.zbbmeta.service.StudentService;
public class StudentServiceImpl implements StudentService {
//创建成员对象
private StudentDao studentDao = new StudentDaoImpl();
@Override
public void save() {
}
}
【第四步】创建Spring配置文件在resources目录下,配置对应类作为Spring管理的bean对象
-
定义application.xml配置文件并配置StudentDaoImpl
<?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标签详细的属性应用
-->
<!--
name属性:可以设置多个别名,别名之间使用逗号,空格,分号等分隔
-->
<bean name="studentDao2,abc studentDao3" class="com.zbbmeta.dao.impl.StudentDaoImpl" id="studentDao"></bean>
</beans>
注意事项:bean定义时id属性和name中名称不能有重复的在同一个上下文中(IOC容器中)不能重复
【第四步】根据容器别名获取Bean对象
package com.zbbmeta;
import com.zbbmeta.dao.StudentDao;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class NameApplication {
public static void main(String[] args) {
/**
* 从IOC容器里面根据别名获取对象执行
*/
//1.根据配置文件application.xml创建IOC容器
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("application.xml");
//2.从IOC容器里面获取id="abc"对象
StudentDao studentDao = (StudentDao) ac.getBean("abc");
//3.执行对象方法
studentDao.save();
//4.关闭容器
ac.close();
}
}
打印结果
3 Bean作用范围配置【重点】
配置说明
扩展: scope的取值不仅仅只有singleton和prototype,还有request、session、application、 websocket ,表示创建出的对象放置在web容器(tomcat)对应的位置。比如:request表示保存到request域中。
代码演示
在application.xml中配置prototype格式
-
定义application.xml配置文件并配置StudentDaoImpl
<?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标签详细的属性应用
-->
<!--
scope属性:定义bean的作用范围,一共有5个
singleton: 设置单例创建对象(推荐,也是默认值),好处:节省资源
prototype: 设置多例创建对象,每次从IOC容器获取的时候都会创建对象,获取多次创建多次。
request: 在web开发环境中,IOC容器会将对象放到request请求域中,对象存活的范围在一次请求内。
请求开始创建对象,请求结束销毁对象
session: 在web开发环境中,IOC容器会将对象放到session会话域中,对象存活的范围在一次会话内。
会话开始开始创建对象,会话销毁对象销毁。
global-session: 是多台服务器共享同一个会话存储的数据。
-->
<bean class="com.zbbmeta.dao.impl.StudentDaoImpl" id="studentDao4" scope="prototype"></bean>
</beans>
根据容器别名获取Bean对象
package com.zbbmeta;
import com.zbbmeta.dao.StudentDao;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ScopeApplication {
public static void main(String[] args) {
/**
* Bean的作用域范围演示
*/
//1.根据配置文件application.xml创建IOC容器
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("application.xml");
//2.从IOC容器里面获取id="studentService"对象
System.out.println("=========singleton(单例)模式=========");
StudentDao studentDao = (StudentDao) ac.getBean("studentDao");
StudentDao studentDao1 = (StudentDao) ac.getBean("studentDao");
System.out.println("studentDao = " + studentDao);
System.out.println("studentDao1 = " + studentDao1);
System.out.println("=========prototype模式=========");
StudentDao studentDao2 = (StudentDao) ac.getBean("studentDao4");
StudentDao studentDao3 = (StudentDao) ac.getBean("studentDao4");
System.out.println("studentDao2 = " + studentDao2);
System.out.println("studentDao3 = " + studentDao3);
//4.关闭容器
ac.close();
}
}
打印结果
注意:在我们的实际开发当中,绝大部分的Bean是单例的,也就是说绝大部分Bean不需要配置scope属性。
二、Bean的实例化
思考:Bean的实例化方式有几种?
2 实例化Bean的三种方式
2.1 构造方法方式【重点】
-
BookDaoImpl实现类
public class StudentDaoImpl implements StudentDao {
public StudentDaoImpl() {
System.out.println("Student dao constructor is running ....");
}
@Override
public void save() {
System.out.println("DAO: 添加学生信息到数据库...");
}
}
-
application.xml配置
<!--
目标:讲解bean创建方式
-->
<!--创建StudentDaoImpl对象方式1:默认调用类的无参构造函数创建-->
<bean id="studentDao" class="com.zbbmeta.dao.impl.StudentDaoImpl"/>
-
AppForInstanceBook测试类
public class OneApplication {
public static void main(String[] args) {
/**
* 无参构造方式创建Bean
*/
//1.根据配置文件application.xml创建IOC容器
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("application.xml");
//2.从IOC容器里面获取id="studentService"对象
StudentDao studentDao = (StudentDao) ac.getBean("studentDao");
//3.执行对象方法
studentDao.save();
//4.关闭容器
ac.close();
}
}
-
运行结果
注意:无参构造方法如果不存在,将抛出异常BeanCreationException
2.2 静态工厂方式
-
StudentDaoFactory工厂类
package com.zbbmeta.factory;
import com.zbbmeta.dao.StudentDao;
import com.zbbmeta.dao.impl.StudentDaoImpl;
public class StudentDaoFactory {
// 静态工厂创建对象
public static StudentDao getStudentDao(){
System.out.println("Student static factory setup....");
return new StudentDaoImpl();
}
}
-
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创建方式
-->
<!--创建StudentDaoImpl对象方式1:默认调用类的无参构造函数创建-->
<!-- <bean id="studentDao" class="com.zbbmeta.dao.impl.StudentDaoImpl"/>-->
<!--创建StudentDaoImpl对象方式2:调用静态工厂方法创建对象加入IOC容器
class="com.zbbmeta.factory.StudentDaoFactory" 设置工厂类全名
factory-method="getStudentDao" 调用工厂的静态方法
-->
<bean class="com.zbbmeta.factory.StudentDaoFactory" factory-method="getStudentDao" id="studentDao2"></bean>
</beans>
注意:测试前最好把之前使用Bean标签创建的对象进行注释
-
TwoApplication测试类
public class TwoApplication {
public static void main(String[] args) {
/**
* 无参构造方式创建Bean
*/
//1.根据配置文件application.xml创建IOC容器
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("application.xml");
//2.从IOC容器里面获取id="studentService"对象
StudentDao studentDao = (StudentDao) ac.getBean("studentDao2");
//3.执行对象方法
studentDao.save();
//4.关闭容器
ac.close();
}
}
-
运行结果
2.3 实例工厂方式
-
UserDao接口和UserDaoImpl实现类
//利用实例方法创建StudentDao对象
public StudentDao getStudentDao2(){
System.out.println("调用了实例工厂方法");
return new StudentDaoImpl();
}
-
StudentDaoFactory工厂类添加方法
//实例工厂创建对象
public class UserDaoFactory {
public UserDao getUserDao(){
return new UserDaoImpl();
}
}
-
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创建方式
-->
<!--创建StudentDaoImpl对象方式1:默认调用类的无参构造函数创建-->
<!-- <bean id="studentDao" class="com.zbbmeta.dao.impl.StudentDaoImpl"/>-->
<!--创建StudentDaoImpl对象方式2:调用静态工厂方法创建对象加入IOC容器
class="com.zbbmeta.factory.StudentDaoFactory" 设置工厂类全名
factory-method="getStudentDao" 调用工厂的静态方法
-->
<!-- <bean class="com.zbbmeta.factory.StudentDaoFactory" factory-method="getStudentDao" id="studentDao2"></bean>-->
<!--创建BookDaoImpl对象方式3:调用实例工厂方法创建对象加入IOC容器
class="com.itheima.factory.BookDaoFactory" 设置工厂类全名
factory-method="getBookDao" 调用工厂的静态方法
-->
<!--第一步:创建工厂StudentDaoFactory对象-->
<bean class="com.zbbmeta.factory.StudentDaoFactory" id="studentDaoFactory"></bean>
<!--第一步:调用工厂对象的getStudentDao2()实例方法创建StudentDaoImpl对象加入IOC容器
factory-bean="studentDaoFactory" 获取IOC容器中指定id值的对象
factory-method="getStudentDao2" 如果配置了factory-bean,那么这里设置的就是实例方法名
-->
<bean factory-bean="studentDaoFactory" factory-method="getStudentDao2" id="studentDao3"></bean>
</beans>
-
ThreeApplication测试类
package com.zbbmeta;
import com.zbbmeta.dao.StudentDao;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ThreeApplication {
public static void main(String[] args) {
/**
* 无参构造方式创建Bean
*/
//1.根据配置文件application.xml创建IOC容器
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("application.xml");
//2.从IOC容器里面获取id="studentService"对象
StudentDao studentDao = (StudentDao) ac.getBean("studentDao3");
//3.执行对象方法
studentDao.save();
//4.关闭容器
ac.close();
}
}
-
运行结果
三、Bean的生命周期【了解】
问题导入
问题1:多例的Bean能够配置并执行销毁的方法?
问题2:如何做才执行Bean销毁的方法?
1 生命周期相关概念介绍
-
生命周期:从创建到消亡的完整过程
-
bean生命周期:bean从创建到销毁的整体过程
-
bean生命周期控制:在bean创建后到销毁前做一些事情
1.1生命周期过程
- 初始化容器
-
创建对象(内存分配)
-
执行构造方法
-
执行属性注入(set操作)
-
执行bean初始化方法
-
- 使用bean
-
执行业务操作
-
- 关闭/销毁容器
-
执行bean销毁方法
-
2 代码演示
2.1 Bean生命周期控制
【第0步】创建项目名称为10_4_IOC_BeanLifeCycle
的maven项目
【第一步】导入Spring坐标
<dependencies>
<!--导入spring的坐标spring-context,对应版本是5.2.10.RELEASE-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.15</version>
</dependency>
<!-- 导入junit的测试包 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.28</version>
</dependency>
</dependencies>
【第二步】导入Student实体类
@Data
@ToString
@AllArgsConstructor
public class Student {
private String name;
private String address;
private Integer age;
private Integer status;
}
【第三步】定义Spring管理的类(接口)
-
StudentDao接口和StudentDaoImpl实现类
package com.zbbmeta.dao;
public interface StudentDao {
/**
* 添加学生
*/
void save();
}
package com.zbbmeta.dao.impl;
import com.zbbmeta.dao.StudentDao;
public class StudentDaoImpl implements StudentDao {
public StudentDaoImpl(){
System.out.println("Student Dao 的无参构造");
}
@Override
public void save() {
System.out.println("DAO: 添加学生信息到数据库...");
}
public void init(){
System.out.println("Student Dao 的初始化方法");
}
public void destroy(){
System.out.println("Student Dao 的销毁方法");
}
}
【第四步】创建Spring配置文件在resources目录下,配置对应类作为Spring管理的bean对象
-
定义application.xml配置文件并配置StudentDaoImpl
<?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">
<!--目标:创建StudentDaoImpl对象:设置生命周期方法
init-method="init" 在对象创建后立即调用初始化方法
destroy-method="destroy":在容器执行销毁前立即调用销毁的方法
注意:只有单例对象才会运行销毁生命周期方法
-->
<bean class="com.zbbmeta.dao.impl.StudentDaoImpl" id="studentDao" init-method="init" destroy-method="destroy"></bean>
</beans>
【第四步】根据容器别名获取Bean对象
package com.zbbmeta;
import com.zbbmeta.dao.StudentDao;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class LifeCycleApplication {
public static void main(String[] args) {
//1.根据配置文件application.xml创建IOC容器
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("application.xml");
//2.从IOC容器里面获取id="studentService"对象
StudentDao studentDao = (StudentDao) ac.getBean("studentDao");
//3.执行对象方法
studentDao.save();
//4.关闭容器
ac.close();
}
}
打印结果
3 Bean销毁时机
-
容器关闭前触发bean的销毁
- 关闭容器方式:
-
手工关闭容器 调用容器的
close()
操作文章来源:https://uudwc.com/A/wNyoV -
注册关闭钩子(类似于注册一个事件),在虚拟机退出前先关闭容器再退出虚拟机 调用容器的
registerShutdownHook()
操作文章来源地址https://uudwc.com/A/wNyoV
-
public class LifeCycleApplication {
public static void main(String[] args) {
//1.根据配置文件application.xml创建IOC容器
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("application.xml");
//2.从IOC容器里面获取id="studentService"对象
StudentDao studentDao = (StudentDao) ac.getBean("studentDao");
//3.执行对象方法
studentDao.save();
//4.关闭容器
// ac.close();
//注册关闭钩子函数,在虚拟机退出之前回调此函数,关闭容器
ac.registerShutdownHook();
}
}