SPI(service provider interface)详解|周末学习

501 阅读2分钟

本文已参与周末学习计划,点击链接查看详情: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包下面,有这个文件的定义.

image.png

@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的工程,项目结构如下图: image.png

  • 定义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,张三.

参考资料