天天看點

Spring bean的作用域Spring Bean Scope

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