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代理模式。

有什麼不足的地方,希望大家指正出來,關於代理模式,我也會繼續補充。

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