SPI概念介绍SPI是ServiceProviderInterface的缩写,泛指厂商实现并部署在应用程序ClassPath下的服务提供者接口。ServiceLoader类ServiceLoader是JDK6中提供的一种SPI实现方案。下面通过JDBC编程的步骤来分析ServiceLoader的内部机制。JDBC编程一般有五个步骤:1.执行数据库驱动类加载Class.forName("com.mysql.jdbc.driver")2.创建数据库连接DriverManager.getConnection(url,user,password)3.创建SQL语句Connection#创建语句();4.执行SQL语句,处理结果集Statement#executeQuery()5.释放资源ResultSet#close()Statement#close()Connection#close()上面第二步创建数据库连接使用ServiceLoader来实现加载数据库驱动类。相关开发步骤及源码分析如下:第一步,定义服务接口publicinterfaceDriver{Connectionconnect(Stringurl,java.util.Propertiesinfo)throwsSQLException;}第二步,实现服务接口databasevendorto提供一个或多个实现Driver接口的驱动实现类。下面以mysql为例:}static{try{DriverManager.registerDriver(newDriver());}catch(SQLExceptionvar1){thrownewRuntimeException("无法注册驱动程序!");}}}第三步,将实现类注册到配置文件中在项目目录java的同级目录下新建目录resources/META-INF/services,并在其下新建配置文件java.sql.Driverservices目录(文件名是服务接口的全限定名),文件中的每一行都是实现类的全限定名。第四步com.mysql.cj.jdbc.Driver,(user)loadingservice服务加载在DriverManager类中实现,进入DriverManager类,静态初始化block代码。静态{loadInitialDrivers();println("JDBCDriverManagerinitialized");}进入loadInitialDrivers()方法,内部使用了ServiceLoader。privatestaticvoidloadInitialDrivers(){字符串驱动程序;try{drivers=AccessController.doPrivileged(newPrivilegedActionServiceLoaderload(Classservice){ClassLoadercl=Thread.currentThread().getContextClassLoader();returnServiceLoader.load(service,cl);}继续进入ServiceLoader的重载load()方法,调用ServiceLoader的构造函数privateServiceLoader(Classsvc,ClassLoadercl){service=Objects.requireNonNull(svc,"Service接口不能为空");装载机=(cl==null)?类加载器.getSystemClassLoader():cl;acc=(System.getSecurityManager()!=null)?AccessController.getContext():空;reload();}在reload()方法中,首先清除providers,然后构造一个LazyIterator。LazyIterator是在ServiceLoader内部实现的惰性迭代器。这个迭代器实际上会在遍历的时候加载资源。publicvoidreload(){providers.clear();lookupIterator=newLazyIterator(service,loader);}下面看LazyIterator的hasNextService()方法实现privatebooleanhasNextService(){if(nextName!=null){returntrue;}if(configs==null){try{StringfullName=PREFIX+service.getName();if(loader==null)configs=ClassLoader.getSystemResources(fullName);否则configs=loader.getResources(fullName);}catch(IOExceptionx){fail(service,"错误定位配置文件",x);}}while((pending==null)||!pending.hasNext()){if(!configs.hasMoreElements()){returnfalse;}pending=parse(service,configs.nextElement());}nextName=pending.next();returntrue;}通过PREFIX+service.getName()构建出了fullName,PREFIX的定义如下:privatestaticfinalStringPREFIX="META-INf/services/";service.getName()返回的是java.sql.Driver,所以fullName的值为META-INF/services/java.sql.Driver,是配置文件的路径,里面配置了厂商的数据库驱动实现类,例如:com.mysql.cj.jdbc.Driver,nextName的值为com.mysql.cj.jdbc.Driver。驱动实现类的实际实例化在nextService()方法中privateSnextService(){if(!hasNextService())thrownewNoSuchElementException();Stringcn=nextName;下一个名字=空;类>c=null;try{c=Class.forName(cn,false,loader);}catch(ClassNotFoundExceptionx){fail(service,"Provider"+cn+"notfound");}if(!service.isAssignableFrom(c)){fail(service,"Provider"+cn+"不是子类型");}尝试{Sp=service.cast(c.newInstance());providers.put(cn,p);返回p;}catch(Throwablex){fail(service,"Provider"+cn+"无法实例化",x);抛出新的错误();//这不可能发生}这里是通过nextName(数据库驱动类的全限定名)获取Class对象,然后进行isAssignableFrom校验。验证通过后,通过反射机制实例化一个实例,并缓存在providers中。键是数据库驱动类的完全限定名,值是对应的实例化对象。providers的定义是一个LinkedHashMap。privateLinkedHashMap
