bean的作用域和生命周期和后置处理器以及作用域对生命周期的影响~
创始人
2025-06-01 06:39:38
0

scope属性可以指定bean的作用范围:

在spring中可以通过配置bean标签的scope属性来指定bean的作用域范围,各取值含义参照表如下:

在这里插入图片描述

编写spring_test.xml文件:






编写其中的测试类:

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import poij.student;public class scope_test {@Testpublic void scopeText(){ApplicationContext ioc=new ClassPathXmlApplicationContext("spring-scope.xml");student student1=ioc.getBean(student.class);student student2=ioc.getBean(student.class);//通过比较student1和student2是否为同一个进而判断bean对象是否为单例System.out.println(student1.equals(student2));}
}

输出:

true

在本例测试中,我们并没有设置scope属性的值,它与设置scope=“singleton”,所输出结果是一样的,都是表示获取该bean所对应的对象都是同一个

设置scope属性的值是prototype,表示获取该bean所对应的对象都不是同一个


再次进行测试,输出结果如下所示;

false

bean的生命周期:

bean对象创建(调用无参构造)
给bean对象设置属性
bean对象初始化之前操作(由bean的后置处理器负责)
bean对象初始化(需要在配置bean时指定初始化方法)
bean对象初始化之后操作(由bean的后置处理器负责)
bean对象就绪可以使用
bean对象销毁(需要在配置bean时,指定销毁方法)
IOC容器关闭

创建实体类user:

package projo;public class User {private String username;private  Integer id;private  String password;private  Integer age;public String getUsername() {return username;}public Integer getId() {return id;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public void setId(Integer id) {System.out.println("生命周期2:依赖注入");this.id = id;}public void setUsername(String username) {this.username = username;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public User() {System.out.println("生命周期1:实例化");}@Overridepublic String toString() {return "User{" +"username='" + username + '\'' +", id=" + id +", password='" + password + '\'' +", age=" + age +'}';}public User(int id,String username,String password,int age) {this.age=age;this.password=password;this.id=id;this.username = username;}//创建初始化的过程public void initMethod(){System.out.println("生命周期3:初始化");}//创建销毁的过程public void destroyMethod(){System.out.println("生命周期4:销毁");}
}

spring_lifecycle.xml:




创建测试类:

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import projo.User;public class lifecycle {@Testpublic void test(){ApplicationContext ioc=new ClassPathXmlApplicationContext("spring_lifecycle.xml");User user=ioc.getBean(User.class);System.out.println(user);}
}

输出:

生命周期1:实例化
生命周期2:依赖注入
User{username='张三', id=1, password='124', age=16}

我们在User类中,不仅创建了生命周期,而且还创建了销毁和初始化的方法,但是在输出结果中,销毁和初始化方法中的内容并没有输出,原因是:如果我们想要调用初始化和销毁的方法,需要在spring.xml文件中通过bean标签中的init-method和destory-method属性指定的,如下所示:


再次测试,输出结果如下所示:

生命周期1:实例化
生命周期2:依赖注入
生命周期3:初始化
User{username='张三', id=1, password='124', age=16}

此时的初始化方法被调用了,但销毁方法为什么依然没调用呢?

那么销毁的方法什么时候才会执行呢?我们知道bean对象都是通过IOC容器创建和管理的,那么一旦IOC容器关闭就意味着对象被销毁了

那么容器怎么关闭呢?之前在java中我们都是通过调用close方法区关闭资源,那么这里也可以吗?

一试便知,如下所示,当我们尝试调用close方法想要关闭IOC容器时,却发现没有这个方法

在这里插入图片描述

原因是:ApplicationContext类中并没有提供刷新或者关闭容器的功能,而其功能是在子接口[ConfigurableApplicationContext]中提供的

解决方案:

将ApplicationContext类修改为ConfigurableApplicationContext

import org.junit.Test;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import projo.User;public class lifecycle {@Testpublic void test(){//获取IOC容器ConfigurableApplicationContext  ioc=new ClassPathXmlApplicationContext("spring_lifecycle.xml");//获取beanUser user=ioc.getBean(User.class);//输出beanSystem.out.println(user);//关闭IOCioc.close();//关闭容器}
}

输出如下所示:

当容器关闭时,对象被成功销毁

生命周期1:实例化
生命周期2:依赖注入
生命周期3:初始化
User{username='张三', id=1, password='124', age=16}
生命周期4:销毁

bean的作用域对生命周期的影响:

对上述的lifecycle类中,我们只保留获取IOC容器的代码,那么会输出什么?

输出如下所示:

生命周期1:实例化
生命周期2:依赖注入
生命周期3:初始化

通过输出结果我们会发现,生命周期的前三个方法是在获取IOC容器时,就执行的,并不是我们在获取bean时,才执行的,原因是,我们当前在IOC容器中配置的bean对象,默认是单例,那么就说明,我们根据这个IOC容器获取到的永远都是一个唯一的bean对象那么我们根本没必要在获取的时候再去创建它,直接在创建IOC容器的时候就将其创建好,以后使用的都是同一个

既然是与单例还是多例有关,那么如果我们在bean标签中将其设置为多例,bean对象的创建还能否在获取IOC容器时就执行呢?

如下所示:

将当前的bean对象通过scope属性设置为多例:

 

我们再次运行测试类,会发现什么都没输出

在这里插入图片描述

那么也就是说,我们通过将bean的作用域设置为多例,每次获取到的对象都是新的,那么也就没有必要在获取IOC容器的时候将其创建好

将获取bean对象的代码添加,我发现和上述不同的时,对象创建有关的三个方法都被调用,那么足以说明,当bean对象的作用域为多例的情况下,对象的创建是在获取bean对象时完成的

在这里插入图片描述

由此还有一点不知道大家是否注意到,此时我们即使调用了close方法,但是销毁的方法依然没有被调用,原因是:当我们将bean对象的作用域设置为多例的时候,其销毁的方法就不由IOC容器控制了

bean的后置处理器:

bean的后置处理器会在生命周期的初始化前后添加额外的操作,需要实现BeanPostProcessor接口,且配置到IOC容器中,需要注意的是,bean后置处理器不是单独针对某一个bean生效,而是针对IOC容器中所有的bean都会执行

创建新的类,使其实现BeanPostProcessor接口:

package process;import org.springframework.beans.factory.config.BeanPostProcessor;public class MyBeanPostProcessor implements BeanPostProcessor {
}

仅仅实现该接口,并没有实现其中的方法,我们会发现,也是没有任何的报错的,Ctrl+b跟进源码, 如下所示,我们发现该接口中的方法原来是使用default关键字修饰的,

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//package org.springframework.beans.factory.config;import org.springframework.beans.BeansException;
import org.springframework.lang.Nullable;public interface BeanPostProcessor {@Nullabledefault Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {return bean;}@Nullabledefault Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {return bean;}
}

那么这两个方法到底有什么作用呢?

看方法名postProcessBeforeInitialization[初始化前的后置处理],postProcessAfterInitialization[初始化后的后置处理],下面我们对其进行重写,输出一些信息,如下所示:

package process;import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;public class MyBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("MyBeanPostProcessor--- >后置处理器postProcessBeforeInitialization");//此方法在bean的生命周期初始化之前执行return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println("MyBeanPostProcessor--- >后置处理器postProcessAfterInitialization");//此方法在bean的生命周期初始化之后执行return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);}
}

你以为这就可以对bean对象进行处理了吗?当然不是,我们还需要将其配置到IOC容器中,如下所示:

 

此时进行测试,输出如下:

在这里插入图片描述

我们发现上述在MyBeanPostProcessor类中所添加的两条语句都被输出了,那么后置处理器到底是对那个bean起作用呢?答案是:对当前IOC容器中的每个bean起作用,它会对每个bean在初始化前后都添加额外的操作

相关内容

热门资讯

白银,还能不能买入? 最近白银市场出现了离谱的怪事,不是说白银大涨的事儿,而是说,白银大涨而带动了国内那只白银ETF期货基...
“全球大模型第一股”定了!智谱... 在“全球大模型第一股”的赛跑中,来自北京的智谱(北京智谱华章科技股份有限公司)率先撞线。12月30日...
低利率时代理财变局:存款“搬家... 本文来源:时代周报 作者:王苗苗图源:图虫创意今年5月,国有六大行定期存款一年期利率集体跌破1%关口...
明面是酒厂背地搞金融,酒企大佬... 白酒巨头们明面上是酒厂,背地里早就成了金融巨头。这都不算啥?热衷投资的白酒大佬更是紧追投资圈,茅台五...
云南威信县一天制止8起滥办酒席... 澎湃新闻消息,12月29日,云南威信县扎西镇联合县级多部门开展专项行动,一举成功制止8起滥办酒席行为...
Manus数十亿美元卖给Met... 近2年前,字节试图收购Manus,报价3000万美元遭到拒绝。如今,Meta将Manus收入囊中,价...
乌方袭击普京官邸?特朗普:普京... ▲新京报我们视频出品(ID:wevideo) 12月29日,美国佛罗里达州,俄方称乌方无人机试图袭击...
被错杀的泡泡玛特? 文丨小李飞刀三年大涨三十倍后,泡泡玛特在最近几个月下跌了40%,引发资本市场诸多疑虑与审视。从450...
总裁带董秘搞内幕交易,赣锋锂业... 2025.12.30本文字数:2231,阅读时长大约4分钟作者 |第一财经 杨佼一桩五年前的内幕交易...
3只新股盘初大涨 12月30日,3只新股上市,盘初均大幅上涨。 截至发稿, N双欣环保涨近240%, N强一股份涨逾2...