Bean的作用域和生命周期
Bean的作用域
我们先来看下面这段代码
首先是一个Dog类 (此处使用lombok来完成setter、getter、toString方法)
@Setter
@Getter
public class Dog {private String name;}
然后在DogBeanConfig类里面写一个返回Dog的方法,并将这个方法的返回对象存入Spring容器
@Component
public class DogBeanConfig {@Beanpublic Dog singleDog() {Dog dog = new Dog();return dog;}
}
然后在类里分别注入这个Dog对象
RestController
public class DogController {@Autowiredprivate Dog singleDog;@RequestMapping("/single")public String single(){Dog contextDog = (Dog)applicationContext.getBean("singleDog");return "dog:"+singleDog.toString()+",contextDog:"+contextDog;}
}
结果
多次访问, 得到的都是同⼀个对象, 并且 @Autowired 和 applicationContext.getBean() 也是同⼀个对象.(在打印对象时,Java的Object.toString()
方法(如果类没有重写这个方法)会返回对象的类名加上这个哈希码的十六进制表示) 快速查看对象的类型或唯一性时,这种输出非常有用。
这就是Bean的不同作⽤域了
Bean的六种作用域
(在Spring中⽀持6种作⽤域,后4种在Spring MVC环境才⽣效)
1. 单例模式:singleton(默认)-- 每次注入都是同一份对象。默认为这种模式也是Spring为了性能的考虑。
2. 原型模式:prototype -- 每次注入都是一个新的对象。
3. 请求作用域(适用于Spring MVC / Spring Web):request -- 每次http请求中共享一份Bean对象。
4. 回话作用域(适用于Spring MVC / Spring Web):session -- 每次会话(session)共享一份Bean对象。
5. 全局作用域(适用于Spring MVC / Spring Web):applicaton -- 每个http servlet context中共享一份Bean对象。
6. websocket:适用于Spring WebSocket项目。
Bean的作用域设置
Bean设置作用域使用 @Scope 注解
使用方法:
在@Scope 注解里加上作用域名,比如:@Scope("prototype") 此时被该注解修饰的类为原型模式。
定义⼏个不同作⽤域的Bean
package demo.beanscope;import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.web.context.annotation.ApplicationScope;
import org.springframework.web.context.annotation.RequestScope;
import org.springframework.web.context.annotation.SessionScope;/*** Created with IntelliJ IDEA.* Description:* User: 22652* Date: 2024-08-03* Time: 13:37*/
@Component
public class DogBeanConfig {@Beanpublic Dog dog() {Dog dog = new Dog();dog.setName("旺旺");return dog;}@Bean@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)public Dog singleDog() {Dog dog = new Dog();return dog;}@Bean@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)public Dog prototypeDog() {Dog dog = new Dog();return dog;}@Bean@RequestScopepublic Dog requestDog() {Dog dog = new Dog();return dog;}@Bean@SessionScopepublic Dog sessionDog() {Dog dog = new Dog();return dog;}@Bean@ApplicationScopepublic Dog applicationDog() {Dog dog = new Dog();return dog;}
}
@RequestScope 等同于 @Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)其他同理。
测试不同作⽤域的Bean取到的对象是否⼀样
package demo.beanscope;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** Created with IntelliJ IDEA.* Description:* User: 22652* Date: 2024-08-03* Time: 13:41*/
@RestController
public class DogController {@Autowiredprivate Dog singleDog;@Autowiredprivate Dog prototypeDog;@Autowiredprivate Dog requestDog;@Autowiredprivate Dog sessionDog;@Autowiredprivate Dog applicationDog;@Autowiredprivate ApplicationContext applicationContext;@RequestMapping("/single")public String single(){Dog contextDog = (Dog)applicationContext.getBean("singleDog");return "dog:"+singleDog.toString()+",contextDog:"+contextDog;}@RequestMapping("/prototype")public String prototype(){Dog contextDog = (Dog)applicationContext.getBean("prototypeDog");return "dog:"+prototypeDog.toString()+",contextDog:"+contextDog;}@RequestMapping("/request")public String request(){Dog contextDog = (Dog)applicationContext.getBean("requestDog");return"dog:"+requestDog.toString()+",contextDog:"+contextDog.toString();}@RequestMapping("/session")public String session(){Dog contextDog = (Dog)applicationContext.getBean("sessionDog");return"dog:"+sessionDog.toString()+",contextDog:"+contextDog.toString();}@RequestMapping("/application")public String application(){Dog contextDog = (Dog)applicationContext.getBean("applicationDog");return"dog:"+applicationDog.toString()+",contextDog:"+contextDog.toString();}
}
观察Bean的作⽤域
单例作⽤域: http://127.0.0.1:8080/single 多次访问, 得到的都是同⼀个对象, 并且 @Autowired 和 applicationContext.getBean() 也是同⼀个对象。
多例作⽤域: http://127.0.0.1:8080/prototype 观察ContextDog, 每次获取的对象都不⼀样(注⼊的对象在Spring容器启动时, 就已经注⼊了, 所以多次 请求也不会发⽣变化)
请求作⽤域: http://127.0.0.1:8080/request 在⼀次请求中, @Autowired 和 applicationContext.getBean() 也是同⼀个对象. 但是每次请求, 都会重新创建对象
会话作⽤域: http://127.0.0.1:8080/session 在⼀个session中, 多次请求, 获取到的对象都是同⼀个.
换⼀个浏览器访问, 发现会重新创建对象.(另⼀个Session)
Application作⽤域: http://127.0.0.1:8080/application在⼀个应⽤中, 多次访问都是同⼀个对象
Application scope就是对于整个web容器来说, bean的作⽤域是ServletContext级别的. 这个和 singleton有点类似,区别在于: Application scope是ServletContext的单例, singleton是⼀个 ApplicationContext的单例. 在⼀个web容器中ApplicationContext可以有多个. (了解即可)
Bean的生命周期
⽣命周期指的是⼀个对象从诞⽣到销毁的整个⽣命过程, 我们把这个过程就叫做⼀个对象的⽣命周期. Bean 的⽣命周期分为以下5个部分:
1. 实例化(为Bean分配内存空间)
2. 属性赋值(Bean注⼊和装配, ⽐如 @AutoWired )
3. 初始化
a. 执⾏各种通知, 如 BeanNameAware , BeanFactoryAware , ApplicationContextAware 的接⼝⽅法.
b. 执⾏初始化⽅法:
- xml定义 init-method
- 使⽤注解的⽅式 @PostConstruct
- 执⾏初始化后置⽅法( BeanPostProcessor )
4. 使⽤Bean
5. 销毁Bean
a. 销毁容器的各种⽅法, 如 @PreDestroy , DisposableBean 接⼝⽅法, destroymethod.
如图
//通知需要BeanNameAware接口
public class BeanComponent implements BeanNameAware {@Overridepublic void setBeanName(String s) {System.out.println("执行了通知: " + s);}public void myInit() {System.out.println("XML方式的初始化方法");}@PostConstructpublic void doPostConstruct() {System.out.println("注解方法的初始化方法");}public void myDestroy() {System.out.println("XML方式的销毁方法");}@PreDestroypublic void doPreDestroy() {System.out.println("注解方式的销毁方法");}public void sayHi() {System.out.println("执行 sayHi方法");}
}
下面我们来执行这个类。
public static void main(String[] args) {//启动容器ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");//从容器获取Bean对象BeanComponent beanComponent = context.getBean("beanComponent", BeanComponent.class);//执行Bean对象的内容beanComponent.sayHi();//销毁Bean对象的方法context.destroy();}