Spring註解版--spring給容器註冊組件的四種方法

Spring註解版–spring給容器註冊組件的四種方法:

這是我看網上視頻教程之後,自己整理的,加強記憶。

文章目錄

一. @Configuration&@Bean-自動掃描組件、掃描規則組件

1、創建一個webquick項目,內部添加pom依賴

<dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.1.2.RELEASE</version>
    </dependency>

給maven工程添加spring-context的依賴。

2、比較xml註冊bean與註解註冊bean

(1)xml配置註冊bean

1544768247996

IOC在項目中添加一個resources文件夾,內部存放所有的配置文件以及資源。現在我們在內部創建一個bean.xml這個IOC容器的xml配置文件。

bean.xml配置類代碼如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--<bean></bean>-->
    <bean id="user" class="com.xiaojian.bean.User">
        <property name="username" value="xiaojian"></property>
        <property name="userage" value="11"></property>
    </bean>
</beans>

如圖,我們給容器添加了一個bean,指向我們的一個User的實體類。此處的User的實體類,我們存放在java目錄下的bean文件夾中。
User實體類如下:

package com.xiaojian.bean;

public class User {

    private String username;

    private Integer userage;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Integer getUserage() {
        return userage;
    }

    public void setUserage(Integer userage) {
        this.userage = userage;
    }

    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                ", userage='" + userage + '\'' +
                '}';
    }

    public User(String username, Integer userage) {
        this.username = username;
        this.userage = userage;
    }

    public User() {
    }
}

我們在test目錄下面創建MainTest01測試類,測試獲取IOC容器中的bean。

測試類MainTest01:

package com.xiaojian.test;

import com.xiaojian.bean.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MainTest01 {

    public static void main(String[] args) {
        ApplicationContext ApplicationContext = new ClassPathXmlApplicationContext("bean.xml");
        User user = (User) ApplicationContext.getBean("user");
        System.out.println(user);
        }
}

測試結果:

1544768940929

(2)註解註冊bean

在config目錄下創建MainConfig類,此類爲創建bean的配置類。

package com.xiaojian.config;

import com.xiaojian.bean.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

//配置類==配置文件
@Configuration  //告訴spring這是一個配置類
public class MainConfig {

    //給容器註冊一個bean;類型爲返回值的類型;id默認是用方法名作爲id(不過我們也可以自己指定id,就是value值)
    @Bean(value = "user01")
    public User user01(){
        return new User("小賤",13);
    }
}

此class類文件中,@Configuration註解告訴spring這是一個配置類,配置類的作用就是xml配置文件的作用。

@Bean 註解是給容器註冊一個bean,類型爲返回值的類型,id默認是用方法名作爲id,不過我們可以自己定義id,也就是@Bean註解下value屬性值。

@Bean註解接口(系統內部的註解,我們直接使用):

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.context.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.core.annotation.AliasFor;

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {
    @AliasFor("name")
    String[] value() default {};

    @AliasFor("value")
    String[] name() default {};

    /** @deprecated */
    @Deprecated
    Autowire autowire() default Autowire.NO;

    boolean autowireCandidate() default true;

    String initMethod() default "";

    String destroyMethod() default "(inferred)";
}

此註解接口中定義了value這個屬性,就是bean的id。當使用value這個屬性的時候,所有通過id想要獲取此bean,必須通過value定義的id,而不是方法名。

我們在test目錄下再定義一個MainTest02這個類文件,測試註解方式註冊bean。

測試類MainTest02:

package com.xiaojian.test;

import com.xiaojian.bean.User;
import com.xiaojian.config.MainConfig;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MainTest02 {

    public static void main(String[] args) {

        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
        User user = (User) applicationContext.getBean("user01");
        System.out.println(user);

        //此處的方法是通過類型來獲取所有的bean的集合。
        Map<String, User> beansOfType = applicationContext.getBeansOfType(User.class);
        System.out.println(beansOfType);
        
        
        //此處的方法是通過類型來獲取Bean的名稱,此處是通過User這個類,來獲取Bean的名稱。
        String[] beanNamesForType = applicationContext.getBeanNamesForType(User.class);
        for (String name : beanNamesForType) {
            System.out.println(name);
        }
        
        
        //applicationContext下面還有好多的方法,可以有時間可以自己看看。
        
    }
}

運行結果:

1544770122075

3.@ComponentScan包掃描

(1)創建相關的類

首先在xiaojian這個文件夾下面分別創建controller、service、dao這三個文件夾,裏面在分別創建對應的BookController、BookService、BookDao這三個類文件,同時添加對應的註解。

BookController:

package com.xiaojian.controller;

import org.springframework.stereotype.Controller;

@Controller   //此註解說明這是一個控制器
public class BookController {
}

BookService:

package com.xiaojian.service;

import org.springframework.stereotype.Service;

@Service   //此註解作用是給容器掃描到,說明此類事服務層
public class BookService {
}

BookDao:

package com.xiaojian.dao;

import org.springframework.stereotype.Repository;

@Repository   //此註解是爲了給容器掃描到,說明此類是dao層。
public class BookDao {
}

(2)xml配置中的包掃描方式

xml中的配置方式:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!--  包掃描,只要標註了@Controller、@Service、@Repository、@Component中的任何一個,都會被掃描-->
    <context:component-scan base-package="com.xiaojian"></context:component-scan>
    <!--<bean></bean>-->
    <bean id="user" class="com.xiaojian.bean.User">
        <property name="username" value="xiaojian"></property>
        <property name="userage" value="11"></property>
    </bean>
</beans>

xml文件中通過<context:component-scan base-package="" />來掃描配置的bean。

(3)註解實現包掃描方式

我們只需要在@Configuration主配置類中添加@ComponentScan( value="com.xiaojian")就可以自動掃描xiaojian這個包下面的所有的添加了@Controller@Service、@Repository@Component

MainConfig主配置類:

package com.xiaojian.config;

import com.xiaojian.bean.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

//配置類==配置文件
@Configuration  //告訴spring這是一個配置類
@ComponentScan(value = "com.xiaojian")
//包掃描路徑,與在xml文件中配置 <context:component-scan base-package="com.xiaojian"></context:component-scan>  作用一樣。
public class MainConfig {

    //給容器註冊一個bean;類型爲返回值的類型;id默認是用方法名作爲id(不過我們也可以自己指定id,就是value值)
    @Bean(value = "user01")
    public User user01(){
        return new User("小賤",13);
    }
}

小的測試

在test目錄下添加一個測試類——MainTest03。

MainTest03:

package com.xiaojian.test;

import com.xiaojian.config.MainConfig;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MainTest03 {

    public static void main(String[] args) {

        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);

        //此方法可以獲取所有的bean定義的名字
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        for (String name:beanDefinitionNames){
            System.out.println(name);
        }

    }
}

測試結果:

1544772149159

(4)@ComponentScan屬性值

  1. value值:

    value值是說明此掃描的掃描路徑,系統只會掃描路徑下的所有的添加了@Controller@Service、@Repository@Component註解的類。

  2. excludeFilters屬性:

    產生過濾規則,此指定掃描的時候按照什麼規則排除哪些組件。此寫法是一個Filter數組。

    MainConfig:

    package com.xiaojian.config;
    
    import com.xiaojian.bean.User;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.FilterType;
    import org.springframework.stereotype.Controller;
    import org.springframework.stereotype.Service;
    
    //配置類==配置文件
    @Configuration  //告訴spring這是一個配置類
    @ComponentScan(value = "com.xiaojian",excludeFilters = {
            @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class, Service.class})
    })
    //此處的type中的 FilterType.ANNOTATION表明排除註解方式的配置bean,classes表明排除的配置bean添加的註解的類型(此處主要排除@Controller、@Service兩個註解的配置類)
    //包掃描路徑,與在xml文件中配置 <context:component-scan base-package="com.xiaojian"></context:component-scan>  作用一樣。
    public class MainConfig {
    
        //給容器註冊一個bean;類型爲返回值的類型;id默認是用方法名作爲id(不過我們也可以自己指定id,就是value值)
        @Bean(value = "user01")
        public User user01(){
            return new User("小賤",13);
        }
    }
    
    

    運行MainTest03測試類,測試結果:

    1544773543923

    系統的打印日誌中少了BookController、BookService這兩個。

  3. includeFilters屬性:

    產生過濾規則,此指定掃描的時候按照什麼規則只掃描哪些組件。此寫法是一個Filter數組。

    (與上面相反)

    不過此處需要注意,因爲在@ComponentScan中有個屬性useDefaultFilters,默認爲true,而我們使用includeFilters屬性的時候,需要將useDefaultFilters設置爲false。

    MainConfig:

    package com.xiaojian.config;
    
    import com.xiaojian.bean.User;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.FilterType;
    import org.springframework.stereotype.Controller;
    import org.springframework.stereotype.Service;
    
    //配置類==配置文件
    @Configuration  //告訴spring這是一個配置類
    @ComponentScan(value = "com.xiaojian",includeFilters = {
            @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class})
    },useDefaultFilters = false)  //必須把useDefaultFilters設置爲false才能使用includeFilters
    //包掃描路徑,與在xml文件中配置 <context:component-scan base-package="com.xiaojian"></context:component-scan>  作用一樣。
    public class MainConfig {
    
        //給容器註冊一個bean;類型爲返回值的類型;id默認是用方法名作爲id(不過我們也可以自己指定id,就是value值)
        @Bean(value = "user01")
        public User user01(){
            return new User("小賤",13);
        }
    }
    
    

    運行MainTest03測試類,測試結果:

    在這裏插入圖片描述

    此處只有一個bookController,因爲includeFilters中只有一個Controller通行。

    @ComponentScan是一個可重複寫的註解,我們可以在一個類上多次添加此註解。

    或者我們可以使用@ComponentScans註解,內部添加多個@ComponentScan

(5)@Filter註解type屬性指定掃描規則

  • FilterType.ANNOTATION:按照註解的方式(最常用)
  • FilterType.ASPECTJ:使用ASPECTJ表達式(幾乎不常用)
  • FilterType.REGEX:使用正則表達式
  • FilterType.ASSIGNABLE_TYPE:按照給定的類型
@ComponentScan(value = "com.xiaojian",includeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class}),
        @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,classes = {BookService.class})
},useDefaultFilters = false)

在MainConfig類文件的@ComponentScanincludeFilters屬性中添加一個FilterType.ASSIGNABLE_TYPE的Filter,此Filter通過BookService這個指定的類文件。

運行MainTest03測試類,測試結果:

1544775256737

  • FilterType.CUSTOM:使用自定義規則

使用自定義規則的時候,這個值必須是一個TypeFilter規則的實現類。

首先我們在xiaojian這個文件夾下創建一個impls文件夾,在此文件夾下創建一個MyTypeFilter類去實現TypeFilter這個接口,接口中有一個返回boolean類型值的方法。

package com.xiaojian.impls;

import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;

import java.io.IOException;

public class MyTypeFilter implements TypeFilter{

    /**
     *
     *
     * @param metadataReader     讀取到的當前正在掃描的類的星系
     * @param metadataReaderFactory      可以獲取到其他任何類的信息
     * @return
     * @throws IOException
     */
    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {

        //獲取當前類註釋的信息
        AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();

        //獲取當前正在掃描的類的類信息
        ClassMetadata classMetadata = metadataReader.getClassMetadata();

        //獲取當前類資源(類的路徑)
        Resource resource = metadataReader.getResource();

        //獲取類名
        String className = classMetadata.getClassName();
        //獲取父類名
        String superClassName = classMetadata.getSuperClassName();
        //獲取子類名
        String[] memberClassNames = classMetadata.getMemberClassNames();
        //獲取接口名
        String[] interfaceNames = classMetadata.getInterfaceNames();

        //上面那些方法我們有時間可以自己去看看。
        System.out.println("---->"+className);

        //判斷邏輯,爲true,則放行,爲false則攔截。
        if (className.contains("vice")){
            return true;
        }
        return false;
    }
}

MainConfig類文件也做出相應的改變

package com.xiaojian.config;

import com.xiaojian.bean.User;
import com.xiaojian.impls.MyTypeFilter;
import com.xiaojian.service.BookService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;

//配置類==配置文件
@Configuration  //告訴spring這是一個配置類
@ComponentScan(value = "com.xiaojian",includeFilters = {
        @ComponentScan.Filter(type = FilterType.CUSTOM,classes = {MyTypeFilter.class})
},useDefaultFilters = false)
//FilterType.CUSTOM是使用自定義規則,其實現的是MyTypeFilter這個類中放行的內容。

//包掃描路徑,與在xml文件中配置 <context:component-scan base-package="com.xiaojian"></context:component-scan>  作用一樣。
public class MainConfig {

    //給容器註冊一個bean;類型爲返回值的類型;id默認是用方法名作爲id(不過我們也可以自己指定id,就是value值)
    @Bean(value = "user01")
    public User user01(){
        return new User("小賤",13);
    }
}

運行MainTest03測試類,測試結果:

1544777355713

如圖,只有一個bookService被放行,註冊到了IOC的容器中,因爲判斷邏輯只給包含"vice"的放行。而其他的兩個,是不在這個過濾器管轄範圍內的。

4.@Scope指定作用範圍

(1)默認情況(單實例)

  • 創建MainConfig2

我們重新創建一個MainConfig類,類名爲MainConfig2,同時標註註解@Configuration

MainConfig2:

package com.xiaojian.config;

import com.xiaojian.bean.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MainConfig2 {

   
    //默認的都是單實例的
    @Bean
    public User user(){
        return new User("小賤",333);
    }
}
  • 創建測試類

在test文件夾下面創建一個MainTest04這個測試類。

MainTest04:

package com.xiaojian.test;

import com.xiaojian.bean.User;
import com.xiaojian.config.MainConfig2;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MainTest04 {

    public static void main(String[] args) {


        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);

        //此方法可以獲取所有的bean定義的名字
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        for (String name:beanDefinitionNames){
            System.out.println(name);
        }

        User user = (User) applicationContext.getBean("user");
        User user1 = (User) applicationContext.getBean("user");
        //比較兩者是否爲同一個對象,觀察所有的bean的創建的默認情況下是單例還是多例。
        System.out.println(user==user1);
    }
}

運行MainTest04測試類,測試結果:

1544779115195

說明所有的bean默認情況下都是單例模式,就是所有創建的bean都是同一個。

(2)多實例案例

  • 修改MainConfig2.java類文件,使得User這個bean爲多實例bean。

MainConfig2:

package com.xiaojian.config;

import com.xiaojian.bean.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;

@Configuration
public class MainConfig2 {


    /**
     * @Scope的作用範圍:
     *   singleton:單實例(系統默認情況下都是單實例的)
     *   prototype:多實例
     *   request:同一次請求創建一個實例
     *   session:同一個session創建一個實例
     *
     * 注意:
     * - 系統默認的是單實例,IOC容器啓動會調用方法創建對象放到IOC容器中,以便以後每次獲取,就可以直接從容器中獲取
     * - 多實例:IOC容器獲取的時候纔會創建,且獲取幾次容器會調用方法創建對象幾次。IOC容器啓動的時候並不會調用該方法創建對象到IOC容器中。
     */
    //默認的都是單實例的
    @Scope("prototype")
    @Bean
    public User user(){
        return new User("小賤",333);
    }
}

  • 測試結果

運行MainTest04測試類,測試結果:

1544840397310

所以當bean被定義爲多實例的時候,每次調用都會創建一個bean對象,並且這些bean對象不是同一個。

(3)單實例與多實例的創建時間對比

  • 單實例情況下:

修改MainConfig2.java文件:

MainConfig2:

package com.xiaojian.config;

import com.xiaojian.bean.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;

@Configuration
public class MainConfig2 {


    /**
     * @Scope的作用範圍:
     *   singleton:單實例(系統默認情況下都是單實例的)
     *   prototype:多實例
     *   request:同一次請求創建一個實例
     *   session:同一個session創建一個實例
     *
     * 注意:
     * - 系統默認的是單實例,IOC容器啓動會調用方法創建對象放到IOC容器中,以便以後每次獲取,就可以直接從容器中獲取
     * - 多實例:IOC容器獲取的時候纔會創建,且獲取幾次容器會調用方法創建對象幾次。IOC容器啓動的時候並不會調用該方法創建對象到IOC容器中。
     */
    //默認的都是單實例的
//    @Scope("prototype")
    @Bean
    public User user(){
        System.out.println("給容器中添加User。。。");
        return new User("小賤",333);
    }
}

將User這個bean還原爲單實例對象。

修改測試類MainTest04.java:

MainTest04:

package com.xiaojian.test;

import com.xiaojian.bean.User;
import com.xiaojian.config.MainConfig2;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MainTest04 {

    public static void main(String[] args) {
        
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
        System.out.println("IOC容器啓動完畢");
    }
}

此時系統只是簡單的運行了,創建了IOC容器,但是容器中並沒有做任何的操作。

運行MainTest04測試類,測試結果:

1544840957584

如圖,在系統啓動IOC容器的時候,系統已經給我們創建了user這個bean,也就是說,系統默認的是單實例,IOC容器啓動會調用方法創建對象放到IOC容器中,以便以後每次獲取,就可以直接從容器中獲取。

  • 多實例情況下:

修改MainConfig2.java文件:

MainConfig2:

package com.xiaojian.config;

import com.xiaojian.bean.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;

@Configuration
public class MainConfig2 {


    /**
     * @Scope的作用範圍:
     *   singleton:單實例(系統默認情況下都是單實例的)
     *   prototype:多實例
     *   request:同一次請求創建一個實例
     *   session:同一個session創建一個實例
     *
     * 注意:
     * - 系統默認的是單實例,IOC容器啓動會調用方法創建對象放到IOC容器中,以便以後每次獲取,就可以直接從容器中獲取
     * - 多實例:IOC容器獲取的時候纔會創建,且獲取幾次容器會調用方法創建對象幾次。IOC容器啓動的時候並不會調用該方法創建對象到IOC容器中。
     */
    //默認的都是單實例的
    @Scope("prototype")
    @Bean
    public User user(){
        System.out.println("給容器中添加User。。。");
        return new User("小賤",333);
    }
}

將User這個bean設置爲多實例對象。

修改測試類MainTest04.java:

MainTest04:

package com.xiaojian.test;

import com.xiaojian.bean.User;
import com.xiaojian.config.MainConfig2;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MainTest04 {

    public static void main(String[] args) {


        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);

        System.out.println("IOC容器啓動完畢");


        User user = (User) applicationContext.getBean("user");
        User user1 = (User) applicationContext.getBean("user");
        System.out.println(user==user1);
    }
}

運行MainTest04測試類,測試結果:

1544841318961

如圖,在系統啓動IOC容器的時候,系統並沒有給我們創建User這個bean,而是我們去調用這個bean的時候,系統會給我創建這個bean。也就是說,多實例的情況下,IOC容器獲取的時候纔會創建,且獲取幾次容器會調用方法創建對象幾次。IOC容器啓動的時候並不會調用方法創建對象到IOC容器中。

(4)@Scope指定作用範圍

@Scope的作用範圍:

  • singleton:單實例(系統默認情況下都是單實例的)
  • prototype:多實例
  • request:同一次請求創建一個實例
  • session:同一個session創建一個實例

上面的四個作用範圍,我們實際上用的比較多的是singleton以及prototype這兩種。

注意:

  • 系統默認的是單實例,IOC容器啓動會調用方法創建對象放到IOC容器中,以便以後每次獲取,就可以直接從容器中獲取
  • 多實例:IOC容器獲取的時候纔會創建,且獲取幾次容器會調用方法創建對象幾次。IOC容器啓動的時候並不會調用方法創建對象到IOC容器中。

5.@Lazy懶加載

(1)懶加載含義

單實例bean的情況下,系統會在IOC容器啓動的時候創建單實例的bean,而懶加載的含義是讓容器在啓動的時候,不創建對象,而是等到第一次調用的時候在創建。

(2)案例

  • 修改MainConfig2.java文件:

MainConfig2:

package com.xiaojian.config;

import com.xiaojian.bean.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;

@Configuration
public class MainConfig2 {


    /**
     * @Scope的作用範圍:
     *   singleton:單實例(系統默認情況下都是單實例的)
     *   prototype:多實例
     *   request:同一次請求創建一個實例
     *   session:同一個session創建一個實例
     *
     * 注意:
     * - 系統默認的是單實例,IOC容器啓動會調用方法創建對象放到IOC容器中,以便以後每次獲取,就可以直接從容器中獲取
     * - 多實例:IOC容器獲取的時候纔會創建,且獲取幾次容器會調用方法創建對象幾次。IOC容器啓動的時候並不會調用該方法創建對象到IOC容器中。
     */
    //默認的都是單實例的
//    @Scope("prototype")
    @Lazy
    @Bean
    public User user(){
        System.out.println("給容器中添加User。。。");
        return new User("小賤",333);
    }
}

我們在User這個bean上面添加@Lazy這個註解,標明這個User的bean是懶加載,告訴容器其在本身啓動的時候不先創建此單實例的bean,等到調用的時候再創建。

  • 修改測試類

修改MainTest04.java測試類:

MainTest04:

package com.xiaojian.test;

import com.xiaojian.bean.User;
import com.xiaojian.config.MainConfig2;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MainTest04 {

    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);

        System.out.println("IOC容器啓動完畢");

        User user = (User) applicationContext.getBean("user");
 
    }
}

我們在系統啓動IOC容器完成後再調用這個User的bean,看看這個單實例的bean在什麼時候被創建的。

  • 測試運行

運行MainTest04測試類,測試結果:

1544842015881

如圖,加上@Lazy註解的User這個bean,在容器IOC啓動的時候並沒有被創建,而是在IOC調用這個bean的時候才被IOC容器創建。且以後再調用這個bean的時候,不會再創建這個bean。

6.@Conditional條件註解

@Conditional:按照一定的條件進行判斷,滿足條件給容器中註冊bean

(1)創建類

  • 創建主題類

我們在config文件夾下面創建MainConfig3.java類文件,以作新的案例。

MainConfig3:

package com.xiaojian.config;

import com.xiaojian.bean.User;
import com.xiaojian.impls.LinuxCondition;
import com.xiaojian.impls.WindowsCondition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MainConfig3 {

    /**
     * @Conditional:按照一定的條件進行判斷,滿足條件給容器中註冊bean
     *
     * @Conditional註解:
     *    可以標註在類上,也可以標註在方法上
     *
     * 要求:
     *    如果系統是windows,給容器中註冊xiaojian
     *    如果系統是linux,給容器中註冊dashu
     */

    @Conditional({WindowsCondition.class})
    @Bean("xiaojian")
    public User user01(){
        return new User("xiaojian",22);
    }

    @Conditional({LinuxCondition.class})
    @Bean("dashu")
    public User user02(){
        return new User("dashu",23);
    }
}

  • 創建WindowsCondition.java

我們在impls這個文件包下面創建WindowsCondition.java這個類文件。

WindowsCondition:

package com.xiaojian.impls;

import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;

//判斷系統是否爲windows
public class WindowsCondition implements Condition{

    /**
     *
     * @param conditionContext          判斷條件能使用的上下文(環境)
     * @param annotatedTypeMetadata     註釋信息
     * @return
     */
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {




        //1.能獲取到IOC使用的beanfactory
        ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();

        //2.獲取類加載器
        ClassLoader classLoader = conditionContext.getClassLoader();

        //3.獲取當前環境信息
        Environment environment = conditionContext.getEnvironment();

        //4.獲取到bean定義的註冊類
        BeanDefinitionRegistry registry = conditionContext.getRegistry();
        //我們可以通過registry這個對象,判斷容器中對的bean的註冊情況,同時也可以更新一些信息

        
        //判斷操作系統
        String property = environment.getProperty("os.name");
        boolean windows = property.contains("Windows");

        if (windows){
            System.out.println("此電腦是windows系統");
            return true;
        }
        return false;
    }
}

  • 創建LinuxCondition.java

我們在impls這個文件包下面創建LinuxCondition.java這個類文件。

LinuxCondition:

package com.xiaojian.impls;

import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;

//判斷系統是否爲linux
public class LinuxCondition implements Condition{

    /**
     *
     * @param conditionContext    判斷條件能使用的上下文(環境)
     * @param annotatedTypeMetadata    註釋信息
     * @return
     */
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {



        //1.能獲取到IOC使用的beanfactory
        ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();

        //2.獲取類加載器
        ClassLoader classLoader = conditionContext.getClassLoader();

        //3.獲取當前環境信息
        Environment environment = conditionContext.getEnvironment();

        //4.獲取到bean定義的註冊類
        BeanDefinitionRegistry registry = conditionContext.getRegistry();
        //我們可以通過registry這個對象,判斷容器中對的bean的註冊情況,同時也可以更新一些信息


        //判斷操作系統
        String property = environment.getProperty("os.name");
        boolean linux = property.contains("Linux");
        if (linux){
            System.out.println("此電腦是linux系統");
            return true;
        }
        return false;
    }
}

  • 測試類

我們在test的文件夾下創建MainTest05。

MainTest05:

package com.xiaojian.test;

import com.xiaojian.bean.User;
import com.xiaojian.config.MainConfig3;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.env.Environment;

import java.util.Map;

public class MainTest05 {

    public static void main(String[] args) {

        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig3.class);
        System.out.println("IOC容器啓動完成");

        String[] beanNamesForType = applicationContext.getBeanNamesForType(User.class);
        for (String name:beanNamesForType){
            System.out.println(name);
        }


        //獲取User類型的所有的bean
        Map<String, User> beansOfType = applicationContext.getBeansOfType(User.class);
        System.out.println(beansOfType);
    }
}

(2)測試結果

運行測試類MainTest05.java類文件中的主函數。

測試結果:

1544854351948

如圖所示,在IOC容器啓動過程中,系統會默認的去創建單實例的bean,而我們兩個bean上面都標註了條件註解,所以在創建bean的時候,它們都會先去啓動條件判斷執行的類,所以第一條是“這是windows系統”,之後纔是“IOC容器啓動完成”,而後我們可以看出系統打印出現在系統中已經存在的bean的名稱,因爲此係統是windows系統,所以只有"xiaojian"這個bean被註冊。

二. @Bean[導入的第三方包裏面的組件]

此部分省略。

三. @Import[快速給容器中導入一個組件]

1.導入單個組件

(1)創建Color.java類文件

我們在bean目錄下創建一個Color.java類文件。

Color:

package com.xiaojian.bean;

public class Color {
}

此類文件將作爲本次的bean對象。

(2)創建MainConfig4.java類文件

我們創建MainConfig4.java類文件,此文件作爲此案例的配置類。

MainConfig4:

package com.xiaojian.config;

import com.xiaojian.bean.Color;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
@Import(Color.class)   //@Import導入組件,id默認是組件的全類名
public class MainConfig4 {
}

此處我們使用@Import註解來給IOC容器註冊bean,組件的id默認是組件的全類名。

(3)創建測試類

我們在test包下面創建一個MainTest06.java測試類。
MainTest06:

package com.xiaojian.test;

import com.xiaojian.config.MainConfig4;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MainTest06 {

    public static void main(String[] args) {

        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig4.class);

        //獲取系統中的所有定義的bean名稱
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        for (String name:beanDefinitionNames){
            System.out.println(name);
        }

    }
}

(4)測試運行

運行MainTest06.java測試類,測試結果如下:

1544856125324

如圖,系統中已經註冊了Color這個bean,只不過其id是Color.java這個類文件的全路徑。

2.導入多個組件

(1)創建Animal.java類文件

我們在bean目錄下創建一個Animal.java類文件,內部爲空,只是簡單的作爲一個bean創建對象。

package com.xiaojian.bean;

public class Animal {
}

(2)修改MainConfig4.java類文件

導入多個組件,其實就是修改@Import的導入的值,而這個註解本身是可以導入一個數組的,所以我們將上面的那個之後改成一個數組就行。

MainConfig4:

package com.xiaojian.config;

import com.xiaojian.bean.Animal;
import com.xiaojian.bean.Color;
import com.xiaojian.bean.User;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
@Import({Color.class, Animal.class, User.class})   //@Import導入組件,id默認是組件的全類名
public class MainConfig4 {
}

(3)測試運行

運行MainTest06.java測試類,測試結果如下:

1544857191082

如圖,配置類中導入了三個組件,此處顯示三個組件的全類名。

3.ImportSelector接口實現(第二種導入方法)

此方法是導入一個類,該類去實現ImportSelector接口,這個接口內部有一個selectImports方法需要方法重載,其會返回一個帶有系統需要導入的組件的全類名的數組,告訴IOC容器去創建哪些bean對象。

(1)修改MainConfig4.java類文件

MainConfig4:

package com.xiaojian.config;

import com.xiaojian.bean.Animal;
import com.xiaojian.bean.Color;
import com.xiaojian.bean.User;
import com.xiaojian.impls.MyImportSelector;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
//@Import({Color.class, Animal.class, User.class})   //@Import導入組件,id默認是組件的全類名
@Import({MyImportSelector.class})
//導入一個自定義的組件選擇控制類,其實現了ImportSelector這個接口,接口返回一個需要導入的組件的全類名的數組
public class MainConfig4 {
}

(2)創建MyImportSelector.java類文件

我們在impls這個包下面創建一個MyImportSelector.java類文件,去實現ImportSelector這個接口。

MyImportSelector:

package com.xiaojian.impls;

import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;

//自定義邏輯,返回需要導入的組件。
public class MyImportSelector implements ImportSelector {

    //返回值就是要導入到容器中的組件的全類名
    //AnnotationMetadata:當前標註@Import註解的類的所有註解信息
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {

        //方法不能返回一個null,否則會出現空指針異常。
        return new String[]{"com.xiaojian.bean.Color","com.xiaojian.bean.User"};
    }
}

此處的selectImports方法返回一個數組,其內部包含Color、User這兩個實例對象,目的就是告訴IOC容器在啓動的時候去創建這兩個bean對象。

(3)測試運行

運行MainTest06.java測試類,測試結果如下:

1544858445957

如圖,控制檯打印出了Color以及User這兩個實體類的bean對象。

4.ImportBeanDefinitionRegistrar接口實現(第三種導入方法)

此方法是導入一個類,該類去實現ImportBeanDefinitionRegistrar接口,這個接口內部有一個registerBeanDefinitions方法需要方法重載,我們可以在這個方法的內部註冊我們所需要的bean,同時我們也可以在這個方法中做一些其他的事情。

(1)創建MyImportBeanDefinitionRegistrar.java類文件

我們在impls這個包下面創建一個MyImportBeanDefinitionRegistrar.java類文件,去實現registerBeanDefinitions這個接口。

MyImportBeanDefinitionRegistrar:

package com.xiaojian.impls;

import com.xiaojian.bean.Animal;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;

public class MyImportBeanDefinitonRegistrar implements ImportBeanDefinitionRegistrar {

    /**
     *
     * @param annotationMetadata   當前類的註解信息以及其他的信息
     * @param beanDefinitionRegistry   BeanDefinition註冊類
     *
     * 把所有的需要添加到容器中的bean:調用
     *   BeanDefinitionRegistry.registerBeanDefinition手動註冊進來
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {

        //判斷內部是否有User這個bean的信息
        boolean user = beanDefinitionRegistry.containsBeanDefinition("User");

        //判斷內部是否有Color這個bean的信息
        boolean color = beanDefinitionRegistry.containsBeanDefinition("Color");


        if (!user && !color){


            //registerBeanDefinition這個方法用於註冊bean。
            //    第一個參數是這個bean的名稱
            //    第二個參數是一個BeanDefinition,我們用RootBeanDefinition這個實現

            //指定bean定義信息:(bean的類型,bean的作用域。。。)
            RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Animal.class);
            //註冊一個bean,指定bean的名稱
            beanDefinitionRegistry.registerBeanDefinition("Animal", rootBeanDefinition);
        }
    }
}

(2)修改MainConfig4.java類文件

MainConfig4:

package com.xiaojian.config;

import com.xiaojian.bean.Animal;
import com.xiaojian.bean.Color;
import com.xiaojian.bean.User;
import com.xiaojian.impls.MyImportBeanDefinitonRegistrar;
import com.xiaojian.impls.MyImportSelector;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
//@Import({Color.class, Animal.class, User.class})   //@Import導入組件,id默認是組件的全類名
//@Import({MyImportSelector.class})
//導入一個自定義的組件選擇控制類,其實現了ImportSelector這個接口,接口返回一個需要導入的組件的全類名的數組

@Import(MyImportBeanDefinitonRegistrar.class)
//導入一個實現ImportBeanDefinitionRegistrar接口的類,該接口把所有需要註冊的bean通過內部的方法手動註冊進來。
public class MainConfig4 {
}

(3)測試運行

運行MainTest06.java測試類,測試結果如下:

1544860814962

如圖,控制檯只打印了一個Animal這個bean,因爲我們在方法中只註冊了一個這個bean。

四. 使用Spring提供的FactoryBean工廠(bean)

1.創建ColorFactoryBean.java類文件

我們在impls這個包下面創建一個ColorFactoryBean.java類文件,此類實現FactoryBean這個接口。

ColorFactoryBean:

package com.xiaojian.impls;

import com.xiaojian.bean.Color;
import org.springframework.beans.factory.FactoryBean;

//創建一個Spring定義的FactoryBean
public class ColorFactoryBean implements FactoryBean {

    //返回一個Color對象,這個對象會添加到容器中
    @Override
    public Object getObject() throws Exception {

        //此處返回一個Color對象,意思就是告訴IOC容器,我們註冊一個Color這個bean。
        return new Color();
    }

    @Override
    public Class<?> getObjectType() {

        //此處是返回上面getObject()創建的bean對象的類型
        return Color.class;
    }


    @Override
    public boolean isSingleton() {

        //此處是返回上面getObject()方法創建的bean對象是否爲單例模式
        //true則爲單實例,容器中只保存一份;false,多實例,每次獲取都會返回一份。
        return false;
    }
}

此類實現FactoryBean,我們需要重寫三個方法:

  • getObject():此方法返回的是一個bean對象,就是將會在IOC容器中創建的對象
  • getObjectType():此方法返回的是getObject()方法返回的對象的類型
  • isSingleton():此方法返回的是getObject()方法返回的bean對象作用域範圍,就是是單例模式還是多例模式

2.創建MainConfig5.java類文件

我們在config包下面創建MainConfig5.java類文件,在此類文件中創建ColorFactoryBean這個bean對象的聲明。

MainConfig5:

package com.xiaojian.config;

import com.xiaojian.impls.ColorFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MainConfig5 {

    //此處聲明瞭ColorFactoryBean這個bean的對象,告訴IOC容器
    @Bean
    public ColorFactoryBean colorFactoryBean(){
        return new ColorFactoryBean();
    }
}

3.創建測試類

在test包下面創建一個MainTest07.java類文件,進行相干的測試。

MainTest07:

package com.xiaojian.test;

import com.xiaojian.config.MainConfig5;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MainTest07 {
    public static void main(String[] args) {

        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig5.class);

        //此處查看我們所有的bean對象的名稱
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        for (String name:beanDefinitionNames){
            System.out.println("此方法爲:"+name);
        }

        //工廠Bean獲取的時調用getObject方法創建的對象
        
        //此處是爲了查看我們獲取的bean實際上的名稱
        Object colorFactoryBean = applicationContext.getBean("colorFactoryBean");
        System.out.println("bean的名稱"+colorFactoryBean.toString());
        System.out.println("bean的類型:"+colorFactoryBean.getClass());

    }
}

4.運行測試

運行MainTest07.java測試類,測試結果如下:

1544863586414

如圖,爲測試結果。圖中的1,是IOC容器創建的bean對象,而2是colorFactoryBean這個對象的實際的指向。也就是說,我們在MainConfig5.java類文件中創建的那個bean,我們在IOC容器中獲取的卻是Color,因爲我們在ColorFactoryBean的方法中返回了Color這個bean。

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