各位游戏大佬大家好,今天小编为大家分享关于gg修改器框架root_gg修改器框架下载安装的内容,轻松修改游戏数据,赶快来一起来看看吧。
学习路线图:
服务发现(Service Provider Interface,SPI)是一个服务的注册与发现机制,通过解耦服务提供者与服务使用者,实现了服务创建 & 服务使用的关注点分离。 服务提供模式可以为我们带来以下好处:
服务发现示意图
服务发现和依赖注入都是控制反转 Ioc 的实现形式之一。 IoC 可以认为是一种设计模式,但是由于理论成熟的时间相对较晚,所以没有包含在《设计模式 · GoF》之中,即: 当依赖方需要使用依赖项时,不再直接构造对象,而是由外部 IoC 容器来创建并提供依赖。
在分析 ServiceLoader 的使用原理之前,我们先来介绍下 ServiceLoader 的使用步骤。
我们直接以 JDBC 作为例子,其中「2、连接数据库」内部就是用了 ServiceLoader。为什么连接数据库需要使用 SPI 设计思想呢?因为操作数据库需要使用厂商提供的数据库驱动程序,如果直接使用厂商的驱动耦合太强了,而使用 SPI 设计就能够实现服务提供者与服务使用者解耦。
以下为使用步骤,具体分为 5 个步骤:
Class.forName("com.mysql.jdbc.driver")
DriverManager.getConnection(url, user, password)
Connection#.creatstatement();
Statement#executeQuery()
ResultSet#close()
Statement#close()
Connection#close()
下面,我们一步步手写 JDBC 中关于 ServiceLoader 的相关源码:
定义一个驱动接口,这个接口将由数据库驱动实现类实现。在服务发现框架中,这个接口就是服务接口。
public interface Driver {
// 创建数据库连接
Connection connect(String url, java.util.Properties info);
...
}
数据库厂商提供一个或多个实现 Driver 接口的驱动实现类,以 mysql 和 oracle 为例:
// 已简化
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
static {
// 注册驱动
java.sql.DriverManager.registerDriver(new Driver());
}
...
}
// 已简化
public class OracleDriver implements Driver {
private static OracleDriver defaultDriver = null;
static {
if (defaultDriver == null) {
// 1、单例
defaultDriver = new OracleDriver();
// 注册驱动
DriverManager.registerDriver(defaultDriver);
}
}
...
}
在工程目录 java 的同级目录中新建目录 resources/META-INF/services ,新建一个配置文件 java.sql.Driver(文件名为服务接口的全限定名),文件中每一行是实现类的全限定名,例如:
<.mysql.cj.jdbc.Driver
我们可以解压 mysql-connector-java-8.0.19.jar 包,找到对应的 META-INF 文件夹。
DriverManaer.java
// 已简化
static {
loadInitialDrivers();
}
// 入口
private static void loadInitialDrivers() {
...
// 读取 "jdbc.drivers" 属性
String drivers = System.getProperty("jdbc.drivers");
// 1、使用 ServiceLoader 遍历 Driver 服务接口的实现类
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
// 2、获得迭代器
Iterator<Driver> driversIterator = loadedDrivers.iterator();
// 3、迭代(ServiceLoader 内部会通过反射)
while(driversIterator.hasNext()) {
driversIterator.next();
}
return null;
...
}
可以看到,DriverManager 被类加载时(static{})会调用 loadInitialDrivers() 。这个方法内部通过 ServiceLoader 提供的迭代器 Iterator 遍历了所有驱动实现类。那么,ServiceLoader 是如何实例化 Driver 接口的实现类的呢?下一节,我们会深入 ServiceLoader 的源码来解答这个问题。
ServiceLoader 提供了三个静态泛型工厂方法,内部最终将调用 ServiceLoader.load(Class, ClassLoader),其中第一个参数就是服务接口的 Class 对象。
ServiceLoader.java
// 方法 1:
public static <S> ServiceLoader<S> loadInstalled(Class<S> service) {
// 使用 SystemClassLoader 类加载器
ClassLoader cl = ClassLoader.getSystemClassLoader();
ClassLoader prev = null;
while (cl != null) {
prev = cl;
cl = cl.getParent();
}
return ServiceLoader.load(service, prev);
}
// 方法 2:
public static <S> ServiceLoader<S> load(Class<S> service) {
// 使用线程上下文类加载器
ClassLoader cl = Thread.currentThread().getContextClassLoader();
return ServiceLoader.load(service, cl);
}
// 方法 3(最终走到这个方法):
public static <S> ServiceLoader<S> load(Class<S> service, ClassLoader loader){
return new ServiceLoader<>(service, loader);
}
可以看到,三个方法仅在传入的类加载器不同,最终只是返回了一个面向服务接口 S 的 ServiceLoader 对象。我们先看一下构造器里做了什么工作。
ServiceLoader.java
// 已简化
private final Class<S> service;
// 服务实现缓存
private LinkedHashMap<String,S> providers = new LinkedHashMap<>();
private ServiceLoader(Class<S> svc, ClassLoader cl) {
// 1、类加载器
loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
// 2、清空 providers
providers.clear();
// 3、实例化 LazyIterator
lookupIterator = new LazyIterator(service, loader);
}
可以看到,ServiceLoader 的构造器中主要就是实例化了一个 LazyIterator 迭代器的实例,这是一个「懒加载」的迭代器。这个迭代器里做了什么呢?我们继续往下看
ServiceLoader.java
// -> 3、实例化 LazyIterator
// 前文提到的配置文件路径
private static final String PREFIX = "META-INF/services/";
private class LazyIterator implements Iterator<S> {
// 服务接口 Class 对象
Class<S> service;
ClassLoader loader;
Enumeration<URL> configs = null;
// pending、nextName:用于解析配置文件中的服务实现类名
Iterator<String> pending = null;
String nextName = null;
private LazyIterator(Class<S> service, ClassLoader loader) {
this.service = service;
this.loader = loader;
}
// 3.1 判断是否有下一个服务实现
@Override
public boolean hasNext() {
return hasNextService();
}
// 3.2 返回下一个服务实现
@Override
public S next() {
return nextService();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
// -> 3.1 判断是否有下一个服务实现
private boolean hasNextService() {
if (nextName != null) {
return true;
}
if (configs == null) {
// 3.1.1 拼接配置文件路径:META-INF/services/服务接口的全限定名
String fullName = PREFIX + service.getName();
if (loader == null)
configs = ClassLoader.getSystemResources(fullName);
else
configs = loader.getResources(fullName);
}
// 3.1.2 parse:解析配置文件资源的迭代器
while ((pending == null) || !pending.hasNext()) {
if (!configs.hasMoreElements()) {
return false;
}
pending = parse(service, configs.nextElement());
}
// 3.1.3 下一个实现类的全限定名
nextName = pending.next();
return true;
}
// 3.2 返回下一个服务实现
private S nextService() {
if (!hasNextService()) throw new NoSuchElementException();
= nextName;
nextName = null;
// 3.2.1 使用类加载器 loader 加载
Class<?> c = Class., false /* 不执行初始化 */, loader);
if (!service.isAssignableFrom(c)) {
// 检查是否实现 S 接口
ClassCastException cce = new ClassCastException(service.getCanonicalName() + " is not assignable from " + c.getCanonicalName());
fail(service, "Provider " + cn + " not a subtype", cce);
}
// 3.2.2 使用反射创建服务类实例
S p = service.cast(c.newInstance());
// 3.2.3 服务实现类缓存到 providers
providers., p);
return p;
}
}
// -> 3.1.2 parse:解析配置文件资源的迭代器
private Iterator<String> parse(Class<?> service, URL u) throws ServiceConfigurationError {
// 使用 UTF-8 编码输入配置文件资源
InputStream in = u.openStream();
BufferedReader r = new BufferedReader(new InputStreamReader(in, "utf-8"));
ArrayList<String> names = new ArrayList<>();
int lc = 1;
while ((lc = parseLine(service, u, r, lc, names)) >= 0);
return names.iterator();
}
以上代码已经非常简化了,LazyIterator 的要点如下:
小结一下: LazyInterator 会解析「META-INF/services/服务接口的全限定名」配置,遍历每个服务实现类全限定类名,执行类加载(未初始化),最后将服务实现类缓存到 providers。
那么,这个迭代器在哪里使用的呢?继续往下看~
其实 ServiceLoader 本身就是实现 Iterable 接口的:
ServiceLoader.java
public final class ServiceLoader<S> implements Iterable<S>
让我们来看看 ServiceLoader 中的 Iterable#iterator() 是如何实现的:
private LazyIterator lookupIterator;
// 4、返回一个新的迭代器,包装了 providers 和 lookupIterator
public Iterator<S> iterator() {
return new Iterator<S>() {
// providers 就是上一节 next() 中缓存的服务实现
Iterator<Map.Entry<String,S>> knownProviders = providers.entrySet().iterator();
@Override
public boolean hasNext() {
// 4.1 优先从 knownProviders 取,再从 LazyIterator 取
if (knownProviders.hasNext()) return true;
return lookupIterator.hasNext();
}
@Override
public S next() {
// 4.2 优先从 knownProviders 取,再从 LazyIterator 取
if (knownProviders.hasNext()) return knownProviders.next().getValue();
return lookupIterator.next();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
可以看到,ServiceLoader 里有一个泛型方法 Iterator<S> iterator(),它包装了 providers 集合迭代器和 lookupIterator 两个迭代器。对于已经 “发现” 的服务实现类会被缓存到 providers 集合中,包装类的作用就是优先读取缓存而已。
理解 ServiceLoader 源码之后,我们总结要点如下:
1、服务实现类必须实现服务接口 S( if (!service.isAssignableFrom(c)) ); 2、服务实现类需包含无参的构造器,LazyInterator 是反射创建实现类市里的( S p = service.cast(c.newInstance()) ); 3、配置文件需要使用 UTF-8 编码( new BufferedReader(new InputStreamReader(in, “utf-8”)) )。
ServiceLoader 使用「懒加载」的方式创建服务实现类实例,只有在迭代器推进的时候才会创建实例( nextService() )。
ServiceLoader 使用 LinkedHashMap 缓存创建的服务实现类实例。
提示: LinkedHashMap 在迭代时会按照 Map#put 执行顺序遍历。
服务实现类实例被创建后,它的垃圾回收的行为与 Java 中的其他对象一样,只有这个对象没有到 GC Root 的强引用,才能作为垃圾回收。而 ServiceLoader 内部只有一个方法来完全清除 provices 内存缓存。
public void reload() {
providers.clear();
lookupIterator = new LazyIterator(service, loader);
}
当存在多个提供者时,ServiceLoader 没有提供筛选机制,使用方只能在遍历整个迭代器中的所有实现,从发现的实现类中决策出一个最佳实现。举个例子,我们可以使用字符集的表示符号来获得一个对应的 Charset 对象:Charset.forName(String),这个方法里面就只会选择匹配的 Charaset 对象。
CharsetProvider.java
服务接口
public abstract class CharsetProvider {
public abstract Charset charsetForName(String charsetName);
// 省略其他方法...
}
Charset.java
public static Charset forName(String charsetName) {
// 以下只摘要与 ServiceLoader 有关的逻辑...
ServiceLoader<CharsetProvider> sl = ServiceLoader.load(CharsetProvider.class, cl);
Iterator<CharsetProvider> i = sl.iterator();
for (Iterator<CharsetProvider> i = providers(); i.hasNext();) {
CharsetProvider cp = i.next();
// 满足匹配条件,return
Charset cs = cp.charsetForName(charsetName);
if (cs != null)
return cs;
}
}
以上就是关于gg修改器框架root_gg修改器框架下载安装的全部内容,游戏大佬们学会了吗?
手机未root怎么用gg修改器_手机无root怎么用GG修改器 分类:免root版 6,606人在玩 各位游戏大佬大家好,今天小编为大家分享关于手机未root怎么用gg修改器_手机无root怎么用GG修改器的内容,轻松修改游戏数据,赶快来一起来看看吧。 相信大家都有这个体验,安卓机……
下载gg修改器安卓免root版,下载安卓免root版gg修改器 助力游戏成就 分类:免root版 3,029人在玩 如果你是一个游戏爱好者,一定会遇到各种各样的游戏难题。有时候你可能无法通过关卡或者击败某个BOSS,而这些问题需要你不断地尝试才能解决。但是现在有一款神奇的软件可以帮助你解……
下载gg修改器的免root版本,软件下载:免root版本的gg修改器 分类:免root版 3,474人在玩 gg修改器是一款非常实用的游戏辅助工具,可以帮助玩家修改游戏内部数据,从而获得更好的游戏体验。但是,传统的gg修改器需要root权限才能运行,给一些没有root权限的玩家带来了很多……
下载gg修改器免root版老版本_旧版gg修改器免root 分类:免root版 6,931人在玩 各位游戏大佬大家好,今天小编为大家分享关于gg修改器免root版老版本_旧版gg修改器免root的内容,轻松修改游戏数据,赶快来一起来看看吧。 开发者模式或者说开发者选项,一直存在于……
下载gg修改器怎么购买root,下载GG修改器,轻松获取Root权限 分类:免root版 1,443人在玩 如果您想要在手机上享受更多的自由和权限,那么获取Root权限是必不可少的。而GG修改器是一款非常好用的工具,可以帮助您轻松获取手机的Root权限。 什么是GG修改器? GG修改器是一……
下载gg修改器开不了root,下载gg修改器,轻松开启root权限 分类:免root版 3,853人在玩 对于许多Android用户来说,获得root权限是一件非常重要的事情。有了root权限,用户可以自由地控制设备、安装各种应用程序和定制操作系统。然而,许多人发现他们无法通过传统的方法……
下载gg修改器是否有免root 分类:免root版 4,483人在玩 各位游戏大佬大家好,今天小编为大家分享关于gg修改器是否有免root的内容,轻松修改游戏数据,赶快来一起来看看吧。 作者:刻薄哥 众所周知,小米在近期的部分机型上加了bootloade……
下载gg修改器棉root,下载最新版GG修改器棉ROOT,轻松实现游戏外挂! 分类:免root版 1,769人在玩 在玩一些竞技类手游时,我们往往希望获得更好的游戏体验和更强的战斗力。而很多玩家会选择使用外挂来达成这个目的,其中最为常用的就是GG修改器棉ROOT。 GG修改器棉ROOT是一款非常……
下载gg免root修改器怎么用,下载 gg免root修改器,轻松体验游戏乐趣 分类:免root版 2,951人在玩 如果你是一位热爱游戏的玩家,那么你必须知道 gg免root修改器。这款软件能够让你在不进行手机root操作的情况下,修改游戏内部数据。这意味着你可以轻松获得更多金币、钻石、经验和……
下载gg修改器免root插件,软件下载: GG修改器免root插件 分类:免root版 4,363人在玩 GG修改器免root插件是一款非常实用的软件,可以让用户在不进行手机ROOT的情况下也能够享受到修改游戏的乐趣。这个软件不仅使用方便,而且功能强大,已经被广大玩家所喜爱。 简单易……
下载