每一秒钟的时间都值得铭记

0%

ServiceLoader加载接口的所有实现

什么是ServiceLoader?

ServiceLoader 是由 JDK 提供的,在 java.util 包下的一个工具类,使用该工具类,可以加载一个接口的所有实现类。

getBeansOfType()

在 Spring 框架中,ApplicationContext 类提供了 getBeansOfType() 方法,同样可以启到和 ServiceLoader 相同的效果,可以获取同一类接口的所有实现类。
不过这个方法仅限于被 Spring 管理的项目,如果一个项目没有被 Spring 管理,也就无法使用该方法,这时就可以考虑使用 ServiceLoader

ServiceLoader的使用

接口和实现类

1
2
3
public interface IService {
void service();
}
1
2
3
4
5
6
public class UserServiceImpl implements IService {
@Override
public void service() {
System.out.println("用户服务");
}
}
1
2
3
4
5
6
public class RoleServiceImpl implements IService {
@Override
public void service() {
System.out.println("角色服务");
}
}
1
2
3
4
5
6
public class ResourcesServiceImpl implements IService {
@Override
public void service() {
System.out.println("资源服务");
}
}

配置META-INF/services

在项目的 resources 目录下,创建目录 META-INF/services,该目录为固定目录,ServiceLoader会默认读取该目录下的资源和文件。

META-INF/services 目录下创建一个文件,文件名为接口的全限定类名。
例如上述的 IService接口,应该在 META-INF/services 目录下创建 com.loader.IService 文件。

其中 com.loader 为自定义的包名称,并非定式包名。

1
2
3
4
resources
|_META-INF
|_services
|_com.loader.IService

com.loader.IService

1
2
3
4
# 注释
com.loader.UserServiceImpl
com.loader.RoleServiceImpl
com.loader.ResourcesServiceImpl

load()方法

1
2
3
4
5
6
7
8
public class ServiceMain {
public static void main(String[] args) {
ServiceLoader<IService> services = ServiceLoader.load(IService.class);
for (IService item : services) {
item.service();
}
}
}

ServiceLoader的缺点

  • ServiceLoader 虽然可以加载一个接口的所有实现类,但是本质上还是通过 META-INF/services 目录下配置文件,通过 Java 反射功能进行加载的,并无法实现动态加载的效果。

  • ServiceLoader 因为是通过反射进行加载的,所有的接口实现类必须拥有一个无参构造函数,否则实现类无法被反射实例化。

Spring和ServiceLoader

Spring 的 getBeansOfType() 方法和 JDK 的 ServiceLoader 都可以实现类似的功能,那么在项目中应该优先使用哪个呢?

如果项目中使用了 Spring 框架,项目中的接口实现都已被 Spring 核心容器管理,那么建议优先使用 Spring 的 getBeansOfType() 方法,因为所有实现类被 Spring 核心容器管理的情况下,使用 getBeansOfType() 方法可以达到一种“伪动态”加载的效果,而且代码更具有扩展性。

如果项目只是一个普通的项目,那么可以使用 ServiceLoader 来进行接口实现类的加载。

坚持原创技术分享,您的支持将鼓励我继续创作!
-------------这是我的底线^_^-------------