java 单例 生命周期_Spring bean单例、多例模式与生命周期

Spring中的单例模式和多例模式

单例模式

每个bean定义只生成一个对象实例,每次getBean请求获得的都是此实例

单例模式分为饿汉模式和懒汉模式

饿汉模式:spring singleton的缺省是饿汉模式:启动容器时(即实例化容器时),为所有spring配置文件中定义的bean都生成一个实例

懒汉模式:在第一个请求时才生成一个实例,以后的请求都调用这个实例

在网上有许多这样的概念,你也一定烦了吧,我们用代码来看清楚这个过程

为了展示过程,我们需要引入log4j的jar包,把过程打印在控制台中

BigKingTest类中的代码如下:

package top.bigking.test;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import top.bigking.bean.HelloService;

public class BigKingTest {

public static void main(String[] args) {

System.out.println("开始初始化容器");

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");

System.out.println("初始化容器结束");

HelloService helloService = (HelloService) applicationContext.getBean("helloService");

HelloService helloService1 = (HelloService) applicationContext.getBean("helloService");

helloService.sayHello();

helloService1.sayHello();

System.out.println(helloService == helloService1);

}

}

application.xml的配置如下:

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">

控制台的输出如下:

1 2 开始初始化容器

3 DEBUG [main] - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@3b95a09c

4 DEBUG [main] - Loaded 1 bean definitions from class path resource [application.xml]

5 DEBUG [main] - Creating shared instance of singleton bean 'helloService'

6 初始化容器结束

7 Hello, World

8 Hello, World

9 true

10

11 Process finished with exit code 0

可以看到,在初始化容器时,helloService的bean对象实例就已经被创建了,后面的两次getBean都无法创建新的实例,而是直接使用这个实例,所以返回了true

以上,即可证明饿汉模式:启动容器时(即实例化容器时),为所有spring配置文件中定义的bean都生成一个实例,每次getBean请求获得的都是此实例

接下来我们证明懒汉模式:

修改application.xml,添加default-lazy-init属性

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">

运行结果如下:

1 2 开始初始化容器

3 DEBUG [main] - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@3b95a09c

4 DEBUG [main] - Loaded 1 bean definitions from class path resource [application.xml]

5 初始化容器结束

6 DEBUG [main] - Creating shared instance of singleton bean 'helloService'

7 Hello, World

8 Hello, World

9 true

10

11 Process finished with exit code 0

很明显可以看到,在容器初始化结束后,helloService实例才随着getBean被创建了出来,这样就证明了懒汉模式:在第一个请求时才生成一个实例,以后的请求都调用这个实例

多例模式

任何一个实例都是新的实例,调用getBean时,就new一个新实例

应该注意的是:多例模式中,实例只会随着getBean而创建,不会随着容器初始化而创建!也就是说,多例模式只有懒汉!

默认情况下scope="singleton",那么该Bean是单例;若写成scope="prototype",则为多例,任何一个实例都是新的实例;

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">

运行看一下结果:

1 2 开始初始化容器

3 DEBUG [main] - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@3b95a09c

4 DEBUG [main] - Loaded 1 bean definitions from class path resource [application.xml]

5 初始化容器结束

6 Hello, World

7 Hello, World

8 false

9

10 Process finished with exit code 0

可以看到,最后的false

也就是说,这是两个被实例化的对象,是不同的!

有人可能会问,为什么控制台中,没有打印出 对象被实例化的相关信息呢?

这是因为:多例模式的对象,不归IOC容器管理!

bean生命周期

对于普通的Java对象,当new的时候创建对象,当它没有任何引用的时候被垃圾回收机制回收。而由Spring IoC容器托管的对象,它们的生命周期完全由容器控制。Spring中每个Bean的生命周期如下:

16d7332aa783

1. 实例化Bean

对于BeanFactory容器,当客户向容器请求一个尚未初始化的bean时,或初始化bean的时候需要注入另一个尚未初始化的依赖时,容器就会调用createBean进行实例化。

对于ApplicationContext容器,当容器启动结束后,便实例化所有的bean。

容器通过获取BeanDefinition对象中的信息进行实例化。并且这一步仅仅是简单的实例化,并未进行依赖注入。

实例化对象被包装在BeanWrapper对象中,BeanWrapper提供了设置对象属性的接口,从而避免了使用反射机制设置属性。

2. 设置对象属性(依赖注入)

实例化后的对象被封装在BeanWrapper对象中,并且此时对象仍然是一个原生的状态,并没有进行依赖注入。

紧接着,Spring根据BeanDefinition中的信息进行依赖注入。

并且通过BeanWrapper提供的设置属性的接口完成依赖注入。

3. 注入Aware接口

紧接着,Spring会检测该对象是否实现了xxxAware接口,并将相关的xxxAware实例注入给bean。

4. BeanPostProcessor

当经过上述几个步骤后,bean对象已经被正确构造,但如果你想要对象被使用前再进行一些自定义的处理,就可以通过BeanPostProcessor接口实现。

该接口提供了两个函数:

postProcessBeforeInitialzation( Object bean, String beanName )

当前正在初始化的bean对象会被传递进来,我们就可以对这个bean作任何处理。

这个函数会先于InitialzationBean执行,因此称为前置处理。

所有Aware接口的注入就是在这一步完成的。

postProcessAfterInitialzation( Object bean, String beanName )

当前正在初始化的bean对象会被传递进来,我们就可以对这个bean作任何处理。

这个函数会在InitialzationBean完成后执行,因此称为后置处理。

5. InitializingBean与init-method

当BeanPostProcessor的前置处理完成后就会进入本阶段。

InitializingBean接口只有一个函数:

afterPropertiesSet()

这一阶段也可以在bean正式构造完成前增加我们自定义的逻辑,但它与前置处理不同,由于该函数并不会把当前bean对象传进来,因此在这一步没办法处理对象本身,只能增加一些额外的逻辑。

若要使用它,我们需要让bean实现该接口,并把要增加的逻辑写在该函数中。然后Spring会在前置处理完成后检测当前bean是否实现了该接口,并执行afterPropertiesSet函数。

当然,Spring为了降低对客户代码的侵入性,给bean的配置提供了init-method属性,该属性指定了在这一阶段需要执行的函数名。Spring便会在初始化阶段执行我们设置的函数。init-method本质上仍然使用了InitializingBean接口。

6. DisposableBean和destroy-method

和init-method一样,通过给destroy-method指定函数,就可以在bean销毁前执行指定的逻辑。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值