Spring(二):@Scope的使用

Scope:也叫做作用域,在Spring IoC中的作用是指它创建的Bean对象对于其他Bean对象的请求的可视范围,Spring IoC容器中定义5种常用作用域:

在Spring2.0:

    1).singleton:单实例的(Spring Ioc默认的方式),在IOC容器启动时就会调用方法创建对象放入IOC容器中,在容器中只存在一份。

    2).prototype:多实例的,在IOC容器启动时不会主动调用方法创建对象放入IOC容器中,而是每次获取的时候才会调用方法创建对象。

在Spring2.0之后新增了三种为支持web应用的ApplicationContext,增强另外三种

   1).request:同一次请求只创建一个对象,也就是会为每个Http请求创建一个对象,请求结束后该对象生命周期结束。

   2).session:对于每次HTTP Session,使用session定义的Bean都将产生一个新实例。

   3).global session:每个全局的HTTP Session,使用session定义的Bean都将产生一个新实例。

注意的是:上面三种只有在Web应用中使用Spring时,该作用域才会生效。

在实际应用中,我们常用的主要是singleton和prototype这两种:

创建一个maven项目,导入如下两个依赖:

<dependencies>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>4.1.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>

创建一个实体类:

package bean;

/**
 * @program: springannot
 * @description: bean
 * @author: han
 * @create: 2019-11-09 15:27
 * @xgr:
 * @description:
 **/
public class Person {
    private String name;
    private String age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age='" + age + '\'' +
                '}';
    }

    public Person(String name, String age) {
        this.name = name;
        this.age = age;
    }

    public Person() {
    }
}

1、Singleton:单实例

    创建一个配置类:用@Configuration注明为配置类,并注册到容器当中。用@Scope定义它的域,默认就是单实例的,也可以声明使用value属性。

@Configuration
public class MainConfig {

    @Bean(name = "person")
    @Scope
    public Person per() {
        System.out.println("创建person添加容器中");
        return new Person("张三", "18");
    }
}

1.1 :测试单实例是否为自动注入

package test;

import config.MainConfig;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;


public class TestScope {
    @Test
    public void test01(){
        //创建IOC
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
        System.out.println("容器创建完成");
        String[] names = applicationContext.getBeanDefinitionNames();
        for(String str : names){
            System.out.println("str:"+str);
            //可以看到除了Spring自己的Bean对象,也可以看到我们声明的Person对象。
        }
    }
}

在这里我们也证明了单实例的会在容器启动时默认的注册到容器中,但是这也不是绝对的我们能使用@Lazy懒加载(容器启动不创建对象。第一次使用(获取)Bean创建对象,并初始化。),相当于过去的xml文件中bean属性的lazy属性,@Lazy注解有个值为布尔类型的value属性,默认为true也就是使用懒加载。

@Configuration
public class MainConfig {

    @Bean(name = "person")
    @Scope
    @Lazy
    public Person per() {
        System.out.println("创建person添加容器中");
        return new Person("张三", "18");
    }
}

此时我们在使用test01,测试时会发现没用Person这个对象了,只有我们手动是得到这个Bean才会创建,对於单例的无论你get多少个对象,它也只存在一份,例如:

package test;

import config.MainConfig;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;


public class TestScope {
    @Test
    public void test01(){
        //创建IOC
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
        System.out.println("容器创建完成");
        String[] names = applicationContext.getBeanDefinitionNames();
        for(String str : names){
            System.out.println("str:"+str);
            //可以看到除了Spring自己的Bean对象,也可以看到我们声明的Person对象。

        Object o = applicationContext.getBean("person");
        Object o1 = applicationContext.getBean("person");

        System.out.println("o:"+o);
        System.out.println("o1:"+o1);
        System.out.println("o==o1:"+(o == o1));
        }
    }
}
打印日志:
创建person添加容器中
o:Person{name='张三', age='25'}
o1:Person{name='张三', age='25'}
o==o1:true

2、prototype:多实例的,在IOC容器启动时不会调用方法创建对象放在容器中,而是在每次获取的时候才会调用方法创建对象

我们还是使用之前的person,将@Scope的属性设置为prototype。

@Configuration
public class MainConfig {

    @Bean(name = "person")
    @Scope(value = "prototype")
    public Person per() {
        System.out.println("创建person添加容器中");
        return new Person("张三", "18");
    }
}

测试

package test;

import config.MainConfig;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;


public class TestScope {
    @Test
    public void test01(){
        //创建IOC
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
        System.out.println("容器创建完成");
 
        Object o = applicationContext.getBean("person");
        Object o1 = applicationContext.getBean("person");

        System.out.println("o:"+o);
        System.out.println("o1:"+o1);
        System.out.println("o==o1:"+(o == o1));
        }
    }

打印日志:
容器创建完成
创建person添加容器中
创建person添加容器中
o:Person{name='张三', age='18'}
o1:Person{name='张三', age='18'}
o==o1:false

我们能看到o==o1为false,说明在容器中每次声明得到Person时都会创建一个对象,在容器中会存在多份。

关于@Scope,点开源码我们看到文中讲到的@Scope,还有一个@proxyMode代理模式。

有什么不足的地方,希望大家指正出来,关于代理模式,我也会继续补充。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章