• spring源码 - 理解@Import原理及运用


    先看@Import解析代码

    1. for (SourceClass candidate : importCandidates) {
    2. if (candidate.isAssignable(ImportSelector.class)) {
    3. Class candidateClass = candidate.loadClass();
    4. ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class, this.environment, this.resourceLoader, this.registry);
    5. if (selectorFilter != null) {
    6. exclusionFilter = exclusionFilter.or(selectorFilter);
    7. }
    8. if (selector instanceof DeferredImportSelector) {
    9. this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
    10. } else {
    11. String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
    12. Collection importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
    13. processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
    14. }
    15. } else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
    16. Class candidateClass = candidate.loadClass();
    17. ImportBeanDefinitionRegistrar registrar =
    18. ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
    19. this.environment, this.resourceLoader, this.registry);
    20. configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
    21. }
    22. else {
    23. this.importStack.registerImport(
    24. currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
    25. processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
    26. }
    27. }
    28. }

    看代码得到的可引入哪些class

    从以上代码我们可以看出在@Import注解中可以看出import可以引入四种类型的class

    1.普通类

    主要是通过调用processConfigurationClass方法处理

    2.实现ImportSelector接口的类

    主要实现selectImports方法,返回字符串数组,数组内容须名class全名,包括package部分

    1. public interface ImportSelector {
    2. String[] selectImports(AnnotationMetadata importingClassMetadata);
    3. @Nullable
    4. default Predicate getExclusionFilter() {
    5. return null;
    6. }
    7. }

    3.实现DeferredImportSelector接口类

    从下面的代码可以看出DeferredImportSelector接口是继承ImportSelector ,所以实现类需要实现getImportGroup方法,面且还需要实现其中内部接口类Group

    另外如果getImportGroup返回null的值,需要实现其父接口ImportSelector 的selectImports方法,要不然会出现异常

    1. public interface DeferredImportSelector extends ImportSelector {
    2. @Nullable
    3. default Classextends Group> getImportGroup() {
    4. return null;
    5. }
    6. interface Group {
    7. void process(AnnotationMetadata metadata, DeferredImportSelector selector);
    8. Iterable selectImports();
    9. class Entry {
    10. private final AnnotationMetadata metadata;
    11. private final String importClassName;
    12. public Entry(AnnotationMetadata metadata, String importClassName) {
    13. this.metadata = metadata;
    14. this.importClassName = importClassName;
    15. }
    16. public AnnotationMetadata getMetadata() {
    17. return this.metadata;
    18. }
    19. public String getImportClassName() {
    20. return this.importClassName;
    21. }
    22. @Override
    23. public boolean equals(@Nullable Object other) {
    24. if (this == other) {
    25. return true;
    26. }
    27. if (other == null || getClass() != other.getClass()) {
    28. return false;
    29. }
    30. Entry entry = (Entry) other;
    31. return (this.metadata.equals(entry.metadata) && this.importClassName.equals(entry.importClassName));
    32. }
    33. @Override
    34. public int hashCode() {
    35. return (this.metadata.hashCode() * 31 + this.importClassName.hashCode());
    36. }
    37. @Override
    38. public String toString() {
    39. return this.importClassName;
    40. }
    41. }
    42. }
    43. }

    4.实现ImportBeanDefinitionRegistrar接口类 

    1. public interface ImportBeanDefinitionRegistrar {
    2. default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,
    3. BeanNameGenerator importBeanNameGenerator) {
    4. registerBeanDefinitions(importingClassMetadata, registry);
    5. }
    6. default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    7. }
    8. }

    引入实现ImportSelector接口类样例

    在这里我们实现一个EnableMyImportClass注解,根据其值自动引入class,其实和@Iimport指定普通类一个意思,但是我们这里也可以将其作为条件来决定引入哪一个类,比如根据DB名字引名具体DB连接类,这里只是一个简单实现

    1.准备EnableMyImportClass类 

    1. @Target(ElementType.TYPE)
    2. @Retention(RetentionPolicy.RUNTIME)
    3. @Import(MyDeferredImportSelector.class)
    4. public @interface EnableMyImportClass {
    5. String[] value();
    6. }

    2.准备实现ImportSelectoor类

    这里最为重要的逻辑是selectImports的内容,主要逻辑取到EnableMyImportClass里指定的具体值,其中这里内容其实是让EnableMyImportClass的值指定为具体的类名

    1. public class MyImportSelector implements ImportSelector {
    2. @Override
    3. public String[] selectImports(AnnotationMetadata importingClassMetadata) {
    4. Set set= importingClassMetadata.getAnnotationTypes();
    5. String[] a=null;
    6. if (set.contains(EnableMyImportClass.class.getName())){
    7. MultiValueMap valueMap= importingClassMetadata.getAllAnnotationAttributes(EnableMyImportClass.class.getName());
    8. Object o=valueMap.get("value");
    9. if (o instanceof ArrayList){
    10. ArrayList arrayList=(ArrayList)o;
    11. if (arrayList.size()==1){
    12. Object o1=arrayList.get(0);
    13. a=new String[Array.getLength(o1)];
    14. for(int i=0;i
    15. a[i]=(String)Array.get(o1,i);
    16. }
    17. }
    18. }
    19. }
    20. return a;
    21. }
    22. @Override
    23. public Predicate getExclusionFilter() {
    24. return ImportSelector.super.getExclusionFilter();
    25. }
    26. }

    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方法

    1. public class MyDeferredImportSelector implements DeferredImportSelector {
    2. static class MyGroup implements Group{
    3. private AnnotationMetadata metadata;
    4. @Override
    5. public void process(AnnotationMetadata metadata, DeferredImportSelector selector) {
    6. this.metadata=metadata;
    7. }
    8. @Override
    9. public Iterable selectImports() {
    10. List list=new ArrayList();
    11. Entry entry=new Entry(this.metadata, Person.class.getName());
    12. list.add(entry);
    13. return list;
    14. }
    15. }
    16. @Override
    17. public Classextends Group> getImportGroup() {
    18. return MyGroup.class;
    19. }
    20. public String[] selectImports(AnnotationMetadata importingClassMetadata) {
    21. return null;
    22. }
    23. @Override
    24. public Predicate getExclusionFilter() {
    25. return DeferredImportSelector.super.getExclusionFilter();
    26. }
    27. }

    然后在Configuration类或@Compoent、@Service、@Controller中加入@Import(MyDeferredImportSelector.class),可能上面的方法来使用,只不过getGoup的逻辑需要修改

    1. @Target(ElementType.TYPE)
    2. @Retention(RetentionPolicy.RUNTIME)
    3. @Import(MyDeferredImportSelector.class)
    4. public @interface EnableMyImportClass {
    5. String[] value();
    6. }

    总结

         @Import注解是一个非常实用的方式,如果我们在系统开发时有非常定制化需要要根据不同条件引入不同的class作为bean,比如我们的系统需要支持不同的mq,但是不同的客户选择的MQ产品不一样,这样的话我们就可以采用这种方式为客户启用不同的mq类。

  • 相关阅读:
    C++中静态成员变量和普通成员变量、私有成员变量和公有成员变量的区别
    Nature子刊|“二代+三代”宏基因组学揭示肠道微生物群的个性化结构变异
    使用T2-U和Sensor_Hub开发一款智能温湿度计
    HarmonyOS—低代码开发Demo示例
    波卡三季度报告:已实现白皮书目标,异步支持与应用链技术推进
    日本岛津电子天平UW UX 系列series 精密电子天平Shimadzu使用说明
    ThreadLocal的简单使用及实现的原理
    激活函数介绍
    C++图解模板
    超大规模的产业实用语义分割数据集PSSL与预训练模型开源啦!
  • 原文地址:https://blog.csdn.net/lin000_0/article/details/127991227