⭐️前言⭐️
在前边的文章中我们了解到了如何在Spring
中存储和取出Bean
对象,接下来这篇文章,主要是来深入学习了解Bean
对象。
博客主页: 【如风暖阳】
精品Java专栏【JavaSE】、【备战蓝桥】、【JavaEE初阶】、【MySQL】、【数据结构】
欢迎点赞 收藏 ⭐留言评论 私信必回哟本文由 【如风暖阳】 原创,首发于 CSDN
博主将持续更新学习记录收获,友友们有任何问题可以在评论区留言
博客中涉及源码及博主日常练习代码均已上传码云(gitee)、GitHub
如果有一个公共的Bean,提供给A用户和B用户使用,然而在使用途中A用户修改了公共Bean的数据,导致B用户在使用时发生了预期之外的问题。
我们预期的结果是,公共Bean可以在各自的类中被修改,但不能影响到其他类。
公共Bean:
@Component
public class UserBeans {
@Bean(name="u1")
public User getUser1() {
User user=new User();
user.setId(1);
user.setName("张三");
return user;
}
}
A用户使用时,进行了修改操作:
@Component
public class BeanScope1 {
@Autowired
private User user1;
public User getUser() {
User user=user1;
user.setName("李四");
return user;
}
}
B用户再去使用公共Bean:
@Component
public class BeanScope2 {
@Autowired
private User user1;
public User getUser() {
User user=user1;
return user;
}
}
打印A用户和B用户公共Bean的值:
发现两个用户获取到的name都是修改过后的值。
产生上述问题的原因是因为Bean在默认情况下是单例状态(singleton),也就是所有人使用的都是这一个对象,要想不出现上边这样的问题,还需再对Bean的作用域进行深一步了解,下边我们一起来看:
限定程序中变量的可用范围叫做作用域,或者说在源代码中定义变量的某个区域就叫做作用域。
Bean的作用域是指Bean在Spring整个框架中的某种行为模式,比如singleton单例作用域,就表示Bean在整个Spring中只有一份,它是全局共享的,那么当其他人修改了这个值之后,另一个人读到的就是被修改的值。
Spring容器在初始化一个Bean实例的时候,同时会指定该实例的作用域。有以下六种:
1、singleton:单例作用域
该作用域为Spring默认选择的作用域
2、prototype:原型作用域(多例作用域)
每次对该作用域下的Bean的请求,都会创建新的实例
3、request:请求作用域
每次http请求会创建新的Bean实例,类似于prototype(限定SpringMVC中使⽤)
4、session:会话作用域
在一个http session
中,定义一个Bean实例(限定SpringMVC中使⽤)
5、application:全局作用域
在一个http servlet Context
中,定义一个Bean实例(限定SpringMVC中使⽤)
6、websocket:HTTP WebSocket 作用域
在⼀个HTTP WebSocket
的⽣命周期中,定义⼀个Bean实例(限定Spring WebSocket中使⽤)
单例作用域(singleton)VS全局作用域(application)
Spring Core
的作用域;application 是Spring Web
中的作用域使用@Scope
标签就可以声明Bean的作用域,该标签既可以修饰方法,也可以修饰类,有以下两种设置方式:
1、直接设置值:@Scope("prototype")
2、使用枚举设置:@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
比如设置Bean的作用域,如下代码所示:
@Component
public class UserBeans {
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Bean(name="user1")
public User getUser1() {
User user=new User();
user.setId(1);
user.setName("张三");
return user;
}
}
所谓生命周期就是指一个对象从诞生到销毁的整个生命过程,我们把这个过程就叫做一个对象的生命周期。
Bean的生命周期分为以下5大部分:
1、实例化(给bean分配内存空间)
2、设置属性(对象注入)
3、初始化
a)执行各种通知(执行各种Aware)
b)执行初始化的前置方法
c)执行构造方法,两种执行方式,一种是执行@PostConstruct
,另一种是执行init-method
d)执行初始化的后置方法
4、使用Bean
5、销毁Bean
a)@PreDestory
b)重写DisposableBean
接口方法
c)destory-method
其整个生命周期就像我们买房住房的流程一样,1实例化对应的就是我们买房子,先占据了空间;2设置属性对应的就是进行装修,完成基础的配置;3初始化对应的就是买家电(各种什么电视、冰箱、空调、洗衣机什么的),这步最复杂;4使用Bean对应的就是入住,在前边所有工作完成以后就可以使用了;5销毁Bean对应的就是卖房,在卖房前也要完成旧房的收拾工作。
执行流程图如下:
思考:为什么要先设置属性再进行初始化呢?
下边代码进行解释:
@Service
public class UserService {
public void sayHi() {
System.out.println("你好,Service");
}
}
@Controller
public class UserController {
private UserService userService;
//构造方法注入
@Autowired
public UserController(UserService userService) {
this.userService = userService;
}
}
在UserController
对象的构造方法中,调用到了UserService
对象,如果先进性初始化,因为UserService
对象并没有注入,将会导致空指针异常,所以Bean必须先设置属性,再进行初始化。
下边我们来通过代码演示Bean的生命周期:
public class BeanLifeComponent implements BeanNameAware {
@PostConstruct
public void postConstruct() {
System.out.println("执行了@PostConstruct");
}
public void init() {
System.out.println("执行了init-method");
}
public void use() {
System.out.println("使用bean");
}
@PreDestroy
public void preDestory() {
System.out.println("执行了@PreDestory");
}
@Override
public void setBeanName(String s) {
System.out.println("执行了Aware通知");
}
}
xml配置如下:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:content="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<content:component-scan base-package="com.beans">content:component-scan>
<bean id="beanLifeComponent" class="com.beans.BeanLifeComponent" init-method="init">bean>
beans>
调用类:
⭐️最后的话⭐️
总结不易,希望uu们不要吝啬你们的哟(^U^)ノ~YO!!如有问题,欢迎评论区批评指正