Spring |(二)IoC相关内容 | bean
文章目录
- 📚bean基础配置
- 🐇bean的id和class
- 🐇bean的name属性
- 🐇bean作用范围scope配置
- 🐇bean基础配置小结
- 📚bean实例化
- 🐇构造方法实例化(常用)
- 🐇静态工厂实例化
- 🐇实例工厂与FactoryBean
- 📚bean的生命周期
- 🐇生命周期设置
- 🐇销毁操作未调用解决方法
- 🐇Spring提供的生命周期控制接口
学习来源:黑马程序员SSM框架教程_Spring+SpringMVC+Maven高级+SpringBoot+MyBatisPlus企业实用开发技术
📚bean基础配置
🐇bean的id和class
- id:使用容器可以通过id值获取对应的bean,在一个容器中id值唯一。
- class:bean的类型,即配置的bean的全路径类名。
🐇bean的name属性
-
name
属性,定义bean的别名,可定义多个,使用逗号、分号、空格分隔。
-
配置别名:打开spring的配置文件applicationContext.xml。
<!--name:为bean指定别名,别名可以有多个,使用逗号,分号,空格进行分隔--> <bean id="bookService" name="service service4 bookEbi" class="com.itheima.service.impl.BookServiceImpl"><property name="bookDao" ref="bookDao"/> </bean>
-
根据名称容器获取bean对象(对应名称必须在spring配置文件中存在)
public class AppForName {public static void main(String[] args) {ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");//此处根据bean标签的id属性和name属性的任意一个值来获取bean对象BookService bookService = (BookService) ctx.getBean("service4");bookService.save();} }
🐇bean作用范围scope配置
-
验证思路:同一个bean获取两次,将对象打印到控制台,看打印出的地址值是否一致。
BookDao bookDao1 = (BookDao) ctx.getBean("bookDao"); BookDao bookDao2 = (BookDao) ctx.getBean("bookDao"); System.out.println(bookDao1); System.out.println(bookDao2);
打印结果:
-
默认情况下,Spring创建的bean对象都是单例的。
-
在Spring配置文件中,配置scope属性来实现bean的非单例创建。
<bean id="bookDao" name="dao" class="com.itheima.dao.impl.BookDaoImpl" scope=""/>
- 将scope设置为
singleton
- 将scope设置为
prototype
- 将scope设置为
-
bean默认为单例? bean为单例是指在Spring的IoC容器中只会有该类的一个对象。这避免了对象的频繁创建与销毁,达到了bean对象的复用,性能高。
-
线程安全问题?
- 如果对象是有状态对象,即该对象有成员变量可以用来存储数据,那所有请求线程共用一个bean对象会存在线程安全问题。
- 如果对象是无状态对象,即该对象没有成员变量进行数据存储的,方法中的局部变量在方法调用完成后会被销毁,不会存在线程安全问题。
-
表现层对象、业务层对象、数据层对象、工具对象适合交给容器进行管理。
-
封装实例的域对象,因为会引发线程安全问题,不适合交给容器进行管理。
🐇bean基础配置小结
📚bean实例化
以下涉及到的project结构如下:
🐇构造方法实例化(常用)
- bean本质上就是对象,对象在new的时候会使用构造方法完成,创建bean也是使用构造方法完成的。
- 步骤1:准备一个BookDao和BookDaoImpl类,即【提供可访问的构造方法】。
public interface BookDao {public void save(); }public class BookDaoImpl implements BookDao {public void save() {System.out.println("book dao save ...");}}
- 步骤2:将类【配置】到Spring容器
<?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.itheima.dao.impl.BookDaoImpl"/></beans>
- 步骤3:编写运行程序
public class AppForInstanceBook {public static void main(String[] args) {ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");BookDao bookDao = (BookDao) ctx.getBean("bookDao");bookDao.save();} }
- Spring容器在创建对象的时候走的是类的无参构造方法,且底层用的是反射,能访问类中的私有构造方法。真正在使用这种方式的时候,我们什么也不需要做。
🐇静态工厂实例化
- 步骤1:准备一个OrderDao和OrderDaoImpl类。
public interface OrderDao {public void save(); }public class OrderDaoImpl implements OrderDao {public void save() {System.out.println("order dao save ...");} }
- 步骤2:创建一个工厂类OrderDaoFactory并提供一个【静态方法】。
//静态工厂创建对象 public class OrderDaoFactory {public static OrderDao getOrderDao(){return new OrderDaoImpl();} }
- 步骤3:在spring的【配置】文件application.properties中添加以下内容↓
<!--方式二--> <bean id="orderDao" class="com.itheima.factory.OrderDaoFactory" factory-method="getOrderDao"/>
- 步骤4:在AppForInstanceOrder运行类,使用从IoC容器中获取bean的方法进行运行测试
public class AppForInstanceOrder {public static void main(String[] args) {ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");OrderDao orderDao = (OrderDao) ctx.getBean("orderDao");orderDao.save();} }
- 静态工厂方法的意义?
- 主要的原因是在工厂的静态方法中,我们除了new对象还可以做其他的一些业务操作,这些操作必不可少(直接new对象的方式就无法添加其他的业务内容),比如:
public class OrderDaoFactory {public static OrderDao getOrderDao(){System.out.println("factory setup....");//模拟必要的业务操作return new OrderDaoImpl();} }
- 主要的原因是在工厂的静态方法中,我们除了new对象还可以做其他的一些业务操作,这些操作必不可少(直接new对象的方式就无法添加其他的业务内容),比如:
- 静态工厂实例化一般是用来兼容早期的一些老系统,所以了解为主。
🐇实例工厂与FactoryBean
- 步骤1:准备一个UserDao和UserDaoImpl类。
public interface UserDao {public void save(); }public class UserDaoImpl implements UserDao {public void save() {System.out.println("user dao save ...");} }
- 步骤2:创建一个【实例工厂】类OrderDaoFactory并提供一个普通方法(注意此处和静态工厂的工厂类不一样的是不是静态方法)。
public class UserDaoFactory {public UserDao getUserDao(){return new UserDaoImpl();} }
- 步骤3:在spring的【配置】文件中添加以下内容↓
<!--方式三--> <bean id="userFactory" class="com.itheima.factory.UserDaoFactory"/> <bean id="userDao" factory-method="getUserDao" factory-bean="userFactory"/>
- 步骤4:在AppForInstanceUser运行类,使用从IoC容器中获取bean的方法进行运行测试。
public class AppForInstanceUser {public static void main(String[] args) {ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");UserDao userDao = (UserDao) ctx.getBean("userDao");userDao.save();} }
-
以上就是实例工厂实例化的方式,Spring为了简化这种配置方式提供了一种叫FactoryBean的方式来简化开发。
-
步骤1:【创建一个UserDaoFactoryBean的类】,【实现FactoryBean接口】,重写接口的方法。
public class UserDaoFactoryBean implements FactoryBean<UserDao> {//【代替】原始实例工厂中创建对象的方法public UserDao getObject() throws Exception {return new UserDaoImpl();}//返回所创建类的Class对象public Class<?> getObjectType() {return UserDao.class;} }
- 这里造出的对象【默认是单例】的,如果要改成非单例,则加上下
public boolean isSingleton() {return false; }
- 这里造出的对象【默认是单例】的,如果要改成非单例,则加上下
-
步骤2:在Spring的配置文件中进行配置。
<!--方式四--> <bean id="userDao" class="com.itheima.factory.UserDaoFactoryBean"/>
-
步骤3:AppForInstanceUser运行类不用做任何修改,直接运行。
📚bean的生命周期
⭐️下述内容涉及到的环境配置↓
- 项目中添加BookDao、BookDaoImpl、BookService和BookServiceImpl类。
public interface BookDao {public void save(); }public class BookDaoImpl implements BookDao {public void save() {System.out.println("book dao save ...");} }public interface BookService {public void save(); }public class BookServiceImpl implements BookService{private BookDao bookDao;public void setBookDao(BookDao bookDao) {this.bookDao = bookDao;}public void save() {System.out.println("book service save ...");bookDao.save();} }
- resources下提供spring的配置文件。
<?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.itheima.dao.impl.BookDaoImpl"/> </beans>
- 编写AppForLifeCycle运行类,加载Spring的IoC容器,并从中获取对应的bean对象。
public class AppForLifeCycle {public static void main( String[] args ) {ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");BookDao bookDao = (BookDao) ctx.getBean("bookDao");bookDao.save();} }
🐇生命周期设置
- 生命周期:从创建到消亡的完整过程。
- 在上面这个环境中来为BookDao添加生命周期的控制方法,具体的控制有两个阶段:
- bean创建之后,想要添加内容,比如用来初始化需要用到资源。
- bean销毁之前,想要添加内容,比如用来释放用到的资源。
- 步骤1:添加初始化和销毁方法。在BooDaoImpl类中分别添加两个方法(方法名任意)。
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...");} }
- 步骤2:配置生命周期。
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" init-method="init" destroy-method="destory"/>
- 步骤3:AppForLifeCycle.java运行。
public class AppForLifeCycle {public static void main( String[] args ) {ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");BookDao bookDao = (BookDao) ctx.getBean("bookDao");bookDao.save();} }
- 运行AppForLifeCycle打印结果为:
- init方法执行了,但是destroy方法却未执行?
- Spring的IoC容器是运行在JVM中。
- 运行main方法后,JVM启动,Spring加载配置文件生成IoC容器,从容器获取bean对象,然后调方法执行。
- main方法执行完后,JVM退出,这个时候IoC容器中的bean还没有来得及销毁就已经结束了,所以没有调用对应的destroy方法。
🐇销毁操作未调用解决方法
-
【方法一】:
close()
关闭容器public class AppForLifeCycle {public static void main( String[] args ) {//ApplicationContext中没有close方法,将ApplicationContext更换成ClassPathXmlApplicationContextClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");BookDao bookDao = (BookDao) ctx.getBean("bookDao");bookDao.save();//关闭容器(这句话一出现就直接暴力关闭)ctx.close();} }
-
【方法二】:注册钩子关闭容器,
registerShutdownHook()
。在容器未关闭之前,提前设置好回调函数,让JVM在退出之前回调此函数来关闭容器。public class AppForLifeCycle {public static void main( String[] args ) {//registerShutdownHook在ApplicationContext中也没有ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");//注册关闭钩子函数,在虚拟机退出之前回调此函数,关闭容器(这句代码可以往后放,位置放哪不影响)ctx.registerShutdownHook();BookDao bookDao = (BookDao) ctx.getBean("bookDao");bookDao.save();} }
-
👀比较上述两种方法。
- 相同点:这两种都能用来关闭容器。
- 不同点:
close()
是在调用的时候关闭,registerShutdownHook()
是在JVM退出前调用关闭。
🐇Spring提供的生命周期控制接口
- Spring提供了两个接口来完成生命周期的控制,好处是可以不用再进行配置
init-method
和destroy-method
。 - 在BookServiceImpl完成这两个接口的使用:修改BookServiceImpl类,添加两个接口
InitializingBean
,DisposableBean
并实现接口中的两个方法afterPropertiesSet
和destroy
。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");} }
- 重新运行AppForLifeCycle类↓
⭐️小细节
- 对于InitializingBean接口中的
afterPropertiesSet
方法,翻译过来为属性设置之后
,对于BookServiceImpl来说,bookDao
是它的一个属性,setBookDao
方法是Spring的IoC容器为其注入属性的方法。 - 思考:afterPropertiesSet和setBookDao谁先执行?
- 验证——在setBookDao方法中添加一句话↓
public void setBookDao(BookDao bookDao) {System.out.println("set .....");this.bookDao = bookDao; }
- 重新运行AppForLifeCycle,打印结果如下↓
- 所以初始化方法会在类中属性设置之后执行。
- 验证——在setBookDao方法中添加一句话↓
⭐️对于bean的生命周期控制在bean的整个生命周期中所处的位置如下↓