天天看点

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