Spring拾遺(三)——ImportSelector和ImportBeanDefinitionRegistrar

在學習源碼的過程中,讀到@Import註解的時候,發現其底層有三種解析方式。進入到@Import註解中看到spring的說明如下

/**
 * Indicates one or more <em>component classes</em> to import &mdash; typically
 * {@link Configuration @Configuration} classes.
 *
 * 這裏說明了,@Import註解中可以指定ImportSelector和ImportBeanDefinitionRegistrar
 * <p>Provides functionality equivalent to the {@code <import/>} element in Spring XML.
 * Allows for importing {@code @Configuration} classes, {@link ImportSelector} and
 * {@link ImportBeanDefinitionRegistrar} implementations, as well as regular component
 * classes (as of 4.2; analogous to {@link AnnotationConfigApplicationContext#register}).
 *
 * <p>{@code @Bean} definitions declared in imported {@code @Configuration} classes should be
 * accessed by using {@link org.springframework.beans.factory.annotation.Autowired @Autowired}
 * injection. Either the bean itself can be autowired, or the configuration class instance
 * declaring the bean can be autowired. The latter approach allows for explicit, IDE-friendly
 * navigation between {@code @Configuration} class methods.
 *
 * <p>May be declared at the class level or as a meta-annotation.
 *
 * <p>If XML or other non-{@code @Configuration} bean definition resources need to be
 * imported, use the {@link ImportResource @ImportResource} annotation instead.
 *
 * @author Chris Beams
 * @author Juergen Hoeller
 * @since 3.0
 * @see Configuration
 * @see ImportSelector
 * @see ImportResource
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {

	/**
	 * {@link Configuration @Configuration}, {@link ImportSelector},
	 * {@link ImportBeanDefinitionRegistrar}, or regular component classes to import.
	 */
	Class<?>[] value();

}

從spring的註釋中可以看到這個註解支持ImportSelector和ImportBeanDefinitionRegistrar,但是這兩個具體有什麼區別,就是我們這篇博客需要討論的。

ImportSelector

這裏直接上實例,我們自己實現一個Selector,實現這個接口中的方法

/**
 * autor:liman
 * createtime:2020/3/21
 * comment:
 */
public class TestImportSelector implements ImportSelector {

    /**
     * 這個方法,單純的返回需要註冊的bean的全路徑名,spring會自動注入進工廠,
     * @param annotationMetadata 這個參數是可獲取bean上的註解內容
     * @return
     */
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        return new String[]{TestBean.class.getName()};
    }
}

不太重要的TestBean

/**
 * autor:liman
 * createtime:2020/3/21
 * comment:
 */
@Component
public class TestBean {

    public void testImportSelectorBean(){
        System.out.println("this is test bean for import selector");
    }

}

關於Import中的具體實現,這裏在spring源碼中可以看到相關實現,這裏不再貼出。

ImportBeanDefinitionRegistrar

依舊直接上實例,自己實現的ImportBeanDefinitionRegistrar,實現了該接口中的方法

/**
 * autor:liman
 * createtime:2020/3/15
 * comment: 自己實現的ImportBeanDefinitionRegistrar
 */
public class SelfBeanDefinitionRegistry implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        Class testBeanClazz = TestBean.class;
        //自己構建BeanDefinition,將其交給spring託管,這個構建BeanDefinition的方法在spring源碼中很常見。
        RootBeanDefinition testBeanDefinition = new RootBeanDefinition(testBeanClazz);
        String beanName = StringUtils.uncapitalize(testBeanClazz.getName());
        registry.registerBeanDefinition(beanName,testBeanDefinition);
    }
}

其餘和ImportSelector一樣,具體的Config類如下:

/**
 * autor:liman
 * createtime:2020/2/25
 * comment:
 */
@Configuration
@ComponentScan(value="com.learn.importselector")
//@Import(TestImportSelector.class) 如果需要測試ImportSelector,可以用這個
@Import(SelfBeanDefinitionRegistry.class)
public class AppConfig {
}

一個簡單的小結

從功能上看,兩者似乎差不多,最大的區別似乎就是ImportSelector是將類的全路徑名直接註冊給Spring的IOC,而ImportBeanDefinitionRegistrar就是通過BeanDefinition註冊給spring。這裏拋出一個問題,我們在使用MyBatis框架的時候,似乎並沒有實現類,但是Spring卻能完美的給我們註冊相關的實現類,這是如何做到的?其實走到這裏,應該能猜到,其實MyBatis的底層就是通過實現了ImportBeanDefinitionRegistrar來實現的,在構建真正需要註冊進入到IOC的實現類之前,MyBatis動態實現了我們指定的數據訪問層的接口,然後將其構建BeanDefinition並注入到了IOC中,這一點我們在閱讀MyBatis源碼的時候,再詳細探討。

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