自身只定义接口,通过其他jar来提供实现,从而可以实现jar包替换后即可执行,实现IoC,可用于plugins/modules。
(1)Java Service Loader
JDK 6开始提供java.util.ServiceLoader
https://docs.oracle.com/javase/tutorial/ext/basics/spi.html
https://docs.oracle.com/javase/6/docs/api/java/util/ServiceLoader.html
定义实现类
META-INF/services/[InterfaceFQCN]
比如:
META-INF/services/com.rensanning.spi.Sample
引用
com.rensanning.spi.SampleImpl1 # comment1
com.rensanning.spi.SampleImpl2 # comment2
com.rensanning.spi.SampleImpl2 # comment2
-可以定义多行,一行一个实现(多个实现具体如何用是在程序内部控制的)
-#号开头的是注释
-必须UTF-8编码(避免使用非英语字符)
获取实现类
ServiceLoader<Sample> loader = ServiceLoader.load(Sample.class); for (Sample provider: loader) { // ... }
-默认采用的是当前线程的类加载器(Thread Context Classloader)
-可以自己指定类加载器:ServiceLoader.load(Sample.class, ClassLoader.getSystemClassLoader());
-ServiceLoader只能通过无参构造函数对服务实例
-ServiceLoader返回一个Iterator对象对服务实例懒加载,只有调用next()方法时才会实例化下一个服务实例
集成Spring
@Configuration public class ServiceConfiguration { @Bean public ServiceListFactoryBean serviceListFactoryBean() { ServiceListFactoryBean serviceListFactoryBean = new ServiceListFactoryBean(); serviceListFactoryBean.setServiceType(Sample.class); return serviceListFactoryBean; } }
Object object = serviceListFactoryBean.getObject();
JDK1.5通过sun.misc.Service(未公开的API)来加载。
Iterator<Sample> i = Service.providers(Sample.class); while (i.hasNext()) { Sample s = i.next(); // ... }
-其实META-INF/services是JDK 1.3开始就有的“Java Extension Mechanism”
最典型的2个应用:JDBC、Logger
JDBC
JDBC4 定义了接口java.sql.Driver,具体实现可以是MySQL、PostgreSQL等
mysql-connector-java-5.1.22-bin.jar/META-INF/services/java.sql.Driver
引用
com.mysql.jdbc.Driver
postgresql-9.4-1201-jdbc4.jar/META-INF/services/java.sql.Driver
引用
org.postgresql.Driver
java.sql.DriverManager在初始化的时候会主动加载这些驱动类,不需要Class.forName()。
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
Logger
可以通过实现来切换JUL,Logback, Log4J等
SLF4J 定义了接口org.slf4j.Logger,但还不支持SPI,仍采用的是StaticLoggerBinder来实现的。
https://jira.qos.ch/browse/SLF4J-227
JBoss Logging:META-INF/services/org.jboss.logging.LoggerProvider
common-logging:META-INF/services/org.apache.commons.logging.LogFactory
OSGi 和 SPI之间还有些纠纷,具体可以看看这里:
http://blog.osgi.org/2013/02/javautilserviceloader-in-osgi.html
(2)Spring Factories Loader
Spring自己实现了一个类似于属性文件的配置文件: META-INF/spring.factories
http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/core/io/support/SpringFactoriesLoader.html
http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/factory/serviceloader/package-summary.html
比如:
spring-boot-autoconfigure-1.5.2.RELEASE.jar/META-INF/spring.factories
引用
# Template availability providers
org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider=\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.mustache.MustacheTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.web.JspTemplateAvailabilityProvider
org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider=\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.mustache.MustacheTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.web.JspTemplateAvailabilityProvider
org.springframework.boot.autoconfigure.template.TemplateAvailabilityProviders
List<TemplateAvailabilityProvider> providers = SpringFactoriesLoader.loadFactories(TemplateAvailabilityProvider.class, classLoader);
参考:
https://dzone.com/articles/java-service-loader-vs-spring-factories-loader
http://stackoverflow.com/questions/2954372/difference-between-spi-and-api