这个类借助了@import注解,用来支持SpringBoot的SPI机制,让SPI更加方便使用。
第一步:
假设我们现在已经知道了一个类继承了SpringFactoryimportSelector,如下:
public class EnableDiscoveryClientimportSelector
extends SpringFactoryimportSelector
{
// ..
}
那我们可以直接获取到SpringFactoryimportSelector这个在泛型位置上的注解,我们先看下这个示例:
public class Test {
public static void main(String[] args) {
Class> clazz = GenericTypeResolver.resolveTypeArgument(
EnableDiscoveryClientimportSelector.class,
SpringFactoryimportSelector.class);
System.out.println(clazz);
// 输出:interface org.springframework.cloud.client.discovery.EnableDiscoveryClient
}
}
第二步:
如果我们知道了这个注解,那么我们可以借助之前说到的springboot的spi
(加载所有jar包下meta-INF/spring.factories文件),中的指定注解对应的配置。
拿到这些配置之后(直接super调用父类的方法就可以了),然后你又拿到了这个指定的注解,就可以使用@import的机制拿到类上指定注解的信息,这样就可以达到灵活配置bean的作用了。
这样说可能有点抽象,我们看个具体的,比如下面这个注解:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@documented
@Inherited
@import(EnableDiscoveryClientimportSelector.class)
public @interface EnableDiscoveryClient {
boolean autoRegister() default true;
}
假设这个注解应用在了一个类上面,那么spring会处理到上面的@import注解,
发现它是个importSelector,然后就实例化它,恰恰在这个实例化的时候,
父类SpringFactoryimportSelector构造方法里它解析了当前类上的泛型注解(可以看上面面的Test示例),
获取到了泛型EnableDiscoveryClient, 然后就回调selectimports方法,
恰恰也是在父类的selectimports方法里,
它又借助SpringFactoriesLoader.loadFactoryNames(this.annotationClass, this.beanClassLoader),
annoationClass就是EnableDiscoveryClient,
那么这个注解在spring.factories文件中对应的配置类就都获取到了。
而且selectimport还回传了Annotationmetadata这个参数,
包含了前面@import应用到的具体的类上的所有注解信息,
那么就可以获取到@EnableDiscoveryCli-ent里面配置的信息了。
然后利用这个信息,我们再去过滤刚刚拿到的所有配置。这个设计真nb。
那么,利用这个原理,框架就可以自定义自己的注解去开启配置,比如@EnableXXX,并且这个开启功能是结合SPI机制的。
下面我们来具体的看下这个类的实现
public abstract class SpringFactoryimportSelector// 它继承了DeferredimportSelector接口,这很重要 implements DeferredimportSelector, BeanClassLoaderAware, EnvironmentAware { private ClassLoader beanClassLoader; private Class annotationClass; private Environment environment; // environment也拿到了,也就意味着个可以拿到配置的信息来做不同的配置(可以设计成开关) private final Log log = LogFactory.getLog(SpringFactoryimportSelector.class); @SuppressWarnings("unchecked") protected SpringFactoryimportSelector() { // 泛型的处理是在构造方法里实现的,具体使用的例子可以参看上面写的 this.annotationClass = (Class ) GenericTypeResolver .resolveTypeArgument(this.getClass(), SpringFactoryimportSelector.class); } @Override public String[] selectimports(Annotationmetadata metadata) { // 嘿嘿,这里有个开关,isEnbaled()是个抽象方法, // 子类经常可以结合environment拿到配置,来决定是否开启功能(其实就是否往spring注入相关组件) // 反映在代码里,就是没开启的话,下面就返回个空的字符串数组了 if (!isEnabled()) { return new String[0]; } // 拿到标注在具体类上的指定注解对象 AnnotationAttributes attributes = AnnotationAttributes.fromMap( metadata.getAnnotationAttributes(this.annotationClass.getName(), true)); // 断言,也就是说,一定会有这个注解,按正常流程来说,都有这个注解 Assert.notNull(attributes, "No " + getSimpleName() + " attributes found. Is " + metadata.getClassName() + " annotated with @" + getSimpleName() + "?"); // 这里就开始使用Springboot的Spi了,注意传入了指定的注解,也就是前面说的拿到指定注解对应的配置类 List factories = new ArrayList<>(new linkedHashSet<>(SpringFactoriesLoader .loadFactoryNames(this.annotationClass, this.beanClassLoader))); // 如果SPI中并没有没有配置指定注解的配置类, 那就通过抛异常的方式告诉说是不是忘了添加启动器 // 因为启动器里面一般会有配置的 // 还有一个配置,问是否有默认工厂,如果有默认工厂的话,那spring也不管了,但是我们得通过实现取告诉spring就行了 if (factories.isEmpty() && !hasDefaultFactory()) { throw new IllegalStateException("Annotation @" + getSimpleName() + " found, but there are no implementations. Did you forget to " +"include a starter?"); } // 超过一个配置,也就警告一下 if (factories.size() > 1) { // there should only ever be one DiscoveryClient, but there might be more than // one factory log.warn("More than one implementation " + "of @" + getSimpleName() + " (now relying on @Conditionals to pick one): " + factories); } // 返回 return factories.toArray(new String[factories.size()]); // 这是默认的逻辑,springCloud已经帮我们写好模板了,可以直接复用这段逻辑,定制配置。 } protected boolean hasDefaultFactory() { return false; } protected abstract boolean isEnabled(); protected String getSimpleName() { return this.annotationClass.getSimpleName(); } protected Class getAnnotationClass() { return this.annotationClass; } protected Environment getEnvironment() { return this.environment; } @Override public void setEnvironment(Environment environment) { this.environment = environment; } @Override public void setBeanClassLoader(ClassLoader classLoader) { this.beanClassLoader = classLoader; } }



