当前位置: 代码网 > it编程>编程语言>Java > 解读spring.factories文件配置详情

解读spring.factories文件配置详情

2025年03月22日 Java 我要评论
使用场景在程序开发中,可能会出现包名不一样的情况(如:pom 依赖的很多的 jar),如何解决spring boot不能被默认路径扫描呢?方法一:在 spring boot application 主

使用场景

在程序开发中,可能会出现包名不一样的情况(如:pom 依赖的很多的 jar),如何解决spring boot不能被默认路径扫描呢?

  • 方法一:在 spring boot application 主类上使用 @import 注解。
  • 方法二:使用 spring.factories 文件

方法一比较简单,在此就不做过多介绍,主要谈谈 spring.factories 使用。

作用

spring.factories 的作用是使用外部 jar 时不用再写配置,会自动加入已经配置好的配置。

内部原理机制

spring.factories 这种机制实际上是仿照 java 中的 spi 扩展机制实现的。

spi机制

spi全称service provider interface,是 java 提供的一套用来被第三方实现或者扩展的接口,其意义在于为某个接口寻找服务的实现,主要应用在框架中用来寻找组件,提高扩展性。

面向的对象设计里,我们一般推荐模块之间基于接口编程,模块之间不对实现类进行硬编码。一旦代码里涉及了具体的实现类,就违反了可插拔的原则,如果需要替换一种实现,就需要修改代码。为了实现在模块装配的时候能不在程序里动态指明,这就需要一种服务发现机制。

  • java 中的 spi 机制:为某个接口寻找服务的实现的机制,有点类似 ioc 的思想,就是将装配的控制权移到程序之外,在模块化设计中这个机制很重要。
  • spring boot 中的 spi 机制:在 meta-info/spring.factories 文件中配置接口的实现类名称,然后在程序中读取这些配置文件并实例化。这种自定义的 spi 机制就是 spring boot starter 实现的基础。

spring factories 实现原理

spring.factories 实现是依赖 spring-core 包里的 springfactoriesloader 类,这个类实现了检索 meta-inf/spring.factories 文件,并获取指定接口的配置的功能。

这个类中定义了两个对外的方法:

  • loadfactories:根据接口类获取其实现类的实例,这个方法返回的是对象列表
  • loadfactorynames:根据接口获取其接口类的名称,这个方法返回的是类名的列表

上面两个方法的关键都是从指定的 classloader 中获取 spring.factories 文件,并解析得到类名列表,具体代码如下:

public final class springfactoriesloader {
    public static final string factories_resource_location = "meta-inf/spring.factories";
    private static final log logger = logfactory.getlog(springfactoriesloader.class);
    private static final map<classloader, multivaluemap<string, string>> cache = new concurrentreferencehashmap();

    private springfactoriesloader() {
    }

    public static <t> list<t> loadfactories(class<t> factorytype, @nullable classloader classloader) {
        assert.notnull(factorytype, "'factorytype' must not be null");
        classloader classloadertouse = classloader;
        if (classloader == null) {
            classloadertouse = springfactoriesloader.class.getclassloader();
        }

        list<string> factoryimplementationnames = loadfactorynames(factorytype, classloadertouse);
        if (logger.istraceenabled()) {
            logger.trace("loaded [" + factorytype.getname() + "] names: " + factoryimplementationnames);
        }

        list<t> result = new arraylist(factoryimplementationnames.size());
        iterator var5 = factoryimplementationnames.iterator();

        while(var5.hasnext()) {
            string factoryimplementationname = (string)var5.next();
            result.add(instantiatefactory(factoryimplementationname, factorytype, classloadertouse));
        }

        annotationawareordercomparator.sort(result);
        return result;
    }

    public static list<string> loadfactorynames(class<?> factorytype, @nullable classloader classloader) {
        string factorytypename = factorytype.getname();
        return (list)loadspringfactories(classloader).getordefault(factorytypename, collections.emptylist());
    }

    private static map<string, list<string>> loadspringfactories(@nullable classloader classloader) {
        multivaluemap<string, string> result = (multivaluemap)cache.get(classloader);
        if (result != null) {
            return result;
        } else {
            try {
                enumeration<url> urls = classloader != null ? classloader.getresources("meta-inf/spring.factories") : classloader.getsystemresources("meta-inf/spring.factories");
                linkedmultivaluemap result = new linkedmultivaluemap();

                while(urls.hasmoreelements()) {
                    url url = (url)urls.nextelement();
                    urlresource resource = new urlresource(url);
                    properties properties = propertiesloaderutils.loadproperties(resource);
                    iterator var6 = properties.entryset().iterator();

                    while(var6.hasnext()) {
                        entry<?, ?> entry = (entry)var6.next();
                        string factorytypename = ((string)entry.getkey()).trim();
                        string[] var9 = stringutils.commadelimitedlisttostringarray((string)entry.getvalue());
                        int var10 = var9.length;

                        for(int var11 = 0; var11 < var10; ++var11) {
                            string factoryimplementationname = var9[var11];
                            result.add(factorytypename, factoryimplementationname.trim());
                        }
                    }
                }

                cache.put(classloader, result);
                return result;
            } catch (ioexception var13) {
                throw new illegalargumentexception("unable to load factories from location [meta-inf/spring.factories]", var13);
            }
        }
    }

    private static <t> t instantiatefactory(string factoryimplementationname, class<t> factorytype, classloader classloader) {
        try {
            class<?> factoryimplementationclass = classutils.forname(factoryimplementationname, classloader);
            if (!factorytype.isassignablefrom(factoryimplementationclass)) {
                throw new illegalargumentexception("class [" + factoryimplementationname + "] is not assignable to factory type [" + factorytype.getname() + "]");
            } else {
                return reflectionutils.accessibleconstructor(factoryimplementationclass, new class[0]).newinstance();
            }
        } catch (throwable var4) {
            throw new illegalargumentexception("unable to instantiate factory class [" + factoryimplementationname + "] for factory type [" + factorytype.getname() + "]", var4);
        }
    }
}

从上述代码中可以看到,在这个方法中会遍历整个 classloader 中所有 jar 包下的 spring.factories 文件,文件之间不会相互影响配置,也不回被别人的配置覆盖。

spring.factories 的是通过 properties 解析得到的,所以在写文件中的内容都是按照下面这种方式配置的。

用法及配置

spring.factories 文件必须放在 resources 目录下的 meta-inf 的目录下,否则不会生效。如果一个接口希望配置多个实现类,可以用","分割。

spring.factories 各种配置的具体含义

applicationcontextinitializer

  • 该配置项用来配置实现了 applicationcontextinitializer 接口的类,这些类用来实现上下文初始化。
  • 配置如下:
org.springframework.context.applicationcontextinitializer=\
com.xh.config.myapplicationcontextinitializer
public class myapplicationcontextinitializer implements applicationcontextinitializer<configurableapplicationcontext> {
 
    @override
    public void initialize(configurableapplicationcontext applicationcontext) {
        system.out.println("myapplicationcontextinitializer.initialize() " + applicationcontext);
    }
 
}

applicationlistener

  • 配置应用程序监听器,该监听器必须实现 applicationlistener 接口。它可以用来监听 applicationevent 事件。
  • 配置如下:
org.springframework.context.applicationlistener=\
com.xh.factories.listener.emailapplicationlistener
@slf4j
public class emailapplicationlistener implements applicationlistener<emailmessageevent> {

    @override
    public void onapplicationevent(emailmessageevent event) {
        log.info("模拟发送邮件... ");
        log.info("emailapplicationlistener 接受到的消息:{}", event.getcontent());
    }
}

autoconfigurationimportlistener

  • 该配置项用来配置自动配置导入监听器,监听器必须实现 autoconfigurationimportlistener 接口。
  • 该监听器可以监听 autoconfigurationimportevent 事件。
  • 配置如下:
org.springframework.boot.autoconfigure.autoconfigurationimportlistener=\
com.xh.config.myautoconfigurationimportlistener
public class myautoconfigurationimportlistener implements autoconfigurationimportlistener {
 
    @override
    public void onautoconfigurationimportevent(autoconfigurationimportevent event) {
        system.out.println("myautoconfigurationimportlistener.onautoconfigurationimportevent() " + event);
    }
 
}

autoconfigurationimportfilter

  • 配置自动配置导入过滤器,过滤器必须实现 autoconfigurationimportfilter 接口。
  • 该过滤器用来过滤那些自动配置类可用。
  • 配置如下:
org.springframework.boot.autoconfigure.autoconfigurationimportfilter=\
com.xh.config.myconfigurationcondition
public class myconfigurationcondition implements autoconfigurationimportfilter {
 
    @override
    public boolean[] match(string[] autoconfigurationclasses, autoconfigurationmetadata autoconfigurationmetadata) {
        system.out.println("myconfigurationcondition.match() autoconfigurationclasses=" +  arrays.tostring(autoconfigurationclasses) + ", autoconfigurationmetadata=" + autoconfigurationmetadata);
        return new boolean[0];
    }
 
}

enableautoconfiguration

  • 配置自动配置类。这些配置类需要添加 @configuration 注解。
  • 配置如下:
org.springframework.boot.autoconfigure.enableautoconfiguration=\
com.xh.config.myconfiguration
@configuration
public class myconfiguration {
 
    public myconfiguration() {
        system.out.println("myconfiguration()");
    }
 
}

failureanalyzer

  • 配置自定的错误分析类,该分析器需要实现 failureanalyzer 接口。
  • 配置如下:
org.springframework.boot.diagnostics.failureanalyzer=\
com.huangx.springboot.autoconfig.myfailureanalyzer
/**
 * 自定义错误分析器
 */
public class myfailureanalyzer implements failureanalyzer {
 
    @override
    public failureanalysis analyze(throwable failure) {
        system.out.println("myfailureanalyzer.analyze() failure=" + failure);
        return new failureanalysis("myfailureanalyzer execute", "test spring.factories", failure);
    }
 
}

templateavailabilityprovider

  • 配置模板的可用性提供者,提供者需要实现 templateavailabilityprovider 接口。
  • 配置如下:
org.springframework.boot.autoconfigure.template.templateavailabilityprovider=\
com.huangx.springboot.autoconfig.mytemplateavailabilityprovider
/**
 * 验证指定的模板是否支持
 */
public class mytemplateavailabilityprovider implements templateavailabilityprovider {
 
    @override
    public boolean istemplateavailable(string view, environment environment, classloader classloader, resourceloader resourceloader) {
        system.out.println("mytemplateavailabilityprovider.istemplateavailable() view=" + view + ", environment=" + environment + ", classloader=" + classloader + "resourceloader=" + resourceloader);
        return false;
    }
 
}

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com