栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

SpringFactoryImportSelector<T>

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

SpringFactoryImportSelector<T>

这个类借助了@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;
	}

}

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/281416.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号