本文已参与周末学习计划,点击链接查看详情:juejin.cn/post/696572…
概述
SPI全称为service provider interface ,是JDK自带的一种服务提供发现机制,类似于一种小型的IOC容器,可以自动依赖注入,动态替换服务扩展.一般用于提供第三方实现和扩展,可以增加框架的扩展和替换一些组件.目前很多框架使用了这个机制,比如spring,dubbo,还是数据库驱动包,设置JDK里面的NIO包中的SelectorProvider也是使用的这个机制.
SPI约定
在META-INF/services/目录中创建接口的权限名命名的文件,该文件内容为接口实现的全限名,文件编码为UTF-8 使用SeriveLoader.load(Class class) 动态加载接口的实现 实现类必须有一个无参构造 如果SPI的实现是一个jar,放在项目的classpath目录下
springmvc对servlet3.0的支持
在servlet3.0情况下,可以不使用web.xml文件,可以使用WebApplicationInitializer来初始化一个WebApplicationContext. WebApplicationInitializer加载机制:javaEE容器启动的时候会通过SPI机制寻找javax.servlet.ServletContainerInitializer的实现类,在spring-web包下面,有这个文件的定义.
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
throws ServletException {
List<WebApplicationInitializer> initializers = new LinkedList<WebApplicationInitializer>();
if (webAppInitializerClasses != null) {
for (Class<?> waiClass : webAppInitializerClasses) {
// Be defensive: Some servlet containers provide us with invalid classes,
// no matter what @HandlesTypes says...
if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
try {
initializers.add((WebApplicationInitializer) waiClass.newInstance());
}
catch (Throwable ex) {
throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
}
}
}
}
if (initializers.isEmpty()) {
servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
return;
}
AnnotationAwareOrderComparator.sort(initializers);
servletContext.log("Spring WebApplicationInitializers detected on classpath: " + initializers);
for (WebApplicationInitializer initializer : initializers) {
initializer.onStartup(servletContext);
}
}
}
在onStartup方法上面有一段这样的注释:
Because this class declares @{@code >HandlesTypes(WebApplicationInitializer.class)}, Servlet 3.0+ containers will automatically scan the classpath for implementations of > Spring’s {@code WebApplicationInitializer} interface and provide the set of all such types to the {@code webAppInitializerClasses} parameter of this >method.
因为这个类的注解是HandlesTypes(WebApplicationInitializer.class),servlet3.0容器会自动扫描classpath下面对WebApplicationInitializer接口的实现类,并以set类型的参数提供给SpringServletContainerInitializer.onStartup()方法
自己使用SPI特性实现一个service
新建一个spi-demo的工程,项目结构如下图:
- 定义service接口
package com.spi.demo.service;
public interface UserService {
String sayHello(String userName);
}
- 提供provider实现
package com.spi.demo.service.impl;
import com.spi.demo.service.UserService;
public class UserServiceImpl implements UserService {
@Override
public String sayHello(String userName) {
return new StringBuffer("hello,").append(userName).append(".").toString();
}
}
- 添加配置 resouces/META-INF/services/目录下新建com.spi.demo.service.UserService文件,编码为UTF-8 文件内容如下:
com.spi.demo.service.impl.UserServiceImpl
- 运行测试
hello,张三.