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....