@Import注入之ImportSelector和ImportBeanDefinitionRegistrar

在spring-boot中隨處可以見的就是@Import, 它的作用就是通過導入的方式把實例注入到IOC中,本文主要介紹下@Import的用法。

知識點回顧:

給IOC容器中註冊Bean的方式:

  1. 包掃描+組件標註註解(@Controller/@Service/@Repository/@Component)
  2. @Bean
  3. @Import

@Import源碼可以發現@Import註解只能註解在類

/**
 * Indicates one or more {@link Configuration @Configuration} classes to import.
 *
 * <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 {
    Class<?>[] value();
}

從上面的英文註釋可得知,參數value上可以配置3種類型的值:

普通的class

普通的class,容器中就會自動註冊這個組件,id默認是全類名

public class Blue {}
public class Red {}
@Configuration
@Import({ Blue.class, Red.class })
public class ColorConfig {
}

ImportSelector

它需要返回需要導入的組件的全類名數組,它可以實現一些Aware接口:

  • EnvironmentAware
  • BeanFactoryAware
  • BeanClassLoaderAware
  • ResourceLoaderAware
/**
 * Interface to be implemented by types that determine which @{@link Configuration}
 * class(es) should be imported based on a given selection criteria, usually one or more
 * annotation attributes.
 *
 * <p>An {@link ImportSelector} may implement any of the following
 * {@link org.springframework.beans.factory.Aware Aware} interfaces, and their respective
 * methods will be called prior to {@link #selectImports}:
 * <ul>
 * <li>{@link org.springframework.context.EnvironmentAware EnvironmentAware}</li>
 * <li>{@link org.springframework.beans.factory.BeanFactoryAware BeanFactoryAware}</li>
 * <li>{@link org.springframework.beans.factory.BeanClassLoaderAware BeanClassLoaderAware}</li>
 * <li>{@link org.springframework.context.ResourceLoaderAware ResourceLoaderAware}</li>
 * </ul>
 *
 * <p>ImportSelectors are usually processed in the same way as regular {@code @Import}
 * annotations, however, it is also possible to defer selection of imports until all
 * {@code @Configuration} classes have been processed (see {@link DeferredImportSelector}
 * for details).
 *
 * @author Chris Beams
 * @since 3.1
 * @see DeferredImportSelector
 * @see Import
 * @see ImportBeanDefinitionRegistrar
 * @see Configuration
 */
public interface ImportSelector {

	/**
	 * Select and return the names of which class(es) should be imported based on
	 * the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
	 */
	String[] selectImports(AnnotationMetadata importingClassMetadata);
}

下面演示下它的用法:

public class MyImportSelector implements ImportSelector {
	@Override
	public String[] selectImports(AnnotationMetadata importingClassMetadata) {
		// 方法不要返回null值,就是到導入到容器中的組件全類名
		//return new String[]{"com.github.bean.Blue"};
		return new String[] { Blue.class.getName(), Yellow.class.getName() };
	}
}
@Configuration
@Import({ MyImportSelector.class })
public class ColorConfig {
}

ImportBeanDefinitionRegistrar

它提供了手動註冊bean到容器中,它可以實現一些Aware接口:

  • EnvironmentAware
  • BeanFactoryAware
  • BeanClassLoaderAware
  • ResourceLoaderAware
/**
 * Interface to be implemented by types that register additional bean definitions when
 * processing @{@link Configuration} classes. Useful when operating at the bean definition
 * level (as opposed to {@code @Bean} method/instance level) is desired or necessary.
 *
 * <p>Along with {@code @Configuration} and {@link ImportSelector}, classes of this type
 * may be provided to the @{@link Import} annotation (or may also be returned from an
 * {@code ImportSelector}).
 *
 * <p>An {@link ImportBeanDefinitionRegistrar} may implement any of the following
 * {@link org.springframework.beans.factory.Aware Aware} interfaces, and their respective
 * methods will be called prior to {@link #registerBeanDefinitions}:
 * <ul>
 * <li>{@link org.springframework.context.EnvironmentAware EnvironmentAware}</li>
 * <li>{@link org.springframework.beans.factory.BeanFactoryAware BeanFactoryAware}
 * <li>{@link org.springframework.beans.factory.BeanClassLoaderAware BeanClassLoaderAware}
 * <li>{@link org.springframework.context.ResourceLoaderAware ResourceLoaderAware}
 * </ul>
 *
 * <p>See implementations and associated unit tests for usage examples.
 *
 * @author Chris Beams
 * @since 3.1
 * @see Import
 * @see ImportSelector
 * @see Configuration
 */
public interface ImportBeanDefinitionRegistrar {
	/**
	 * Register bean definitions as necessary based on the given annotation metadata of
	 * the importing {@code @Configuration} class.
	 * <p>Note that {@link BeanDefinitionRegistryPostProcessor} types may <em>not</em> be
	 * registered here, due to lifecycle constraints related to {@code @Configuration}
	 * class processing.
	 * @param importingClassMetadata annotation metadata of the importing class
	 * @param registry current bean definition registry
	 */
	public void registerBeanDefinitions(
			AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
}

下面演示下它的用法:

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
	/**
	 * AnnotationMetadata:當前類的註解信息
	 * BeanDefinitionRegistry:BeanDefinition註冊類;
	 */
	@Override
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { 
		boolean definition = registry.containsBeanDefinition(Red.class.getName());
		boolean definition2 = registry.containsBeanDefinition(Blue.class.getName());
		if (definition && definition2) {
			// 指定Bean定義信息
			RootBeanDefinition beanDefinition = new RootBeanDefinition(Yellow.class);
			// 註冊一個Bean,指定bean名
			registry.registerBeanDefinition(Yellow.class.getName(), beanDefinition);
		}
	}
}
@Configuration
@Import({ MyImportSelector.class, MyImportBeanDefinitionRegistrar.class })
public class ColorConfig {
}

這裏重點下:

AnnotationMetadata,它可以獲取當前類(被@Import標記)的註解信息,它使用標準的反射來獲取制定類的內部註解信息。

public interface AnnotationMetadata extends ClassMetadata, AnnotatedTypeMetadata {
    Set<String> getAnnotationTypes();

    Set<String> getMetaAnnotationTypes(String var1);

    boolean hasAnnotation(String var1);

    boolean hasMetaAnnotation(String var1);

    boolean hasAnnotatedMethods(String var1);

    Set<MethodMetadata> getAnnotatedMethods(String var1);
}

AnnotationConfigApplicationContext,@Import內部制定的類作爲regular component,而AnnotationConfigApplicationContext才能識別

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