敬礼,朋友。 这个星期五将是新的
Java Developer课程组中的第一节课。 本出版物将致力于此课程。

许多Java开发人员使用
Spring来实现依赖关系。 有些人可能尝试过
Google Guice甚至
OSGi服务 。 但是许多人不知道Java已经有一个内置的DI。 您认为它出现在Java 11或12中吗? 不,它在Java 6中可用。
ServiceLoader提供了搜索和创建接口或抽象类的注册实例的功能。 如果您熟悉Spring,那么这与
Bean和
Autowired批注非常相似。 让我们看一下使用Spring和ServiceLoader的示例。 并讨论异同。
春天
首先,让我们看看如何在Spring中制作一个简单的DI。 创建一个简单的界面:
public interface SimpleService { String echo(String value); }
并执行接口:
import org.springframework.stereotype.Component; @Component public class SimpleServiceImpl implements SimpleService { public String echo(final String value) { return value; } }
查看
@Component
。 该注释将在Spring上下文中将我们的类注册为Bean。
还有我们的主要班级。
@SpringBootApplication public class SpringExample implements CommandLineRunner { private static final Logger log = LoggerFactory.getLogger(SpringExample.class); @Autowired List<SimpleService> simpleServices; public static void main(String[] args) { SpringApplication.run(SpringExample.class, args); } public void run(final String... strings) throws Exception { for (SimpleService simpleService : simpleServices) { log.info("Echo: " + simpleService.echo(strings[0])); } } }
请注意
SimpleService
组合
SimpleService
上的
@Autowired
批注。 注释
@SpringBootApplication
旨在自动搜索包中的bean。 然后在启动时,将它们自动注入到
SpringExample
。
服务加载器
我们将使用与Spring示例相同的接口,因此在此不再重复。 相反,立即查看服务的实现:
import com.google.auto.service.AutoService; @AutoService(SimpleService.class) public class SimpleServiceImpl implements SimpleService { public String echo(final String value) { return value; } }
在实现中,我们使用
@AutoService
批注“注册”服务的实例。 该注释仅在编译时才需要,因为javac使用它来自动生成服务注册文件(
译者注:对于包含@AutoService
maven依赖@AutoService
,请指定提供的作用域) :
META-INF/services/io.github.efenglu.serviceLoader.example.SimpleService
此文件包含实现服务的类的列表:
io.github.efenglu.serviceLoader.example.SimpleServiceImpl
文件名必须是服务(接口)的全名。 文件可以具有任意数量的实现,每个实现都位于单独的行上。
在实现中,
必须有一个没有参数的构造函数。 您可以手动创建这样的文件,但是使用注释要容易得多。 和主类:
public class ServiceLoaderExample { public static void main(String [] args) { final ServiceLoader<SimpleService> services = ServiceLoader.load(SimpleService.class); for (SimpleService service : services) { System.out.println("Echo: " + service.echo(args[0])); } } }
调用
ServiceLoader.load
方法可获取
ServiceLoader
,该服务可用于获取服务实例。 ServiceLoader实例为服务类型实现
Iterable
接口,因此,可以在
for each
循环中使用
services
变量。
那又怎样
两种方法都相对较小。 两者都可以与注释一起使用,因此非常易于使用。 那么,为什么要使用ServiceLoader而不是Spring?
依存关系
让我们看一下我们简单的Spring示例的依赖关系树:
[INFO] -----------< io.github.efenglu.serviceLoader:spring-example >----------- [INFO] Building spring-example 1.0.X-SNAPSHOT [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- maven-dependency-plugin:3.1.1:tree (default-cli) @ spring-example --- [INFO] io.github.efenglu.serviceLoader:spring-example:jar:1.0.X-SNAPSHOT [INFO] +- org.slf4j:slf4j-api:jar:1.7.25:compile [INFO] +- org.springframework:spring-context:jar:4.3.22.RELEASE:compile [INFO] | +- org.springframework:spring-aop:jar:4.3.22.RELEASE:compile [INFO] | +- org.springframework:spring-core:jar:4.3.22.RELEASE:compile [INFO] | | \- commons-logging:commons-logging:jar:1.2:compile [INFO] | \- org.springframework:spring-expression:jar:4.3.22.RELEASE:compile [INFO] +- org.springframework.boot:spring-boot-autoconfigure:jar:1.5.19.RELEASE:compile [INFO] +- org.springframework.boot:spring-boot:jar:1.5.19.RELEASE:compile [INFO] \- org.springframework:spring-beans:jar:4.3.22.RELEASE:compile
并与ServiceLoader进行比较:
[INFO] io.github.efenglu.serviceLoader:serviceLoader-example:jar:1.0.X-SNAPSHOT ## Only provided dependencies for the auto service annotation [INFO] \- com.google.auto.service:auto-service:jar:1.0-rc4:provided [INFO] +- com.google.auto:auto-common:jar:0.8:provided [INFO] \- com.google.guava:guava:jar:23.5-jre:provided [INFO] +- com.google.code.findbugs:jsr305:jar:1.3.9:provided [INFO] +- org.checkerframework:checker-qual:jar:2.0.0:provided [INFO] +- com.google.errorprone:error_prone_annotations:jar:2.0.18:provided [INFO] +- com.google.j2objc:j2objc-annotations:jar:1.1:provided [INFO] \- org.codehaus.mojo:animal-sniffer-annotations:jar:1.14:provided
如果我们不注意提供的依赖关系,则ServiceLoader
没有任何依赖关系。 没错,他只需要Java。
开发基于Spring的应用程序并不重要,但是如果要编写将在许多不同框架中使用的内容,或者如果您有小型控制台应用程序,那么这已经非常重要。
速度
对于控制台应用程序,ServiceLoader的启动时间比Spring Boot App短得多。 这是由于可下载代码的数量较少,没有扫描,没有反射,没有大型框架。
记忆
Spring不以节省内存而闻名。 如果内存使用对您很重要,请考虑使用ServiceLoader for DI。
Java模块
Java模块的关键方面之一是能够完全保护模块中的类免受模块外部代码的影响。 ServiceLoader是一种允许外部代码“访问”内部实现的机制。 Java模块允许您注册用于内部实现的服务,同时保留边界。
实际上,这是唯一正式批准的Java模块依赖注入支持机制。 Spring和大多数其他DI框架使用反射来查找和连接其组件。 但这与Java模块不兼容。 甚至反射也无法查看模块(除非您允许它,但是为什么需要允许它)。
结论
春天是一件好事。 它具有比ServiceLoader中更多的功能。 但是有时候ServiceLoader是正确的选择。 它简单,小巧,快速且始终可用。
我的
Git Repo中示例的完整源代码。
仅此而已。 在
课程中见!