Spring註解 - 組件的註冊

Spring Boot的出現極大的簡化了咱們的開發,讓咱們無需再寫繁雜的配置文件,其正是利用了註解的便捷性,而Spring Boot又依賴於Spring,所以深刻學習Spring的註解是十分必要的。java

組件註冊相關注解

@Configuration

寫在類上,聲明此類是一個配置類,替代xml文件正則表達式

@Bean

做用:spring

​ 給 IOC 容器中註冊一個Bean,通常添加在方法上,組件類型爲方法的返回值,id默認爲方法名稱數組

經常使用屬性:session

  • value / name:指定組件的名稱,若是不指定,默認是方法名
  • initMethod:指定初始化方法
  • destroyMethod:指定銷燬方法

@ComponentScan

做用:app

根據自定義的規則,自動掃描 IOC 容器中全部組件,在 jdk1.8 以後能夠在一個類上定義多個@ComponentScanide

還有一個@ComponentScans註解,也能夠在裏面定義多個@ComponentScan學習

經常使用屬性:測試

  • value / basePackages:指定要掃描的包名
  • @Filter:用於指定過濾的規則
    • type:過濾類型
      • FilterType.ANNOTATION:按照註解的方式
      • FilterType.ASSIGNABLE_TYPE:按照給定的類型
      • FilterType.ASPECTJ:使用ASPECTJ表達式
      • FilterType.REGEX:使用正則表達式
      • FilterType.CUSTOM:自定義類型
    • value / classes:過濾值
    • pattern:過濾規則,根據不一樣的過濾類型配置不一樣的規則
  • useDefaultFilters:是否使用默認過濾規則, 默認是 true
  • includeFilters:指定掃描的時候只包含什麼組件,須要配置 useDefaultFilters 屬性爲false
  • excludeFilters:指定掃描的時候按照什麼規則排除哪些組件
  • lazyInit:懶加載

如何使用FilterType.CUSTOM自定義過濾規則?prototype

public class MyTypeFilter implements TypeFilter {

    /**
     * 匹配方法,肯定此過濾器是否與給定元數據描述的類匹配
     * @param metadataReader 讀取到的當前正在掃描的類的信息
     * @param metadataReaderFactory 能夠獲取到其餘任何類的信息
     * @return true:匹配, false:不匹配
     * @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();
        if(className.contains("Controller")){
            return true;
        }
        return false;
    }
}

@Scope

做用:

​ 調整做用域

經常使用參數:

  • value / scopeName
    • singleton:單實例,ioc容器啓動的時候就會調用方法,建立bean對象,之後每次獲取都是直接從ioc容器中取(map.get())。
    • prototype:多實例,ioc容器啓動的時候不會去調用,當從ioc容器中獲取bean對象的時候纔會建立。
    • request:同一次請求建立一個實例。
    • session:同一個session範圍建立一個實例。
    • global session:全局session範圍建立一個實例,通常用於集羣。

@Lazy

懶加載,通常用於單例模式,容器啓動的時候不會建立bean,第一次調用的時候才建立

@Conditional

做用:

​ 按照必定條件進行判斷,知足條件才註冊bean,能夠放在方法或類上,此註解在Spring Boot底層大量使用

經常使用參數:

  • value:傳入一個繼承了Condition接口的類(可傳入多個),類中定義本身須要的條件

@Import

做用:

​ 給ioc容器中導入指定的組件

經常使用參數:

  • value:傳入指定類,id默認是全類名。可傳入自定義類、ImportSelector 和 ImportBeanDefinitionRegistrar

用法:

​ 在配置類上添加以下形式的註解便可

@Import({Red.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class})

ImportSelector

傳入實現了 ImportSelector 接口的類,返回一個全類名數組,好處就是能夠自定義須要導入的組件

public class MyImportSelector implements ImportSelector {

    /**
     * 自定義導入的組件
     * @param importingClassMetadata 當前標註了@Import註解的類的全部註解信息
     * @return 返回全類名
     */
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"com.spring.color.Blue", "com.spring.color.Yellow"};
    }
}

打斷點debug一下,能夠看到參數的信息,的確是當前標註@Import的類上的註解信息

ImportBeanDefinitionRegistrar

手動註冊bean到容器中,調用 registerBeanDefinition() 方法

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    /**
     *
     * @param importingClassMetadata 當前標註了@Import註解的類的全部註解信息
     * @param registry 容器中已註冊組件的信息
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        int count = registry.getBeanDefinitionCount();
        if(count > 10){
            BeanDefinition beanDefinition = new RootBeanDefinition(Ten.class);
            registry.registerBeanDefinition("ten", beanDefinition);
        }
    }
}

使用 FactoryBean 註冊組件

自定義一個類,實現 FactoryBean 接口

public class FoodFactoryBean implements FactoryBean<Food> {

    /**
     * 獲取實例對象
     * @return
     * @throws Exception
     */
    @Override
    public Food getObject() throws Exception {
        return new Food();
    }

    /**
     * 獲取實例類型
     * @return
     */
    @Override
    public Class<?> getObjectType() {
        return Food.class;
    }

    /**
     * 是否單例,true:單例  false:多例
     * @return
     */
    @Override
    public boolean isSingleton() {
        return true;
    }
}

使用@Bean註冊到容器

@Bean
public FoodFactoryBean foodFactoryBean(){
    return new FoodFactoryBean();
}

測試一下

@Test
public void test2(){
    Object bean = applicationContext.getBean("foodFactoryBean");
    System.out.println("foodFactoryBean 的類型:" + bean.getClass());
}

運行結果以下,發現類型居然不是 FoodFactoryBean ,使用@Bean註冊的組件類型不是方法的返回值嗎?實際上,FoodFactoryBean註冊的時候調用的了 getObject() 方法,因此註冊的是 Food 類

foodFactoryBean 的類型:class com.spring.bean.Food

那麼若是想要得到 FoodFactoryBean 類怎麼辦呢?

看一下 BeanFactory 的源碼,定義了一個成員變量 FACTORY_BEAN_PREFIX

這個變量用於取消引用 FactoryBean 實例,並將其與由 FactoryBean 建立的bean區別開。

例如,若是名爲 test 的 bean 是 FactoryBean,則獲取 &test 將返回工廠,而不是工廠返回的實例。

因此在getBean的時候,在 id 前加上 & 便可

@Test
public void test2(){
    Object bean = applicationContext.getBean("&foodFactoryBean");
    System.out.println("foodFactoryBean 的類型:" + bean.getClass());
}

總結

註冊組件的方式:

  1. 包掃描 (@ComponentScan) + 組件註解(@Component / @Controller / @Service / @Repository)
  2. @Bean [導入第三方包裏的組件]
  3. @Import [快速給容器中導入組件]
    • 普通類:直接註冊,id默認是全類名
    • ImportSelector:返回須要註冊組件的全類名數組
    • ImportBeanDefinitionRegistrar: 手動註冊組件到容器中
  4. 使用 spring 提供的 FactoryBean(工廠Bean)
    • 默認獲取的是 FactoryBean 調用getObject方法返回的對象
    • 能夠在獲取Bean的時候在 id 前面加上 & 符號,獲取 FactoryBean 自己
相關文章
相關標籤/搜索