Bean的作用域和生命周期
目录
通过一个案例来理解Bean作用域的问题
Bean作用域定义
Bean的6种作用域
1.singleton
2.prototype
3.request
4.session
5.application
6.websocket
Bean的作用域的设置
Spring的执行流程
Bean的生命周期
通过一个案例来理解Bean作用域的问题
打开一个Spring core项目:
创建一个User的实体类:
package com.java.demo.entity; import lombok.Getter; import lombok.Setter; import lombok.ToString;//lombok @Setter @Getter @ToString public class User {private int id;public String name; }
创建一个UserBeans类,将User存入Spring容器:
package com.java.demo.component;import com.java.demo.entity.User; import com.java.demo.entity.User; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component;@Component public class UserBeans {@Beanpublic User user() {// 伪代码User user = new User();user.setId(1);user.setName("张三");return user;} }
创建一个UserController类,通过属性注入的方式获取到Bean对象:
在这段代码里获取到对象之后,在方法内部将user赋值给myUser,通过myUser对对象进行修改.
那么这里修改myUser会不会对user产生影响呢?
package com.java.demo.controller; import com.java.demo.entity.User; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import javax.annotation.PostConstruct; @Controller public class UserController {@Autowiredprivate User user;public void printUser() {System.out.println(user);// 修改 UserUser myUser = user;myUser.setName("悟空");System.out.println("myUser -> " + myUser);System.out.println("user -> " + user); } }
在创建一个UserController2:
在这里我们也获取User对应的Bean对象,我们预期得到的是张三,而不是修改之后的悟空.
package com.java.demo.controller; import com.java.demo.entity.User; import org.springframework.stereotype.Controller; import javax.annotation.Resource; @Controller public class UserController2 {@Resourceprivate User user;public void printUser2() {System.out.println("user -> " + user); } }
在启动类中获取到controller对象:
import com.java.demo.controller.UserController; import com.java.demo.controller.UserController2; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;public class App {public static void main(String[] args) {ApplicationContext context =new ClassPathXmlApplicationContext("spring-config.xml");UserController userController =context.getBean("userController",UserController.class);userController.printUser();UserController2 userController2 =context.getBean("userController2",UserController2.class);userController2.printUser2();} }
打印结果:
可以看到的是:UserController中myUser和user指向的是同一个对象,所以说都打印了悟空.
从最后一条打印结果可以看到:UserController2 获取的User的Bean对象是修改之后的悟空,而不是预期的张三,也就是说它们得到的是同一个Bean对象.
出现以上问题的原因就是:Bean默认情况下是单例状态,也就是所有人使用的都是同一个对象.使用单例可以很大程度上提高性能,所以在Spring中Bean的作用域默认也是singleton单例模式.
Bean作用域定义
限定程序中变量的可用范围叫做作用域.而Bean的作用域是指Bean在Spring整个框架中的某种行为模式.
比如singleton单例作用域就表示Bean在整个Spring中只有一份,它是全局共享的,当其他人修改了这个值后,另一个人读取到的就是修改之后的值.
Bean的6种作用域
Spring容器在初始化一个Bean的实例时,同时也会指定该实例的作用域.
Bean作用域(Scope)包括:
1.singleton
单例作用域,默认的作用域,出于性能的考虑.该作用域下的Bean在IoC容器中只存在一个实例.通常无状态的Bean使用该作用域.无状态表示Bean对象的属性状态不需要更新.
2.prototype
原型作用域(多例作用域),每次对该作用域下的Bean的请求都会创建新的实例,及获取Bean和装配Bean都是新的实例.通常有状态的Bean会使用该作用域.
3.request
请求作用域,每次Http请求,都会创建一个Bean对象.适用于Spring MVC/Spring Web.
4.session
会话作用域,每次Session会话共享一个Bean.适用于Spring MVC.
5.application
全局作用域,一个http servlet context 中共享一个Bean.适用于Spring MVC.
6.websocket
HTTP WebSocket作用域,网络长连接,只适用于Spring WebSocket项目.
Bean的作用域的设置
作用域的设置实在存储Bean对象的时候进行设置的,而不是在取的时候.
我们使用@Scope标签就可以来设置Bean的作用域.@Scope标签既可以修饰方法也可以修饰类.
有两种设置方式:
再次启动测试类:
Spring的执行流程
Spring的执行流程:启动Spring容器 ->实例化Bean(分配内存空间,从无到有) ->Bean注册到Spring中(存) ->将Bean装配到需要的类中(取).
Bean的生命周期
Bean从诞生到销毁的过程.
1.开辟内存空间:实例化.实例化不等于初始化.
2.设置属性(注入属性)
3.初始化
- 3.1各种通知
- 3.2初始化前置方法
- 3.3初始化方法(两种实现方式:xml方式或者是注解的方式)
- 3.4初始化后置方法
4.使用Bean
5.销毁Bean对象
代码演示:
package com.java.demo.component; import org.springframework.beans.factory.BeanNameAware; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy;public class BeanComponent implements BeanNameAware {@Overridepublic void setBeanName(String s) {System.out.println("执行了通知 BeanName ->" + s );}//xml方式的初始化方法public void myInit(){System.out.println("xml方式初始化");}@PostConstructpublic void doPostConstruct(){System.out.println("注解的初始化方法");}public void sayHi(){System.out.println("执行 sayHi()");}@PreDestroypublic void doPreDestroy(){System.out.println("do PreDestroy");}}
import com.java.demo.component.BeanComponent; import org.springframework.context.support.ClassPathXmlApplicationContext;public class App {public static void main(String[] args) {ClassPathXmlApplicationContext context =new ClassPathXmlApplicationContext("spring-config.xml");BeanComponent beanComponent =context.getBean("beanComponent",BeanComponent.class);beanComponent.sayHi();context.destroy();} }
打印: