目录
前景引入:
作用域定义
Bean的六种作用域
单例作用域(Singleton)VS 全局作用域(application)
如何设置 Bean 作用域?
Bean的生命周期
假设现在有⼀个公共的 Bean,提供给 A ⽤户和 B ⽤户使⽤,当A把这个公共Bean修改之后,会导致 B ⽤户获取到这个Bean的时候是修改之后的Bean。
案例如下:
公共Bean:
@Component
public class Users {
@Bean
public User user1() {
User user = new User();
user.setId(1);
user.setName("Java"); // 【重点:名称是 Java】
return user;
}
}
A用户使用时,进行了修改操作:
@Controller
public class BeanScopesController {
@Autowired
private User user1;
public User getUser1() {
User user = user1;
System.out.println("Bean 原 Name:" + user.getName());
user.setName("悟空"); // 【重点:进⾏了修改操作】
return user;
}
}
B用户使用公共Bean:
@Controller
public class BeanScopesController2 {
@Autowired
private User user1;
public User getUser1() {
User user = user1;
return user;
}
}
打印A用户和B用户所持有的公共Bean:
public class BeanScopesTest {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
BeanScopesController beanScopesController =
context.getBean(BeanScopesController.class);
System.out.println("A 对象修改之后 Name:" +
beanScopesController.getUser1().toString());
BeanScopesController2 beanScopesController2 =
context.getBean(BeanScopesController2.class);
System.out.println("B 对象读取到的 Name:" +
beanScopesController2.getUser1().toString());
}
}
运行结果:
原因分析:
操作以上问题的原因是因为 Bean 默认情况下是单例状态(singleton),也就是所有⼈的使⽤的都是同⼀个对象,之前我们学单例模式的时候都知道,使⽤单例可以很⼤程度上提⾼性能,所以在 Spring 中Bean 的作⽤域默认也是 singleton 单例模式。
限定程序中变量的可⽤范围叫做作⽤域,或者说在源代码中定义变量的某个区域就叫做作⽤域。
⽽ Bean 的作⽤域是指 Bean 在 Spring 整个框架中的某种⾏为模式,⽐如 singleton 单例作⽤域,就表示 Bean 在整个 Spring 中只有⼀份,它是全局共享的,那么当其他⼈修改了这个值之后,那么另⼀个⼈读取到的就是被修改的值
Spring 容器在初始化⼀个 Bean 的实例时,同时会指定该实例的作⽤域。Spring有 6 种作⽤域,最后四种是基于 Spring MVC ⽣效的:
Singleton
prototype
request
session
application
答: 可以通过 @Scope 注解来设置 Bean 的作用域,它的设置方式有以下两种
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
public class Users {
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Bean(name = "u1")
public User user1() {
User user = new User();
user.setId(1);
user.setName("Java"); // 【重点:名称是 Java】
return user;
}
}
所谓的⽣命周期指的是⼀个对象从诞⽣到销毁的整个⽣命过程,我们把这个过程就叫做⼀个对象的⽣命周期。
Bean 的⽣命周期分为以下 5 ⼤部分:
需要注意的是:第二步和第三步不能进行调换位置,因为可能在初始化时候用到Bean的属性。
如下所示,可能Bean会在初始化时候调用自己的方法,如果没有设置属性,那么就会导致程序出错。
@Service
public class UserService {
public UserService(){
System.out.println("调⽤ User Service 构造⽅法");
}
public void sayHi(){
System.out.println("User Service SayHi.");
}
}
@Controller
public class UserController {
@Resource
private UserService userService;
@PostConstruct
public void postConstruct() {
userService.sayHi();
System.out.println("执⾏ User Controller 构造⽅法");
}
}
下面举个买房子的例子,方便理解Bean的生命周期:
以下为代码演示Bean的生命周期过程:
spring-config.xml如下:(bean为BeanComponent)
Bean(BeanComponent)如下:
package com.java.demo.component;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.config.BeanPostProcessor;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
public class BeanCompont implements BeanNameAware, BeanPostProcessor {
@Override
public void setBeanName(String s) {
System.out.println("执行了通知 BeanName -> " + s);
}
/**
* xml 方式的初始化方法
*/
public void myInit() {
System.out.println("XML 方式初始化");
}
@PostConstruct
public void doPostConstruct() {
System.out.println("注解初始化方法");
}
public void sayHi() {
System.out.println("执行 sayHi()");
}
@PreDestroy
public void doPreDestroy() {
System.out.println("do PreDestroy");
}
// 初始化前置方法
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("do postProcessBeforeInitialization");
return bean;
}
// 初始化后置方法
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("do postProcessAfterInitialization");
return bean;
}
}
调用类:
package com.java.demo;
import com.java.demo.component.BeanCompont;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
BeanCompont compont = context.getBean("beanCompont", BeanCompont.class);
compont.sayHi();
context.close();
}
}
执行结果如下:
说明:这里同时用注解和xml方式进行Bean的初始化,注解方式的优先级是比较高的。