1. 观察案例
User:
publicclassUser{publicint id;publicString name;publicintgetId(){return id;}publicvoidsetId(int id){this.id = id;}publicStringgetName(){return name;}publicvoidsetName(String name){this.name = name;}@OverridepublicStringtoString(){return"User{"+"id="+ id +", name='"+ name +'\''+'}';}}
UserBean(公共的Bean):
importorg.springframework.context.annotation.Bean;importorg.springframework.stereotype.Component;@ComponentpublicclassUserBean{@Bean(name ="user1")publicUsergetUser(){User user =newUser();
user.setId(1);
user.setName("张三");return user;}}
UserController1 在使用时,进行了修改操作:
importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Controller;@ControllerpublicclassUserController1{@AutowiredprivateUser user1;publicUsergetUser1(){User user = user1;System.out.println("userController1 修改之前的 User: "+user);
user.setId(2);
user.setName("李四");return user;}}
UserController2 再去使用公共 Bean:
importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Controller;@ControllerpublicclassUserController2{@AutowiredprivateUser user1;publicUsergetUser2(){User user = user1;return user;}}
打印 UserController1 和 UserController2 公共 Bean 的值:
importbeans.UserController1;importbeans.UserController2;importorg.springframework.context.ApplicationContext;importorg.springframework.context.support.ClassPathXmlApplicationContext;publicclassApp{publicstaticvoidmain(String[] args){ApplicationContext context =newClassPathXmlApplicationContext("spring-config.xml");UserController1 userController1 = context.getBean("userController1",UserController1.class);User user1 = userController1.getUser1();System.out.println("userController1 修改之后的 User: "+user1);UserController2 userController2 = context.getBean("userController2",UserController2.class);User user2 = userController2.getUser2();System.out.println("userController2 在 userController1 修改之后读出的 User: "+user2);}}
结果:
原因:
Bean 作用域:bean 在 spring 整个框架中,默认所有人使用同一个对象,作用域默认是
singleton(单例模式)
2. 作用域定义
限定程序中变量的可⽤范围叫做作⽤域,或者说在源代码中定义变量的某个区域就叫做作⽤域。
⽽ Bean 的作⽤域是指 Bean 在 Spring 整个框架中的某种⾏为模式,⽐如 singleton 单例作⽤域,就表示 Bean 在整个 Spring 中只有⼀份,它是全局共享的,那么当其他⼈修改了这个值之后,那么另⼀个⼈读取到的就是被修改的值
3. Bean 的 6 种作用域
Spring 容器在初始化⼀个 Bean 的实例时,同时会指定该实例的作⽤域.
singleton
:单例模式(默认)
prototype
: 原型模式(多例模式)
request
: 请求作用域(Spring MVC)
session
: 会话作用域(Spring MVC)
application
: 全局作用域(Spring MVC)
websocket
: HTTP WebSocket 作用域(Spring WebSocket)
注意:
后 4 种状态是 Spring MVC 中的值,在普通的 Spring 项⽬中只有前两种
3.1 singleton
描述:
该作⽤域下的Bean在IoC容器中只存在⼀个实例:获取Bean(即通过applicationContext.getBean等⽅法获取)及装配Bean(即通过@Autowired注⼊)都是同⼀个对象。
场景:
通常⽆状态的Bean使⽤该作⽤域。⽆状态表示Bean对象的属性状态不需要更新
备注:
Spring默认选择该作⽤域
3.2 prototype
描述:
每次对该作⽤域下的Bean的请求都会创建新的实例:获取Bean(即通过applicationContext.getBean等⽅法获取)及装配Bean(即通过@Autowired注⼊)都是新的对象实例。
场景:
通常有状态的Bean 使⽤该作⽤域
3.3 request
描述:
每次http请求会创建新的Bean实例,类似于prototype
场景:
⼀次http的请求和响应的共享Bean
备注:
限定SpringMVC中使⽤
3.4 session
描述:
在⼀个http session中,定义⼀个Bean实例
场景:
⽤户回话的共享Bean, ⽐如:记录⼀个⽤户的登陆信息
备注:
限定SpringMVC中使⽤
3.5 application
描述:
在⼀个http servlet Context中,定义⼀个Bean实例
场景:
Web应⽤的上下⽂信息,⽐如:记录⼀个应⽤的共享信息
备注:
限定SpringMVC中使⽤
3.6 websocket
描述:
在⼀个HTTP WebSocket的⽣命周期中,定义⼀个Bea实例
场景:
WebSocket的每次会话中,保存了⼀个Map结构的头信息,将⽤来包裹客户端消息头。第⼀次初始化后,直WebSocket结束都是同⼀个Bean。
备注:
限定Spring WebSocket中使⽤
单例作⽤域(singleton)和全局作⽤域(application)区别
1:
singleton 是 Spring Core 的作⽤域;application 是 Spring Web 中的作⽤域;
2:
singleton 作⽤于 IoC 的容器,⽽ application 作⽤于 Servlet 容器
4. 设置 Bean 作用域
1:
使用枚举设置(类似于枚举,是常量的方式):
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
2:
直接设置值:
@Scope(“prototype”)
4.1 @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
4.2 @Scope(“prototype”)
5. Bean 生命周期
所谓的⽣命周期指的是⼀个对象从诞⽣到销毁的整个⽣命过程,我们把这个过程就叫做⼀个对象的⽣命周期.
Bean 的⽣命周期分为以下 5 ⼤部分:
1: 实例化(给 bean 分配内存空间)
2: 设置属性(对象注入)
3: 初始化
a)
执行各种通知(执行各种 Aware)
b)
执行初始化的前置方法(BeanPostProcessor)
c)
执行构造方法,两种执行方式,一种是执行 @PostConstruct,另一种实质性 init-method
d)
执行初始化的后置方法(BeanPostProcessor)
4: 使用 Bean
5: 销毁 Bean
(销毁容器的各种方法)
a)
@PreDestroy
b)
重写 DisposableBean 接口方法
c)
destroy-method
5.1 实例化和初始化的区别
实例化和属性设置是 Java 级别的系统“事件”,其操作过程不可⼈⼯⼲预和修改;⽽初始化是给开发者提供的,可以在实例化之后,类加载完成之前进⾏⾃定义“事件”处理
5.2 生命流程"案例"
Bean 的⽣命流程看似繁琐,但咱们可以以⽣活中的场景来理解它,⽐如我们现在需要买⼀栋房⼦,那么我们的流程是这样的:
- 先买房(实例化,从⽆到有);
- 装修(设置属性);
- 买家电,如洗⾐机、冰箱、电视、空调等([各种]初始化);
- ⼊住(使⽤ Bean);
- 卖出去(Bean 销毁)。
5.3 演示代码
importorg.springframework.beans.factory.BeanNameAware;importjavax.annotation.PostConstruct;importjavax.annotation.PreDestroy;//@ComponentpublicclassBeanLifeComponentimplementsBeanNameAware{@PostConstructpublicvoidpostConstruct(){System.out.println("执行 @PostConstruct");}publicvoidinit(){System.out.println("执行 init-method");}publicvoiduse(){System.out.println("使用 bean");}@PreDestroypublicvoidpreDestroy(){System.out.println("执行了 @PreDestroy");}publicvoidsetBeanName(String s){System.out.println("执行了 Aware 通知");}}
xml 配置:
<?xml version="1.0" encoding="UTF-8"?><beansxmlns="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-scanbase-package="beans"></content:component-scan><beanid="beanLifeComponent"class="beans.BeanLifeComponent"init-method="init"></bean></beans>
测试:
importorg.springframework.context.support.ClassPathXmlApplicationContext;publicclassApp2{publicstaticvoidmain(String[] args){ClassPathXmlApplicationContext context =newClassPathXmlApplicationContext("spring-config.xml");BeanLifeComponent beanLifeComponent = context.getBean("beanLifeComponent",BeanLifeComponent.class);
beanLifeComponent.use();
context.destroy();}}
结果:
版权归原作者 粉色的志明 所有, 如有侵权,请联系我们删除。