什么是ServiceLoader?
ServiceLoader 是由 JDK 提供的,在 java.util
包下的一个工具类,使用该工具类,可以加载一个接口的所有实现类。
getBeansOfType()
在 Spring 框架中,ApplicationContext
类提供了 getBeansOfType()
方法,同样可以启到和 ServiceLoader
相同的效果,可以获取同一类接口的所有实现类。
不过这个方法仅限于被 Spring 管理的项目,如果一个项目没有被 Spring 管理,也就无法使用该方法,这时就可以考虑使用 ServiceLoader
。
ServiceLoader的使用
接口和实现类
1 | public interface IService { |
1 | public class UserServiceImpl implements IService { |
1 | public class RoleServiceImpl implements IService { |
1 | public class ResourcesServiceImpl implements IService { |
配置META-INF/services
在项目的 resources
目录下,创建目录 META-INF/services
,该目录为固定目录,ServiceLoader
会默认读取该目录下的资源和文件。
在 META-INF/services
目录下创建一个文件,文件名为接口的全限定类名。
例如上述的 IService
接口,应该在 META-INF/services
目录下创建 com.loader.IService
文件。
其中
com.loader
为自定义的包名称,并非定式包名。
1 | resources |
com.loader.IService
1 | # 注释 |
load()方法
1 | public class ServiceMain { |
ServiceLoader的缺点
ServiceLoader 虽然可以加载一个接口的所有实现类,但是本质上还是通过
META-INF/services
目录下配置文件,通过 Java 反射功能进行加载的,并无法实现动态加载的效果。ServiceLoader 因为是通过反射进行加载的,所有的接口实现类必须拥有一个无参构造函数,否则实现类无法被反射实例化。
Spring和ServiceLoader
Spring 的 getBeansOfType()
方法和 JDK 的 ServiceLoader
都可以实现类似的功能,那么在项目中应该优先使用哪个呢?
如果项目中使用了 Spring 框架,项目中的接口实现都已被 Spring 核心容器管理,那么建议优先使用 Spring 的 getBeansOfType()
方法,因为所有实现类被 Spring 核心容器管理的情况下,使用 getBeansOfType()
方法可以达到一种“伪动态”加载的效果,而且代码更具有扩展性。
如果项目只是一个普通的项目,那么可以使用 ServiceLoader
来进行接口实现类的加载。