0
点赞
收藏
分享

微信扫一扫

SPI机制

花姐的职场人生 2022-03-11 阅读 92
java

一、SPI机制理解

SPI 全称为 Service Provider  Interface 是一种服务发现机制。
它通过在ClassPath路径下的META-INF/services文件夹查找文件,自动加载文件里所定义的类。

实现步骤:
1、在ClassPath路径下的META-INF/services文件夹下创建文件
2、文件名为接口服务的全限定名,文件内容是多个实现类
3、API加载实现类。

二、Java SPI 案例

1、编写接口服务

public interface SPIService {
    void execute();
}

2、创建两个接口实现类 SPIServiceImpl1 和 SPIServiceImpl2

public class SPIServiceImpl1 implements SPIService {
    @Override
    public void execute() {
        System.out.println( this.getClass().getSimpleName() + "....execute..........");
    }
}
public class SPIServiceImpl2 implements SPIService{
    @Override
    public void execute() {
        System.out.println( this.getClass().getSimpleName() + "....execute..........");
    }
}

3、在META-INF/services 下创建文件,文件名为com.xyy.spi.java.SPIService,文件内容为两个实现类的全限定名

com.xyy.spi.java.SPIServiceImpl1
com.xyy.spi.java.SPIServiceImpl2

在这里插入图片描述

4、编写测试类

public class SPIMainClass {

    @Test
    public void test() {
       
        //方式一:sun.misc.Service 加载
        Iterator<SPIService> providers = Service.providers(SPIService.class);
        while (providers.hasNext()) {
            SPIService servcie = providers.next();
            servcie.execute();
        }
        System.out.println("**************************************************");
        //方式二:java.util.ServiceLoader 加载
        ServiceLoader<SPIService> load = ServiceLoader.load(SPIService.class);
        Iterator<SPIService> iterator = load.iterator();
        while (iterator.hasNext()) {
            SPIService servcie = iterator.next();
            servcie.execute();
        }

    }

}

运行结果:
在这里插入图片描述

三、源码分析

1、sun.misc.Service 加载源码分析流程图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wcrYt1jz-1646269344910)(9996E4D495A2439299B2D092B2343694)]

  总结:
    1、创建Service.LazyIterator,设置service等属性
    2、hasNext加载META-INF/services下接口文件内容,设置LazyIterator的nextName属性等
    3、next方法获得nextName,Class.forName加载类,反射实例化

2、java.util.ServiceLoader 加载源码分析流程图

在这里插入图片描述

总结:
  1、初始化 ServiceLoader,设置lookupIterator属性为LazyIterator
  2、hasNext先从缓存的providers中获取,为空,则去加载META-INF/services下接口文件内容,设置LazyIterator的nextName属性等
  3、next方法获得nextName,Class.forName加载类,反射实例化,放入providers

四、应用

1、JDBC驱动

1、创建自定义的MyDriver类,extends com.mysql.cj.jdbc.NonRegisteringDriver implements java.sql.Driver

    public class MyDriver extends com.mysql.cj.jdbc.NonRegisteringDriver implements java.sql.Driver{
      public MyDriver() throws SQLException {
      }
  
      static {
          try {
              System.out.println("register MyDriver...");
              //注册
              DriverManager.registerDriver(new MyDriver());
          } catch (SQLException var1) {
              throw new RuntimeException("Can't register MyDriver!");
          }
      }
  
      @Override
      public Connection connect(String url, Properties info) throws SQLException {
          System.out.println("创建连接..."+ url);
          System.out.println("jdbc配置信息..." + info);
          Connection connect = super.connect(url, info);
          System.out.println("数据库连接创建完成..."+ connect.toString());
          return connect;
      }
    }


2、在META-INF/services下创建java.sql.Driver文件,内容为:com.xyy.spi.jdbc.MyDriver

com.xyy.spi.jdbc.MyDriver

3、测试类

  @Test
  public  void test() throws Exception {

      String url = "jdbc:mysql://127.0.0.1:3306/db?characterEncoding=utf-8&serverTimezone=Asia/Shanghai&zeroDateTimeBehavior=convertToNull&autoReconnect=true&allowMultiQueries=true&useSSL=false";
      String user = "root";
      String password = "root";

      /**
       * 不需要这个啦
       */
      //Class.forName("com.xyy.spi.jdbc.MyDriver");
      Connection connection = DriverManager.getConnection(url, user, password);
      PreparedStatement preparedStatement = connection.prepareStatement("SELECT id FROM table");

      ResultSet resultSet = preparedStatement.executeQuery();
      while (resultSet.next()){
          Long id = resultSet.getLong(1);
          System.out.println("id=="+ id);
      }

      preparedStatement.close();
      connection.close();


  }

运行结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IzYIQE7V-1646269344912)(3252444B2B084A4EBFD3865DDC128BCF)]

如何加载到MyDriver?

源码分析流程图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kJuGb5n3-1646269344913)(9D1F23119EB8456E9FEB04AC0933F52E)]

总结:
   1、DriverManager类加载时,静态代码块调用loadInitialDrivers(),SPI机制加载META-INF/services/java.sql.Driver内的实现类
   2、加载MyDriver的静态方法,注册MyDriver
   3、DriverManager的getConnection方法,会遍历注册驱动,调用驱动类的connnect方法

2、Dubbo应用

dubbo的SPI机制:
1、接口有@SPI注解,指定默认实现
2、加载三个路径: META-INF/services/、META-INF/dubbo/、META-INF/services/internal

示例1 、扩展dubbo的Protocol接口服务

1、创建类MyProtocol,实现Protocol

   
   public class MyProtocol implements Protocol {

   /**
    * 我们重写了默认端口
    * @return
    */
   @Override
   public int getDefaultPort() {
       return 8888;
   }

   @Override
   public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
       return null;
   }

   @Override
   public <T> Invoker<T> refer(Class<T> aClass, URL url) throws RpcException {
       return null;
   }

   @Override
   public void destroy() {

   }
}

2、在META-INF/dubbo/下创建文件com.alibaba.dubbo.rpc.Protocol,内容

MyProtocol=com.xyy.spi.dubbo.MyProtocol

3、测试代码

    @Test
    public void testMyProtocol(){

        ExtensionLoader<Protocol> extensionLoader = ExtensionLoader.getExtensionLoader(Protocol.class);
        Protocol myProtocol = extensionLoader.getExtension("MyProtocol");
        System.out.println(myProtocol.getDefaultPort());
    }

运行结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AeuK61v4-1646269344914)(10EFF6AC278040D5A8FE3165C7218FAD)]

示例2、我们自己定义接口服务

1、创建接口DubboService,写上 @SPI(“dubboService1”)注解,默认是dubboService1

    @SPI("dubboService1")
    public interface DubboService {
        public void sayHello();
    }

2、创建DubboService接口的两个实现类

public class DubboServiceImp1 implements DubboService {
    @Override
    public void sayHello() {
        System.out.println("DubboServiceImp1...hello");
    }
}
public class DubboServiceImp2 implements DubboService {
    @Override
    public void sayHello() {
        System.out.println("DubboServiceImp2...hello");
    }
}

3、在META-INF/dubbo/下创建文件com.xyy.spi.dubbo.DubboService,内容

dubboService1=com.xyy.spi.dubbo.DubboServiceImp1
dubboService2=com.xyy.spi.dubbo.DubboServiceImp2

4、测试代码

    @Test
    public void testDubboService(){
        ExtensionLoader<DubboService> extensionLoader1 = ExtensionLoader.getExtensionLoader(DubboService.class);
        DubboService dubboService = extensionLoader1.getExtension("dubboService1");
        dubboService.sayHello();
    }

运行结果:
在这里插入图片描述

源码分析

入口:


ExtensionLoader.getExtensionLoader(DubboService.class);

看下 ExtensionLoader.getExtensionLoader方法:

    @SuppressWarnings("unchecked")
    public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
        if (type == null)
            throw new IllegalArgumentException("Extension type == null");
		//必须是接口	
        if (!type.isInterface()) {
            throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");
        }
		//必须有@SPI注解
        if (!withExtensionAnnotation(type)) {
            throw new IllegalArgumentException("Extension type(" + type +
                    ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
        }
        //从缓存中获取接口对应的ExtensionLoader,EXTENSION_LOADERS 是ConcurrentMap<Class<?>, ExtensionLoader<?>>
        ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
        if (loader == null) {
		    //创建ExtensionLoader,放入缓存
            EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
            loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
        }
        return loader;
    }

这个ExtensionLoader.withExtensionAnnotation方法,查看是否有SPI注解:

    private static <T> boolean withExtensionAnnotation(Class<T> type) {
        return type.isAnnotationPresent(SPI.class);
    }

创建 new ExtensionLoader(type)方法:

   private ExtensionLoader(Class<?> type) {
       //接口服务的class
       this.type = type;
   	//如果类型是ExtensionFactory.class 则设为null,否则就是再次使用SPI机制获得ExtensionFactory
       objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
   }

下面我们看下获得具体扩展的方法 这个getExtension(String name):

   public T getExtension(String name) {
       //为空,则报异常
       if (name == null || name.length() == 0)
           throw new IllegalArgumentException("Extension name == null");
   	//如是字符串"true",则获取默认的扩展实例。	
       if ("true".equals(name)) {
           return getDefaultExtension();
       }
       Holder<Object> holder = cachedInstances.get(name);
       if (holder == null) {
           cachedInstances.putIfAbsent(name, new Holder<Object>());
           holder = cachedInstances.get(name);
       }
       Object instance = holder.get();
       if (instance == null) {
           synchronized (holder) {
               instance = holder.get();
               if (instance == null) {
   			    //创建指定的扩展实例
                   instance = createExtension(name);
                   holder.set(instance);
               }
           }
       }
       return (T) instance;
   }

创建指定的扩展,createExtension方法:

   private T createExtension(String name) {
       //从配置文件中加载所有的拓展类,可得到“配置项名称”到“配置类”的映射关系表
       //获得指定扩展的class对象
       Class<?> clazz = getExtensionClasses().get(name);
       if (clazz == null) {
           throw findException(name);
       }
       try {
           T instance = (T) EXTENSION_INSTANCES.get(clazz);
           if (instance == null) {
   		    //实例化,放入缓存
               EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
               instance = (T) EXTENSION_INSTANCES.get(clazz);
           }
   		//依赖注入和cachedWrapperClasses,后面分析
           injectExtension(instance);
           Set<Class<?>> wrapperClasses = cachedWrapperClasses;
           if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
               for (Class<?> wrapperClass : wrapperClasses) {
                   instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
               }
           }
           return instance;
       } catch (Throwable t) {
           throw new IllegalStateException("Extension instance(name: " + name + ", class: " +
                   type + ")  could not be instantiated: " + t.getMessage(), t);
       }
   }

获得所有的扩展getExtensionClasses()方法:

  private Map<String, Class<?>> getExtensionClasses() {
      //缓存中获取映射关系表
      Map<String, Class<?>> classes = cachedClasses.get();
      if (classes == null) {
          synchronized (cachedClasses) {
              classes = cachedClasses.get();
              if (classes == null) {
  			    //加载映射关系表
                  classes = loadExtensionClasses();
                  cachedClasses.set(classes);
              }
          }
      }
      return classes;
  }

loadExtensionClasses()方法:

  private Map<String, Class<?>> loadExtensionClasses() {
      //获得注解指定的默认值
      final SPI defaultAnnotation = type.getAnnotation(SPI.class);
      if (defaultAnnotation != null) {
          String value = defaultAnnotation.value();
          if ((value = value.trim()).length() > 0) {
              String[] names = NAME_SEPARATOR.split(value);
              if (names.length > 1) {
                  throw new IllegalStateException("more than 1 default extension name on extension " + type.getName()
                          + ": " + Arrays.toString(names));
              }
  			// 获取@SPI注解中的值,并缓存起来
              if (names.length == 1) cachedDefaultName = names[0];
          }
      }

      Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
  	//加载指定文件夹下的配置文件,META-INF/services/、META-INF/dubbo/、META-INF/dubbo/internal/
      loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
      loadDirectory(extensionClasses, DUBBO_DIRECTORY);
      loadDirectory(extensionClasses, SERVICES_DIRECTORY);
      return extensionClasses;
  }

加载目录loadDirectory方法:

    private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir) {
	    //  fileName 是 META-INF/dubbo/com.xyy.spi.dubbo.DubboService
        String fileName = dir + type.getName();
        try {
            Enumeration<java.net.URL> urls;
            ClassLoader classLoader = findClassLoader();
            if (classLoader != null) {
                urls = classLoader.getResources(fileName);
            } else {
                urls = ClassLoader.getSystemResources(fileName);
            }
            if (urls != null) {
                while (urls.hasMoreElements()) {
                    java.net.URL resourceURL = urls.nextElement();
					//加载文件,一个resourceURL就是一个文件
                    loadResource(extensionClasses, classLoader, resourceURL);
                }
            }
        } catch (Throwable t) {
            logger.error("Exception when load extension class(interface: " +
                    type + ", description file: " + fileName + ").", t);
        }
    }

loadResource方法加载资源:

    private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) {
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), "utf-8"));
            try {
                String line;
                while ((line = reader.readLine()) != null) {
				    //去掉# 后面的注释
                    final int ci = line.indexOf('#');
                    if (ci >= 0) line = line.substring(0, ci);
                    line = line.trim();
                    if (line.length() > 0) {
                        try {
                            String name = null;
							//包含等号,按等号切割
                            int i = line.indexOf('=');
                            if (i > 0) {
                                name = line.substring(0, i).trim();
                                line = line.substring(i + 1).trim();
                            }
                            if (line.length() > 0) {
							    //加载类
                                loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name);
                            }
                        } catch (Throwable t) {
                            IllegalStateException e = new IllegalStateException("Failed to load extension class(interface: " + type + ", class line: " + line + ") in " + resourceURL + ", cause: " + t.getMessage(), t);
                            exceptions.put(line, e);
                        }
                    }
                }
            } finally {
                reader.close();
            }
        } catch (Throwable t) {
            logger.error("Exception when load extension class(interface: " +
                    type + ", class file: " + resourceURL + ") in " + resourceURL, t);
        }
    }

loadClass方法加载class:


    private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
        // clazz 必须是type类型的
		if (!type.isAssignableFrom(clazz)) {
            throw new IllegalStateException("Error when load extension class(interface: " +
                    type + ", class line: " + clazz.getName() + "), class "
                    + clazz.getName() + "is not subtype of interface.");
        }
		//判断clazz是否为标注了@Adaptive注解
        if (clazz.isAnnotationPresent(Adaptive.class)) {
            if (cachedAdaptiveClass == null) {
                cachedAdaptiveClass = clazz;
            } else if (!cachedAdaptiveClass.equals(clazz)) {
                throw new IllegalStateException("More than 1 adaptive class found: "
                        + cachedAdaptiveClass.getClass().getName()
                        + ", " + clazz.getClass().getName());
            }
        } 
		//判断是否是Wrapper类型
		else if (isWrapperClass(clazz)) {
            Set<Class<?>> wrappers = cachedWrapperClasses;
            if (wrappers == null) {
                cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
                wrappers = cachedWrapperClasses;
            }
            wrappers.add(clazz);
        } else {
		    //clazz是一个普通的拓展类
			//获得默认构造
            clazz.getConstructor();
            if (name == null || name.length() == 0) {
			    //name 配置为空,去类上找@Extension注解
                name = findAnnotationName(clazz);
                if (name.length() == 0) {
                    throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL);
                }
            }
            String[] names = NAME_SEPARATOR.split(name);
            if (names != null && names.length > 0) {
			    //判断clazz是否为标注了@Activate注解
                Activate activate = clazz.getAnnotation(Activate.class);
                if (activate != null) {
                    cachedActivates.put(names[0], activate);
                }
                for (String n : names) {
                    if (!cachedNames.containsKey(clazz)) {
                        cachedNames.put(clazz, n);
                    }
                    Class<?> c = extensionClasses.get(n);
                    if (c == null) {
					    // 存储名称到class的映射关系,这样就解析好了一行
                        extensionClasses.put(n, clazz);
                    } else if (c != clazz) {
                        throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName());
                    }
                }
            }
        }
    }

查看处理@Extension注解,findAnnotationName方法:

    private String findAnnotationName(Class<?> clazz) {
        com.alibaba.dubbo.common.Extension extension = clazz.getAnnotation(com.alibaba.dubbo.common.Extension.class);
		//没注解,再去判断
        if (extension == null) {
            String name = clazz.getSimpleName();
			//扩展类名是否以接口的名称结尾,包含的话,就截取扩展类前面的部分,不包含的话就直接扩展类名
            if (name.endsWith(type.getSimpleName())) {
                name = name.substring(0, name.length() - type.getSimpleName().length());
            }
			//转成小写
            return name.toLowerCase();
        }
		//有注解,返回注解value
        return extension.value();
    }

源码分析流程图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9kAkVvXp-1646269344916)(831052A92835495B98589C93511A2646)]

举报

相关推荐

0 条评论