- for (SourceClass candidate : importCandidates) {
- if (candidate.isAssignable(ImportSelector.class)) {
- Class> candidateClass = candidate.loadClass();
- ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class, this.environment, this.resourceLoader, this.registry);
- if (selectorFilter != null) {
- exclusionFilter = exclusionFilter.or(selectorFilter);
- }
-
- if (selector instanceof DeferredImportSelector) {
- this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
- } else {
- String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
- Collection
importSourceClasses = asSourceClasses(importClassNames, exclusionFilter); - processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
- }
- } else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
-
- Class> candidateClass = candidate.loadClass();
- ImportBeanDefinitionRegistrar registrar =
- ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
- this.environment, this.resourceLoader, this.registry);
- configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
- }
- else {
- this.importStack.registerImport(
- currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
- processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
- }
- }
- }
从以上代码我们可以看出在@Import注解中可以看出import可以引入四种类型的class
主要是通过调用processConfigurationClass方法处理
主要实现selectImports方法,返回字符串数组,数组内容须名class全名,包括package部分
- public interface ImportSelector {
- String[] selectImports(AnnotationMetadata importingClassMetadata);
- @Nullable
- default Predicate
getExclusionFilter() { - return null;
- }
- }
从下面的代码可以看出DeferredImportSelector接口是继承ImportSelector ,所以实现类需要实现getImportGroup方法,面且还需要实现其中内部接口类Group
另外如果getImportGroup返回null的值,需要实现其父接口ImportSelector 的selectImports方法,要不然会出现异常
- public interface DeferredImportSelector extends ImportSelector {
- @Nullable
- default Class extends Group> getImportGroup() {
- return null;
- }
- interface Group {
- void process(AnnotationMetadata metadata, DeferredImportSelector selector);
- Iterable
selectImports(); - class Entry {
- private final AnnotationMetadata metadata;
- private final String importClassName;
- public Entry(AnnotationMetadata metadata, String importClassName) {
- this.metadata = metadata;
- this.importClassName = importClassName;
- }
- public AnnotationMetadata getMetadata() {
- return this.metadata;
- }
-
-
- public String getImportClassName() {
- return this.importClassName;
- }
-
- @Override
- public boolean equals(@Nullable Object other) {
- if (this == other) {
- return true;
- }
- if (other == null || getClass() != other.getClass()) {
- return false;
- }
- Entry entry = (Entry) other;
- return (this.metadata.equals(entry.metadata) && this.importClassName.equals(entry.importClassName));
- }
-
- @Override
- public int hashCode() {
- return (this.metadata.hashCode() * 31 + this.importClassName.hashCode());
- }
-
- @Override
- public String toString() {
- return this.importClassName;
- }
- }
- }
-
- }
- public interface ImportBeanDefinitionRegistrar {
- default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,
- BeanNameGenerator importBeanNameGenerator) {
-
- registerBeanDefinitions(importingClassMetadata, registry);
- }
- default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
- }
- }
在这里我们实现一个EnableMyImportClass注解,根据其值自动引入class,其实和@Iimport指定普通类一个意思,但是我们这里也可以将其作为条件来决定引入哪一个类,比如根据DB名字引名具体DB连接类,这里只是一个简单实现
- @Target(ElementType.TYPE)
- @Retention(RetentionPolicy.RUNTIME)
- @Import(MyDeferredImportSelector.class)
- public @interface EnableMyImportClass {
- String[] value();
- }
这里最为重要的逻辑是selectImports的内容,主要逻辑取到EnableMyImportClass里指定的具体值,其中这里内容其实是让EnableMyImportClass的值指定为具体的类名
- public class MyImportSelector implements ImportSelector {
- @Override
- public String[] selectImports(AnnotationMetadata importingClassMetadata) {
- Set
set= importingClassMetadata.getAnnotationTypes(); -
- String[] a=null;
- if (set.contains(EnableMyImportClass.class.getName())){
- MultiValueMap
valueMap= importingClassMetadata.getAllAnnotationAttributes(EnableMyImportClass.class.getName()); - Object o=valueMap.get("value");
-
- if (o instanceof ArrayList){
- ArrayList
arrayList=(ArrayList)o; -
- if (arrayList.size()==1){
-
- Object o1=arrayList.get(0);
- a=new String[Array.getLength(o1)];
- for(int i=0;i
- a[i]=(String)Array.get(o1,i);
- }
- }
-
- }
- }
- return a;
- }
-
- @Override
- public Predicate
getExclusionFilter() { - return ImportSelector.super.getExclusionFilter();
- }
- }
3.在@Configuration或@Component类中以下方式引入
@EnableMyImportClass({"com.extions.Person"})
如指定的类名不存在框将会报错,找不到引用类,比如如下
如果@EnableMyImportClass({"testme"}),其中testmes是不存在的,运行时会报如下错误
十一月 22, 2022 3:02:02 下午 org.springframework.context.support.AbstractApplicationContext refresh
警告: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanDefinitionStoreException: Failed to process import candidates for configuration class [com.zhouyu.AppConfig]; nested exception is java.io.FileNotFoundException: class path resource [testme.class] cannot be opened because it does not exist
Exception in thread "main" org.springframework.beans.factory.BeanDefinitionStoreException: Failed to process import candidates for configuration class [com.zhouyu.AppConfig]; nested exception is java.io.FileNotFoundException: class path resource [testme.class] cannot be opened because it does not exist
at org.springframework.context.annotation.ConfigurationClassParser.processImports(ConfigurationClassParser.java:646)
引入实现DeferredImportSelector接口类样例
以下代码最为关键的是内部类MyGroup及getImportGroup方法
- public class MyDeferredImportSelector implements DeferredImportSelector {
- static class MyGroup implements Group{
- private AnnotationMetadata metadata;
-
- @Override
- public void process(AnnotationMetadata metadata, DeferredImportSelector selector) {
- this.metadata=metadata;
- }
-
- @Override
- public Iterable
selectImports() { - List
list=new ArrayList(); - Entry entry=new Entry(this.metadata, Person.class.getName());
- list.add(entry);
- return list;
- }
- }
-
- @Override
- public Class extends Group> getImportGroup() {
- return MyGroup.class;
- }
-
- public String[] selectImports(AnnotationMetadata importingClassMetadata) {
- return null;
- }
-
- @Override
- public Predicate
getExclusionFilter() { - return DeferredImportSelector.super.getExclusionFilter();
- }
- }
然后在Configuration类或@Compoent、@Service、@Controller中加入@Import(MyDeferredImportSelector.class),可能上面的方法来使用,只不过getGoup的逻辑需要修改
- @Target(ElementType.TYPE)
- @Retention(RetentionPolicy.RUNTIME)
- @Import(MyDeferredImportSelector.class)
- public @interface EnableMyImportClass {
- String[] value();
- }
总结
@Import注解是一个非常实用的方式,如果我们在系统开发时有非常定制化需要要根据不同条件引入不同的class作为bean,比如我们的系统需要支持不同的mq,但是不同的客户选择的MQ产品不一样,这样的话我们就可以采用这种方式为客户启用不同的mq类。