【深入浅出Spring6】第四期——实例化Bean和Bean的生命周期
创始人
2024-01-28 17:53:42
0

一、获取 Bean

  • Spring 提供了多种实例化Bean的方式:【只是表现形式不同,底层都是通过构造方法创建对象的】
    • 通过构造方法实例化 【最简单的方式直接声明bean
    • 通过简单工厂模式实例化 【定义一个简单模式工厂,然后通过工厂的静态方法获得Bean
    • 通过factory-bean实例化 【定义一个方法工厂,通过实例方法(需要创建对象才能调用)获取Bean
    • 通过FactoryBean接口实例化 【我们工厂类实现了FactoryBean 接口,声明工厂类的bean就能返回特定Bean的实例】

$ 通过构造方法实例化

  • 需求:我们创建一个普通的Bean,然后在配置文件中声明一下,最后测试是否可以成功获得Bean

编写一个Bean类 SpringBean.java

package com.powernode.spring6.bean;/*** @author Bonbons* @version 1.0*/
public class SpringBean {
}

编写我们的配置文件 spring.xml



编写测试方法 SpringBeanTest.java

@Testpublic void testInstantiation1(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");SpringBean sb = applicationContext.getBean("sb", SpringBean.class);System.out.println(sb);}

$ 通过简单工厂实例化

  • 在之前工厂模式专题我们可以知道,简单工厂就是通过一个静态方法来获取Bean对象

编写我们的测试类 Gun.java

package com.powernode.spring6.bean;/*** @author Bonbons* @version 1.0*/
public class Star {public Star() {System.out.println("调用了Star的无参构造方法。");}
}

编写我们的简单工厂类 StarFactory.java

package com.powernode.spring6.bean;/*** @author Bonbons* @version 1.0*/
public class StarFactory {// 在简单工厂中,通过静态方法获取我们的产品对象public static Star get(){return new Star();}
}

编写我们的配置文件


编写我们的测试方法

@Testpublic void testInstantiation2(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");Star star = applicationContext.getBean("starBean", Star.class);System.out.println(star);}

在这里插入图片描述

$ 通过 factory-bean 实例化

  • 利用工厂方法模式来获取Bean对象,与简单工厂模式的区别在于 获取对象的方法方法不是静态的 【需要创建这个工厂的Bean对象】
  • 需求:我们创建一个类和对应的工厂,在配置文件中演示如果通过工厂方法模式实例化我们的Bean

编写我们的 Gun.java

package com.powernode.spring6.bean;/*** 具体产品角色* @author Bonbons* @version 1.0*/
public class Gun {public Gun() {System.out.println("调用了Gun的无参构造方法。");}
}

编写我们的工厂方法类 GunFactory.java

package com.powernode.spring6.bean;/*** 具体工厂角色* @author Bonbons* @version 1.0*/
public class GunFactory {// 方法是实例的 >> 需要创建对象才能调用public Gun get(){return new Gun();}
}

配置我们的 bean


  • 第一步,我们要声明我们工厂的bean,因为要用到这个对象
  • 第二步,不需要指定class属性,通过 factory-beanfactory-method 两个属性指定了使用哪个对象的什么方法获取Bean

编写测试方法:

@Testpublic void testInstantiation3(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");Gun gun = applicationContext.getBean("gunBean", Gun.class);System.out.println(gun);}

在这里插入图片描述

  • 为什么此处还打印了 调用了Star的无参构造方法。
    • 因为我们生命Bean的时候scope使用了默认的 singleton >> 单例模式
    • 创建 Bean 对象的时机是初始化上下文的时候,也就是解析XML文件时就给XML文件中的所有Bean创建了对象。

$ 通过 FactoryBean 实例化

  • 属于第三种方法的简化版,我们只要将工厂实现了FactoryBean接口,就不用去配置 factory-beanfactory-method 两个属性
  • 在我们声明工厂Bean的时候,就会返回一个我们指定的普通Bean的实例
  • factory-bean会自动指向实现FactoryBean接口的类,factory-method会自动指向**getObject()**方法
  • 需求:通过一个类和对应的工厂类来演示

定义我们的具体角色类 Person.java

package com.powernode.spring6.bean;/*** @author Bonbons* @version 1.0*/
public class Person {public Person() {System.out.println("调用了Person的无参构造方法。");}
}

定义我们的具体工厂类 PersonFactory.java

package com.powernode.spring6.bean;import org.springframework.beans.factory.FactoryBean;/*** 工厂Bean >> 可以获得普通Bean* @author Bonbons* @version 1.0*/
public class PersonFactoryBean implements FactoryBean {@Overridepublic Person getObject() throws Exception {// 获取我们的Bean对象return new Person();}@Overridepublic Class getObjectType() {return null;}@Overridepublic boolean isSingleton() {// 在接口中就实现了这个方法,默认为 true >> 单例return FactoryBean.super.isSingleton();}
}

编写配置文件


编写我们的测试文件

@Testpublic void testInstantiation4(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");Person person = applicationContext.getBean("person", Person.class);System.out.println(person);}

在这里插入图片描述

$ BeanFactory 和 FactoryBean 的区别

  • BeanFactory

    • 工厂
    • Spring IoC 容器的顶级对象,被称为“Bean工厂”,负责创建Bean对象
  • FactoryBean

    • Bean
    • 能够辅助Spring实例化其它Bean对象的一个Bean。
  • 在Spring中,Bean可以分为两类:

    • 普通的Bean
    • 工厂Bean

$ 注入 Date 针对方法四的实现案例分析

  • 我们知道 Date 可以作为简单类型也可以作为非简单类型使用
    • 作为简单类型时,通过 value 传递参数值要采用特定的语法格式
    • 作为非简单类型时,我们通过 ref 传入指定日期的 Bean
  • 需求:接下来我们演示如何通过工厂类实现非简单类型日期的注入

编写我们的普通角色类 Person.java

package com.powernode.spring6.bean;
package com.powernode.spring6.bean;import java.util.Date;/*** @author Bonbons* @version 1.0*/
public class Student {// 为了演示如何注入Date类型 >> 定义一个私有日期类型的birthprivate Date birth;public void setBirth(Date birth) {this.birth = birth;}@Overridepublic String toString() {return "Student{" +"birth=" + birth +'}';}
}

编写我们的工厂类 DateFactory.java

package com.powernode.spring6.bean;import org.springframework.beans.factory.FactoryBean;import java.text.SimpleDateFormat;
import java.util.Date;/*** 使用工厂类获取我们的日期对象* @author Bonbons* @version 1.0*/
public class DataFactoryBean implements FactoryBean {// 通过构造方法传递我们要生成的日期private final String strDate;public DataFactoryBean(String srtDate) {this.strDate = srtDate;}@Overridepublic Date getObject() throws Exception {SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");// 格式化我们的日期字符串return sdf.parse(strDate);}@Overridepublic Class getObjectType() {return null;}
}

编写我们配置文件



  • 我们通过构造注入传入指定的日期,在工厂类中通过实例方法将字符串类型的日期转化为对应的格式
  • 再创建我们Student的bean的时候,将属性值注入我们工厂类获得的Bean

编写我们测试方法

@Testpublic void testStudent2(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");Student student2 = applicationContext.getBean("student2", Student.class);System.out.println(student2);}

在这里插入图片描述

可以看到我们输入的日期还是那个格式,只是通过工厂类把输入日期的格式变成了我们习惯的格式。

二、Bean的生命周期

🌔 1、什么是Bean的生命周期?

  • 从对象创建到销毁的一个过程
  • Spring 框架就是一个Bean的工厂,负责Bean对象的创建和销毁

🌔 2、那么一个Bean的一个完整生命周期都包括哪些部分?

  • 粗略的分,可以将生命周期分为五步
  • 考虑Bean后处理器,可以将生命周期分为七步
  • 再考虑接口的识别,可以将生命周期分为十步

$ 分为五步的生命周期

  • 都包含哪五步:
    • 第一步:实例化Bean
    • 第二步:Bean属性赋值
    • 第三步:初始化Bean
    • 第四步:使用Bean
    • 第五步:销毁Bean
  • 需求:需要我们自己编写初始化和销毁方法,并在配置文件中进行配置

编写我们的 User 类,我们通过User类演示Bean的这五步生命周期

package com.powernode.spring6.bean;import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;/*** Bean的生命周期被分为五步的情况* @author Bonbons* @version 1.0*/
public class User implements BeanNameAware, BeanClassLoaderAware, BeanFactoryAware, InitializingBean, DisposableBean {public User(){System.out.println("第一步,无参构造方法被调用");}private String name;public void setName(String name) {System.out.println("第二步,set方法被调用 >> 属性赋值");this.name = name;}// 让我们的User类去实现这三个接口 >> 用于在执行Bean后处理器的before方法之前调用public void initBean(){System.out.println("第三步,初始化Bean");}public void destroyBean(){System.out.println("第五步,销毁Bean");}
}

在配置文件中声明一下我们的Bean


  • 通过 init-method 属性指定我们的初始化方法
  • 通过 destroy-method 属性指定我们的销毁方法 【销毁不会自动执行,需要我们在测试方法中手动完成】

编写我们的测试方法

@Testpublic void testBeanLifecycleFive(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");User user = applicationContext.getBean("user", User.class);System.out.println("第四步,使用Bean" + user);// 需要我们手动销毁Bean,但是这个方法属于ClassPathXMLApplicationContext的,所以我们需要强转ClassPathXmlApplicationContext context = (ClassPathXmlApplicationContext) applicationContext;context.close();}
  • 只有正常关闭spring容器,bean的销毁方法才会被调用。
  • ClassPathXmlApplicationContext类才有close()方法,所以我们需要强制类型转换
    在这里插入图片描述

$ 分为七步的生命周期

  • 我们使用了Bean后处理器,就会在初始化Bean前后添加两个方法
    • 一个方法为Bean后处理器的 before 方法
    • 另一个方法为Bean后处理器的after方法
  • 我们这个Bean后处理器需要实现 BeanPostProcessor 接口中的方法,才能作为Bean后处理器使用
  • 而且这个Bean后处理器的作用范围是整个XML文件,配置后对整个XML文件中的bean都生效
  • 需求:我们写一个Bean后处理器,查看是否生命周期变成了七步

编写我们的Bean后处理器:LogBeanPostProcessor

package com.powernode.spring6.bean;import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.lang.Nullable;/*** 日志Bean后处理器 >> 用来演示初始化前后插入代码的* @author Bonbons* @version 1.0*/
public class LogBeanPostProcessor implements BeanPostProcessor {// bean 我们的bean对象、beanName 我们bean的名字@Nullablepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("执行Bean后处理器的before方法");return bean;}@Nullablepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println("执行Bean后处理器的after方法");return bean;}
}

在配置文件中配置我们的Bean后处理器



测试方法我们依旧使用我们上面五步生命周期的测试方法 >> 我们可以很清楚的看到生命周期变成了七步

在这里插入图片描述

$ 分为十步的生命周期

  • 在七步的基础上,增添了识别Bean是否实现了某些特定的接口
  • 如果实现了特定的接口,就调用接口中的方法,给我们传递一些与Bean相关的一些参数信息
  • 重新梳理一下,生命周期这十步都包括什么?
    • 第一步,实例化Bean
    • 第二步,Bean属性赋值
    • 第三步,检查Bean是否实现了Aware的相关接口,并设置相关依赖
      • BeanNameAware:通过setBeanName方法Spring会将Bean的名字传递给Bean
      • BeanClassLoaderAware:通过setBeanClassLoader方法Spring会将加载该Bean的类加载器传递给Bean
      • BeanFactoryAware :通过setBeanFactory方法Spring会将Bean工厂对象传递给Bean
    • 第四步,Bean后处理器的before执行
    • 第五步,检查Bean是否实现了InitializingBean接口,井调用接口方法 【afterPropertiesSet
    • 第六步,初始化Bean
    • 第七步,Bean后处理器的after执行
    • 第八步,使用Bean
    • 第九步,检查Bean是否实现了DisposableBean接口,井调用接口方法【destroy
    • 第十步,销毁Bean
  • 需求:让我们的User实现这三种接口中的方法,查看一下这十步的具体执行情况

让我们User类实现这五个接口 【第一种三个、第二种一个、第三种一个】

package com.powernode.spring6.bean;import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;/*** Bean的生命周期被分为五步的情况* @author Bonbons* @version 1.0*/
public class User implements BeanNameAware, BeanClassLoaderAware, BeanFactoryAware, InitializingBean, DisposableBean{public User(){System.out.println("第一步,无参构造方法被调用");}private String name;public void setName(String name) {System.out.println("第二步,set方法被调用 >> 属性赋值");this.name = name;}// 让我们的User类去实现这三个接口 >> 用于在执行Bean后处理器的before方法之前调用public void initBean(){System.out.println("第三步,初始化Bean");}public void destroyBean(){System.out.println("第五步,销毁Bean");}@Overridepublic void setBeanClassLoader(ClassLoader classLoader) {System.out.println("传递了类加载器" + classLoader);}@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {System.out.println("传递了创建这个Bean的工厂" + beanFactory);}@Overridepublic void setBeanName(String s) {System.out.println("传递了Bean的名字" + s);}@Overridepublic void destroy() throws Exception {System.out.println("DisposableBean's destroy method");}@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("InitializingBean's afterPropertiesSet method");}
}

配置文件、测试文件都不需要改变

在这里插入图片描述

$ 不同的作用域的管理方式不同

  • 对于上面的生命周期,是针对我们采用默认的作用域 singleton 而言的
  • 如果我们让 scope = prototype,那么spring的工作内容只负责到使用Bean,之后的工作内容交给我们的客户端
  • 需求:我们将上面十步生命周期的配置文件进行修改,将scope设置为多例模式

执行测试方法 >> Spring在完成Bean对象初始化之后,就不再追踪其生命周期了

在这里插入图片描述

$ 将我们自己创建的对象添加到Spring容器中

  • 需求:
    • 我们定义一个 Student 类,然后通过测试方法将这个类的实例添加到Spring容器中
    • 通过getBean再获取一下,看是否添加成功【返回同一个对象说明我们注入成功】

编写我们的Student

package com.powernode.spring6.bean;/*** @author Bonbons* @version 1.0*/
public class Student {
}

编写我们的测试方法:

@Testpublic void testRegisterBean(){// 我们自己创建一个对象Student student = new Student();System.out.println(student);// 创建一个可以将对象加入到Bean中的工厂DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();defaultListableBeanFactory.registerSingleton("studentBean", student);// 通过getBean方法获取我们的BeanStudent studentBean = defaultListableBeanFactory.getBean("studentBean", Student.class);System.out.println(studentBean);}

通过测试结果我们可以得出结论 >> 注入我们自己new的对象成功
在这里插入图片描述

相关内容

热门资讯

话费小本创业的项目 话费小本创... 小本创业项目:快餐小吃店话费小本创业的项目随着社会压力的增大,很多人没有时间本人做饭,为了工作会选择...
2014小本创业不容错过的项目... 2014小本创业做什么比较好?其实对于大部分创业者来说,小本创业更能取得成功,今天就给大家介绍几个小...
2014小本创业不容错过 卤中... 如今,随着近年来熟食行业的快速发展,给广大的消费者带来了美味,也给投资者带来了绝好的契机,卤中仙熟食...
小本创业好项目摆地摊做什么好?... 小本创业一直以来都是比较受普通大众所喜爱的,而很多人白手起家最开始做的就是摆地摊,不要瞧不起这个小本...
重访抗战地标丨英雄之城·不能忘...   为什么要永远铭记抗战英雄?因为当“中华民族到了最危险的时候”,“用血肉筑起新的长城”并不是艺术升...
各地高校持续实施“宏志助航计划...   央视网消息:当前正值高校毕业生就业关键期,教育部要求各地高校加快实施年度“宏志助航计划”,为离校...
2018小本创业好2017十个... 1、农户住宅设计业我们知道盖房是农民生活中的头等大事,随着社会经济的发展,农户住宅也向实用、美观、个...
环保创业好项目 环保行业有哪些... 酿酒蒸馏/餐饮食堂,全国90%传统锅炉需要改造。千亿市场商机等你来分享。具有竞争性独特产品,市场唯一...
2015年有哪些小本创业好项目... 对于普通大众来说,由于资金的限制,进行小本创业是最恰当的选择.都有什么项目适宜小本创业经营呢?  第...
创业小本好项目 创业小本好项目... 生活水平越来越高,这对于刚毕业的年轻人来说或许就产生了不小的压力,进入职场的时间不长,工资也没有太高...