Spring Bean Scope
文档参考:
https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#beans-factory-scopes
Bean的Scope 什么是?Bean的Scope定义了Bean的生命周期和在上下文中的可见性。
在Spring框架中共定义了6种Scope;
- singleton
- prototype
- request
- session
- application
- websocket
最后四个:request, session, application, websocket,只有在web应用中才能够被使用到。
本文用到的两只动物
public class Dog {
public Dog() {
System.out.println("Dog init...");
}
}
public class Cat {
public Cat() {
System.out.println("Cat init...");
}
}
1. Singleton Scope
用Singleton Scope来定义一个bean,意味着容器创建一个单例的bean。虽有对它的请求都会返回相同的对象,任何一方进行了修改,都会响应到其他使用的地方。如果不指定其他scope,singleton是默认的scope。
例子:
@Configuration
public class AppConfig {
@Bean
public Dog dog(){
return new Dog();
}
}
public class TestDemo1 {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
Dog dog = (Dog)context.getBean("dog");
Dog dog1 = (Dog)context.getBean("dog");
System.out.println(dog);
System.out.println(dog1);
}
}
打印结果
Dog init...
[email protected]
[email protected]
2. Prototype Scope
用Prototype Scope定义的bean,每次容器请求都会创建一个新的bean;
在之前的AppConfig .java文件中添加一个新的bean,标示为prototype scope.
@Configuration
public class AppConfig {
@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Dog dog(){
return new Dog();
}
}
打印结果
Dog init...
Dog init...
[email protected]
[email protected]
3. Singleton Beans with Prototype-bean Dependencies
在Singleton 当中引用了一个Prototype的bean的时候引发的问题,参考文档:https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#beans-factory-method-injection
简单举个例子:
// 定义一个service
@Service
public class AnimalService {
@Autowired
private Dog dog;
@Autowired
private Cat cat;
public void animal() {
System.out.println("cat" + cat.hashCode());
System.out.println("dog:" + dog.hashCode());
System.out.println("animal:" + this.hashCode());
}
}
// 添加我们的配置类
@Configuration
@ComponentScan("com.good") // 为了引入AnimalService
public class AppConfig {
@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Dog dog(){
return new Dog();
}
@Bean
public Cat cat(){
return new Cat();
}
}
// 执行我们的测试方法
public class TestDemo2 {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new
AnnotationConfigApplicationContext(AppConfig.class);
AnimalService animalService = (AnimalService)context.getBean("animalService");
animalService.animal();
animalService.animal();
}
}
返回的结果:
Dog init...
Cat init...
cat398572781
dog:765284253
animal:1077199500
cat398572781
dog:765284253
animal:1077199500
Dog不乐意了,明明是PROTOTYPE类型的,为啥就实例化一只Dog。下面来看看官方给的解决方案:
- 方案一:通过实现ApplicationContextAware,获取bean
@Service
public class Animal1Service implements ApplicationContextAware {
private ApplicationContext applicationContext;
public void animal() {
Dog dog = (Dog) this.applicationContext.getBean("dog");
Cat cat = (Cat) this.applicationContext.getBean("cat");
System.out.println("cat" + cat.hashCode());
System.out.println("dog:" + dog.hashCode());
System.out.println("animal:" + this.hashCode());
}
public void setApplicationContext(
ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
// 这时候再执行测试方法的结果
public class TestDemo2 {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new
AnnotationConfigApplicationContext(AppConfig.class);
Animal1Service animalService = (Animal1Service)context.getBean("animal1Service");
animalService.animal();
animalService.animal();
}
}
Dog表示很满意
Dog init...
Cat init...
Dog init...
cat1054932644
dog:1213349904
animal:1259769769
Dog init...
cat1054932644
dog:444920847
animal:1259769769
- 方案二: 通过@Lookup注解
@Service
public abstract class Animal2Service {
@Autowired
private Cat cat;
@Lookup(value = "dog") // 默认名称,则可以忽略
protected abstract Dog createDog();
public void animal() {
Dog dog = createDog();
System.out.println("cat:" + cat.hashCode());
System.out.println("dog:" + dog.hashCode());
System.out.println("animal:" + this.hashCode());
}
}
public class TestDemo2 {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new
AnnotationConfigApplicationContext(AppConfig.class);
Animal2Service animalService = (Animal2Service)context.getBean("animal2Service");
animalService.animal();
animalService.animal();
}
}
执行结果:
Cat init...
Dog init...
Dog init...
cat:63001505
dog:191037037
animal:330084561
Dog init...
cat:63001505
dog:1043351526
animal:330084561
Dog表示也很满意
4. @Lazy: bean的懒加载
简单就是一句话,在容器启动的时候,并不会初始化这个bean,等到调用的时候,才会。比如
@Bean
@Lazy
public Mouse mouse(){
return new Mouse();
}
测试类:
public class TestDemo2 {
public static void main(String[] args) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(AppConfig2.class);
System.out.println("==== context started =====");
context.getBean("mouse");
}
}
打印的结果:
Cat init...
Dog init...
==== context started =====
Mouse constructor....