目录
一、bean生命周期的原理
二、Spring的单例&&多例模式
2.1 理论知识
2.2 代码论证
1.1 了解bean的生命周期
首先我们就要思考一个问题,那就是:
Spring是管理项目中所有的javabean对象;
那这些bean对象又是什么时候生?什么时候提供服务?什么时候销毁?
我们可以一起来看看这张bean的生命周期图: 运行逻辑是从下往上根据箭头来看的;
我们一起来分析一下:
第一步:
通过XML、Java annotation(注解)以及Java Configuration(配置类)
等方式加载Spring Bean;
第二步:
:解析Bean的定义。 在Spring容器启动过程中,会将Bean解析成Spring内部的BeanDefinition结构;
理解为:将spring.xml中的
标签转换成BeanDefinition结构 有点类似于XML解析;
第三步:
BeanDefinition:包含了很多属性和方法。
例如:id、class(类名)、scope、ref(依赖的bean)等等。
其实就是:
将bean(例如
)的定义信息存储到这个对应BeanDefinition相应的属性中;
例如:
第四步:
BeanFactoryPostProcessor:是Spring容器功能的扩展接口。
注释:
1)BeanFactoryPostProcessor在spring容器加载完BeanDefinition之后,
在bean实例化之前执行的;
2)对bean元数据(BeanDefinition)进行加工处理,
也就是BeanDefinition属性填充、修改等操作。
结论1:
当我们运行这行代码时:
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("/spring-context.xml");
会对Spring的配置文件进行建模,然后把bean标签转成BeanDefinition这个类,
这个时候BeanDefinition里面还没有值。
所以它会在被实例化之前被进行加工处理。
第五步:
BeanFactory:bean工厂。它按照我们的要求生产我们需要的各种各样的bean。
解释案例:
例:
BeanFactory -> List
BeanDefinition(id/class/scope/init-method)
foreach(BeanDefinition bean : List){
//根据class属性反射机制实例化对象
//反射赋值设置属性
}
第六步:
Aware感知接口:在实际开发中,经常需要用到Spring容器本身的功能资源;
具体应用案例:
BeanNameAware、ApplicationContextAware等等
BeanDefinition 实现了 BeanNameAware、ApplicationContextAware
结论2:
就是Aware感知接口帮助我们自己写的 bean对象 去获取到 spring容器 内部 内置对象。
第七步:
BeanPostProcessor:后置处理器。
在Bean对象实例化和引入注入完毕后,
再显示调用初始化方法的前后添加自定义的逻辑。(类似于AOP的绕环通知)
然后就完成Bean的创建;
第八步:
destory:销毁。
简单点做个总结整个生命周期的流程:
①:通过三种方式(配置文件、注解、配置类)将bean标签转成BeanDefinition对象;
③:通过BeanFactoryPostProcessor类,可以在初始化之前修改属性值;
④ :BeanFactory类进行bean实例化,也就是生产javabean;
⑤:Aware感知接口,能够在拿到Spring上下文中内部的资源对象;
⑥:BeanPostProcessor后置处理器,相当于环绕通知。
最后就是创建Bean成功,destory进行销毁。
然而Spring的javaBean是分为两种的,一种是单例,一种是多例;
我们在Spring配置文件中也是可以配置的:
scope="singleton" ---> 代表单例
scope="prototype" ---> 代表多例
不配置的话默认就是单例;
我们先来看看一段代码:
public static void main(String[] args) {
//实例化四个对象
Person p1 = new Person();
Person p2 = new Person();
Person p3 = new Person();
Person p4 = new Person();
//依次打印
System.out.println(p1);
System.out.println(p2);
System.out.println(p3);
System.out.println(p4);
}
打印结果:
我们由此思考一下:
一个类如果被使用了一百次会创建多少个对象?
如果这个项目一共有一万个类呢? 答:100w个对象
结论:创建了很多不必要的对象会极大的占用空间。
所以我们就需要用到了单例模式:
package com.leaf.beanLife;
public class Demo1 {
public static void main(String[] args) {
//实例化
// Person p1 = new Person();
// Person p2 = new Person();
// Person p3 = new Person();
// Person p4 = new Person();
Person p1 = Person.newInstance();
Person p2 = Person.newInstance();
Person p3 = Person.newInstance();
Person p4 = Person.newInstance();
System.out.println(p1);
System.out.println(p2);
System.out.println(p3);
System.out.println(p4);
}
}
/**
* 实体类:人类
* @author Leaf
*
* 2022年8月9日 上午3:41:38
*/
class Person{
//私有化构造方法,让其他地方不能创建;
private Person() {
}
//封装私有化的这个类
private final static Person p = new Person();
//封装一个方法,返回私有化的该类;
public static Person newInstance() {
return p;
}
}
通过我们私有化构造方法,修改部分代码用到了单例模式后,我们再次运行:
这样我们就算把一个类在一百个地方使用,使用的都是同一个对象;
再次思考:
整个项目一共有1w个类,Spring上下文需要创建多少个对象?
答:1w个对象
到这里我们也就明显知道单例模式与多例的区别了,
单例模式的优点:就是极大的节省了空间,省略了很多的没必要的对象创建。
所以Spring默认使用单例模式;
但是,Spring之所以还提供了多例模式供我们选择,
原因呢就是:单例模式一样存在弊端!
例如:变量污染的问题;
生活中的案例:给孩子买玩具:
A:买2份玩具,两个孩子都开心,不会有矛盾。
B:买1份玩具,两个孩子可能会有争执;
轮流玩,孩子"小明"先玩了一上午后,孩子"小白"再去玩感觉就不一样了,
觉得污染了,染上了泥巴什么的,就会有争吵。
两种买玩具的方式有利有弊,
买两份呢:钱包有压力。买一份呢:兄弟容易打架。
结论:
A就相当于多例模式,B就相当于单例模式。
所以Spring就视情况而定,提供两种模式供我们按情况来选择。
我们经过生活中的案例,已经把关于单例模式与多例模式的理论方面的理解 全部分析讲出来了,接着 我们就要用代码 实操 印证一下我们的结论了。
首先我们准备一些测试的准备类:
InstanceFactory:验证初始化javabean
package com.leaf.beanLife;
/**
* 为了印证 BeanPostProcessor 初始化javabean对象
* @author Leaf
*
* 2022年8月9日 上午5:06:39
*/
public class InstanceFactory {
public void init() {
System.out.println("初始化方法");
}
public void destroy() {
System.out.println("销毁方法");
}
public void service() {
System.out.println("业务方法");
}
}
ParamAction:验证单例模式与多例模式的区别
package com.leaf.beanLife;
import java.util.List;
import com.leaf.ioc.biz.UserBiz;
import impl.UserBizImpl1;
import impl.UserBizImpl2;
/**
* 印证单例模式与多例模式的区别
* @author Leaf
*
* 2022年8月9日 上午5:07:27
*/
public class ParamAction {
private int age;
private String name;
private List hobby;
private int num = 1;
// private UserBiz userBiz = new UserBizImpl1();
public ParamAction() {
super();
}
public ParamAction(int age, String name, List hobby) {
super();
this.age = age;
this.name = name;
this.hobby = hobby;
}
public void execute() {
// userBiz.upload();
// userBiz = new UserBizImpl2();
System.out.println("this.num=" + this.num++);
System.out.println(this.name);
System.out.println(this.age);
System.out.println(this.hobby);
}
}
Demo2:测试类
package com.leaf.beanLife;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
/*
* 测试类
* spring bean的生命週期
* spring bean的單例多例
*/
public class Demo2 {
// 体现单例与多例的区别
@Test
public void test1() {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("/spring-context.xml");
// ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/spring-context.xml");
ParamAction p1 = (ParamAction) applicationContext.getBean("paramAction");
ParamAction p2 = (ParamAction) applicationContext.getBean("paramAction");
// System.out.println(p1==p2);
p1.execute();
p2.execute();
// 单例时,容器销毁instanceFactory对象也销毁;多例时,容器销毁对象不一定销毁;
applicationContext.close();
}
// 体现单例与多例的初始化的时间点 instanceFactory
@Test
public void test2() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/spring-context.xml");
}
// BeanFactory会初始化bean对象,但会根据不同的实现子类采取不同的初始化方式
// 默认情况下bean的初始化,单例模式立马会执行,但是此时XmlBeanFactory作为子类,单例模式下容器创建,bean依赖没有初始化,只有要获取使用bean对象才进行初始化
@Test
public void test3() {
// ClassPathXmlApplicationContext applicationContext = new
// ClassPathXmlApplicationContext("/spring-context.xml");
Resource resource = new ClassPathResource("/spring-context.xml");
BeanFactory beanFactory = new XmlBeanFactory(resource);
// InstanceFactory i1 = (InstanceFactory) beanFactory.getBean("instanceFactory");
}
}
spring-context.xml:Spring配置文件
抽烟
烫头
大保健
然后我们依次测试:
①:体现单例与多例的区别:
下面这个就是相当于单例:
我们还要知道一个结论:
单例模式:容器生对象生,容器死对象死。
多例模式:使用时对象生,死亡跟着jvm垃圾回收机制走。
以上这些就是单例和多模式的区别啦。
并且bean的初始化时间点,除了跟bean管理模式(单例&多例)有关之外;
还跟BeanFactory的子类有关。
OK,以上就是Leaf今天带来的关于Spring管理的javabean的生命周期以及他的两种模式的知识分享啦,我们下期再见啦!!!