5.spring 中 bean的实例化–构造方法
- 创建新的maven模块respr_newbean,并在pom.xml添加spring的坐标
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.22</version> <scope>compile</scope> </dependency>
- 创建核心配置文件applicationContext.xml文件、dao接口及其实现类以及相关的类(先写标记部分)
- 编写dao接口及其实现类
- BookDao接口
public interface BookDao { public void save(); }
- BookDaoImpl实现类
public class BookDaoImpl implements BookDao { //无参构造 public BookDaoImpl() { System.out.println("book dao constructor is running ...."); } public void save() { System.out.println("book dao save ..."); } }
- 进行测试的类 AppForInstanceBook
public class AppForInstanceBook { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); BookDao bookDao = (BookDao) ctx.getBean("bookDao"); bookDao.save(); } }
- 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--> <bean id="bookDao" class="org.example.dao.impl.BookDaoImpl"/> </beans>
- AppForInstanceBook运行结果
- 结论
spring构造方法实例化bean使用了无参构造器,可以省略无参构造器的书写。实例化bean就是用构造方法来实例化对象。
6.bean的实例化 – 静态工厂实例化
- 创建dao接口及其实现类以及相关的类(先写标记部分)
- 编写dao接口及其实现类以及相关的类
- OrderDao接口
package org.example.dao; public interface OrderDao { public void save(); }
- OrderDaoImpl实现类
在这里插入代码片package org.example.dao.impl; import org.example.dao.OrderDao; public class OrderDaoImpl implements OrderDao { public void save() { System.out.println("order dao save ..."); } }
- 工厂类OrderDaoFactory(静态工厂类代理生成对象)
package org.example.factory; import org.example.dao.OrderDao; import org.example.dao.impl.OrderDaoImpl; //静态工厂创建对象 public class OrderDaoFactory { public static OrderDao getOrderDao(){ System.out.println("factory setup...."); return new OrderDaoImpl(); } }
- AppForInstanceOrder模拟测试类编写(纯JAVA开发,此处还没有Spring)
public class AppForInstanceOrder { public static void main(String[] args) { //通过静态工厂创建对象 OrderDao orderDao = OrderDaoFactory.getOrderDao(); orderDao.save(); } }
- 模拟测试类的运行结果
- 简要分析
将 OrderDaoImpl 创建对象的 工作交给 静态工厂 OrderDaoFactory 类来完成,如果用spring代理这种工厂模式的开发,写法如下
- spring 代理静态工厂实例化对象
public class AppForInstanceOrder { public static void main(String[] args) { //通过静态工厂创建对象 // OrderDao orderDao = OrderDaoFactory.getOrderDao(); // orderDao.save(); // spring代理静态工厂实例化对象 ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); OrderDao orderDao = (OrderDao) ctx.getBean("orderDao"); orderDao.save(); } }
- applicationContext.xml文件配置静态工厂 OrderDaoFactory bean (注意factory-method属性)
<?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--> <!-- <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>--> <!-- 方式二:使用静态工厂实例化bean--> <bean id="orderDao" class="com.itheima.factory.OrderDaoFactory" factory-method="getOrderDao"/> </beans>
- 运行结果
7.bean实例化–实例工厂和FactoryBean
- 创建dao接口及其实现类以及相关的类(标记部分)
- 编写dao接口及其实现类以及相关的类
- UserDao接口
public interface UserDao { public void save(); }
- UserDaoImpl实现类
public class UserDaoImpl implements UserDao { public void save() { System.out.println("user dao save ..."); } }
- UserDaoFactoryBean实例工厂类
//实例工厂创建对象 public class UserDaoFactory { public UserDao getUserDao(){ return new UserDaoImpl(); } }
- AppForInstanceUser模拟测试类(纯JAVA开发)
public class AppForInstanceUser { public static void main(String[] args) { // //创建实例工厂对象 UserDaoFactory userDaoFactory = new UserDaoFactory(); // //通过实例工厂对象创建对象 UserDao userDao = userDaoFactory.getUserDao(); userDao.save(); } }
- 运行结果
- 简要分析
此时与静态工厂的区别是模拟测试类中多了创建工厂对象的步骤
- spring代理下的实例工厂bean实例化模拟测试类
public class AppForInstanceUser { public static void main(String[] args) { // //创建实例工厂对象 // UserDaoFactory userDaoFactory = new UserDaoFactory(); // //通过实例工厂对象创建对象 // UserDao userDao = userDaoFactory.getUserDao(); // userDao.save(); ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); UserDao userDao = (UserDao) ctx.getBean("userDao"); userDao.save(); } }
- 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--> <!-- <bean id="bookDao" class="org.example.dao.impl.BookDaoImpl"/>--> <!-- 方式二:使用静态工厂实例化bean--> <!-- <bean id="orderDao" class="org.example.factory.OrderDaoFactory" factory-method="getOrderDao"/>--> <!--方式三:使用实例工厂实例化bean--> <bean id="userFactory" class="org.example.factory.UserDaoFactory"/> <bean id="userDao" factory-method="getUserDao" factory-bean="userFactory"/> </beans>
- 模拟测试类运行结果
- spring代理实例工厂bean实例化简要分析 --> 改进为BeanFactory bean实例化
- 创建并编写BeanFactory工厂类UserDaoFactoryBean
//FactoryBean创建对象 //实现接口,创建什么对象写什么泛型 public class UserDaoFactoryBean implements FactoryBean<UserDao> { //实现抽象方法 //代替原始实例工厂中创建对象的方法,以后方法名不用指定,就用getObject public UserDao getObject() throws Exception { return new UserDaoImpl(); } //配置的类是什么类型 public Class<?> getObjectType() { return UserDao.class; } }
- 配置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--> <!-- <bean id="bookDao" class="org.example.dao.impl.BookDaoImpl"/>--> <!-- 方式二:使用静态工厂实例化bean--> <!-- <bean id="orderDao" class="org.example.factory.OrderDaoFactory" factory-method="getOrderDao"/>--> <!--方式三:使用实例工厂实例化bean--> <!-- <bean id="userFactory" class="org.example.factory.UserDaoFactory"/>--> <!-- <bean id="userDao" factory-method="getUserDao" factory-bean="userFactory"/>--> <!--方式四:使用FactoryBean实例化bean--> <bean id="userDao" class="org.example.factory.UserDaoFactoryBean"/> </beans>
- 模拟测试类运行结果
8.bean的生命周期
- 概念
- 理解 Spring bean 的生命周期很容易。当一个 bean 被实例化时,它可能需要执行一些初始化使它转换成可用状态。同样,当 bean 不再需要,并且从容器中移除时,可能需要做一些清除工作
- 尽管还有一些在 Bean 实例化和销毁之间发生的活动,有两个重要的生命周期回调方法,它们在 bean 的初始化和销毁的时候是必需的
- 为了定义安装和拆卸一个 bean,我们只要声明带有 init-method 和/或 destroy-method 参数的 。init-method 属性指定一个方法,在实例化 bean 时,立即调用该方法。同样,destroy-method 指定一个方法,只有从容器中移除 bean 之后,才能调用该方法
- Bean的生命周期可以表达为:Bean的定义——Bean的初始化——Bean的使用——Bean的销毁
- 编写代码(IOC 和 DI入门案例的代码,模块名为respr_ioc)
- 在BookDaoImpl实现类中定义代表创建bena初始化和销毁的方法
package org.example.dao.impl; import org.example.dao.BookDao; 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..."); } }
- 在applicationContext.xml文件中配置BookDao bean 的 初始化方法 int-method 和销毁方法 destory-method
- 将BookService的代码注释掉,运行模拟测试类,观察BookDao bean的创建和销毁过程
- 运行结果
- 简要分析
我们从运行结果可以看到bean初始化成功了,但是并没有进行销毁工作,原因是java虚拟机(java在虚拟机运行)在执行玩程序退出时并没有做销毁操作,我们需要自己添加关闭语句,ctx.close()。但是ApplicationContext接口中并没有这个方法,而它的实现类中有,所以我们要用它的实现类ClassPathXmlApplicationContext来调用这个方法 - 关闭ioc容器的代码及运行结果如下
- 关闭ioc容器的第二种方式:关闭钩子(概念、代码及运行结果如下)
在Java程序退出时——尤其是非正常退出时,我们可能需要先执行一些善后工作,如关闭线程池、连接池、文件句柄等。如何保证善后工作的代码能够被执行到呢?Java为用户提供了关闭钩子(shutdown hook)registerShutdownHook()方法来注册关闭钩子
- 两种关闭ioc容器的简要分析
关闭钩子函数代码在程序中的位置要求没有close()方法那么苛刻,如果将其挪到bookDao.save()方法的下面也能用,close()方法比较暴力 - 绑定销毁方法和初始化方法的改进–继承接口(以BookServiceImpl为例)
- 实现InitializingBean, DisposableBean接口
- 重写 destroy(销毁) 和 afterPropertiesSet(初始化) 方法
public class BookServiceImpl implements BookService, InitializingBean, DisposableBean { private BookDao bookDao; public void setBookDao(BookDao bookDao) { this.bookDao = bookDao; } public void save() { System.out.println("book service save ..."); bookDao.save(); } public void destroy() throws Exception { System.out.println("service destroy"); } public void afterPropertiesSet() throws Exception { System.out.println("service init"); } }
- 模拟测试类Main2运行结果
(尽管在模拟测试类Main2并没有调用BookService 的bean,但是在核心配置文件中定义了BookService 的 bean,该创建bean的时候就创建,该初始化就初始化,该销毁就销毁)
9.依赖注入 – setter注入
- 思考
- 依赖注入 – setter注入引用类型的步骤方法(DI入门案例所用的就是setter注入引用类型)
(代码见DI入门案例)
- 探讨引用多个引用对象(在DI入门的代码基础上编码)
- 在dao包下创建并编写UserDao接口
package org.example.dao; public interface UserDao { public void save(); }
- 在impl包下创建并编写UserDaoImpl实现类
package org.example.dao.impl; import org.example.dao.UserDao; public class UserDaoImpl implements UserDao { public void save() { System.out.println("user dao save ..."); } }
- 在BookServiceImpl实现类中setter注入UserDao
public class BookServiceImpl implements BookService{ private BookDao bookDao; private UserDao userDao; //setter注入需要提供要注入对象的set方法 public void setUserDao(UserDao userDao) { this.userDao = userDao; } //setter注入需要提供要注入对象的set方法 public void setBookDao(BookDao bookDao) { this.bookDao = bookDao; } public void save() { System.out.println("book service save ..."); bookDao.save(); userDao.save(); } }
- 在核心配置类applicationContext中配置userDAO的bean,并将其注入到BookService中
- 执行模拟测试类App2
(从运行结果中可以看到多个引用类型是可以用setter注入的)
- 依赖注入 – setter注入普通类型的步骤方法
- 在BookDaoImpl中提供两个变量,并提供对应的setter方法(以BookDaoImpl为例)
public class BookDaoImpl implements BookDao { private String databaseName; private int connectionNum; //setter注入需要提供要注入对象的set方法 public void setConnectionNum(int connectionNum) { this.connectionNum = connectionNum; } //setter注入需要提供要注入对象的set方法 public void setDatabaseName(String databaseName) { this.databaseName = databaseName; } public void save() { System.out.println("book dao save ..."+databaseName+","+connectionNum); } }
- 在核心配置类applicationContext.xml文件中配置中注入普通变量
- 模拟测试类App2类的运行结果