文章目录
- Pre
- 概述
- 插件
- 插件扩展机制(Spi)
- 插件扩展机制概述
- 插件扩展机制的优势
- 插件扩展机制实现步骤
- 第一步:定制插件实现类
- 示例代码:插件实现类
- 第二步:通过插件配置文件声明插件
- 示例插件配置文件:`META-INF/solon/solon.data.properties`
- 第三步:扫描并发现插件
- 插件的排除
- 示例:排除插件
- 插件包的命名规范
- 示例:Solon Data 插件
- 插件实现类:`XPluginImp.java`
- 插件配置文件:`META-INF/solon/solon.data.properties`
- 插件应用示例
- 插件引入
- 小结
- 插件体外扩展机制(E-Spi)
- E-Spi 插件体外扩展机制概述
- E-Spi 机制特点
- 1. 共享资源
- 2. 灵活的打包方式
- 3. 更新机制
- 4. 内核支持
- E-Spi 插件扩展操作说明
- 配置扩展目录
- 示例配置:手动指定扩展目录
- 示例配置:自动创建扩展目录
- 插件包和配置文件加载
- 1. 数据源配置文件
- 2. 插件包
- 插件包的打包注意事项
- 1. 打成 FatJar
- 2. 与主应用共享依赖
- 示例演示:E-Spi 扩展机制使用示例
- 目录结构
- 配置文件示例:`_db.properties`
- 插件包示例:`demo_user.jar`
- 插件包示例:`demo_order.jar`
- 主应用代码示例
- 小结
- 插件热插拔管理机制(H-Spi)
- H-Spi 插件热插拔管理机制概述
- 1. 特点说明
- 1.1 完全隔离
- 1.2 动态热插拔
- 1.3 资源独立管理
- 1.4 事件总线交互
- 2. 关于 ClassLoader 隔离
- 2.1 父子级 ClassLoader
- 2.2 同级 ClassLoader
- 2.3 插件之间的独立性
- 3. 插件开发注意事项
- 3.1 插件资源的管理
- 3.2 资源清理
- 3.3 模板渲染的 ClassLoader 问题
- 4. 插件管理
- 小结
- Code
Pre
插件 - 通过SPI方式实现插件管理
插件 - 一份配置,离插件机制只有一步之遥
插件 - 插件机制触手可及
Plugin - 插件开发01_SPI的基本使用
Plugin - 插件开发02_使用反射机制和自定义配置实现插件化开发
Plugin - 插件开发03_Spring Boot动态插件化与热加载
Plugin - 插件开发04_Spring Boot中的SPI机制与Spring Factories实现
概述
https://solon.noear.org/article/441
本系列在内核知识的基础上做进一步延申。主要涉及:
- 插件
- 插件扩展体系(Spi)
- 插件体外扩展体系(E-Spi)
- 插件热插拔管理机制(H-Spi)
这些知识,为构建大的项目架构会有重要帮助。
本系列演示可参考:
https://gitee.com/noear/solon-examples/tree/main/2.Solon_Advanced
插件
Solon Plugin 是框架的核心接口,简称“插件”。其本质是一个参与应用“生命周期”的接口。它可以代表一个模块参与应用的生命周期过程
public interface Plugin {//启动void start(AppContext context) throws Throwable;//预停止default void prestop() throws Throwable{}//停止default void stop() throws Throwable{}
}
插件扩展机制(Spi)
Solon框架实现的插件扩展机制,基于“插件”+“配置声明”的方式,提供了一种轻量、解耦、灵活的扩展方式,类似于Spring的Factories和Java的SPI(Service Provider Interface)。本文将详细介绍Solon插件扩展机制的实现及使用。
插件扩展机制概述
Solon的插件扩展机制简化了模块化的开发和插件的管理,允许开发者将可复用的功能封装成插件,并通过配置文件进行管理和加载。插件的核心作用是在应用启动时提供初始化和生命周期管理的功能,使得模块化的能力可以灵活地集成到系统中。
插件的类型非常多样,例如像@Tran
、@Cache
等注解功能,通常希望在多个项目中复用,因此它们的实现会被封装为插件。
插件扩展机制的优势
- 解耦:插件的引入和管理通过配置声明,使得核心业务与插件逻辑解耦。
- 灵活:插件可以根据需求进行启用、配置和排除。
- 扩展性强:用户可以方便地通过插件实现功能扩展,并可以根据优先级排序加载插件。
插件扩展机制实现步骤
第一步:定制插件实现类
插件实现类负责处理插件的生命周期,通常包括启动、停止、预停止等方法。在Solon中,插件实现类需要实现Plugin
接口,并重写start
、prestop
和stop
等方法。
示例代码:插件实现类
public class XPluginImpl implements Plugin {@Overridepublic void start(AppContext context) {// 插件启动时,进行初始化工作}@Overridepublic void prestop() throws Throwable {// 插件预停止时的操作// 注意:预停止后,插件将等待几秒后才会停止,适用于需要安全停止的插件}@Overridepublic void stop() {// 插件停止时的清理工作}
}
第二步:通过插件配置文件声明插件
插件需要通过配置文件来声明自己,配置文件通常放置在META-INF/solon/
目录下,并且文件名需保证全局唯一,通常使用包名作为文件名,以避免冲突。
示例插件配置文件:META-INF/solon/solon.data.properties
# 插件实现类配置
solon.plugin=org.noear.solon.data.integration.XPluginImp
# 插件优先级配置,值越大优先级越高,默认为0
solon.plugin.priority=1
第三步:扫描并发现插件
在应用启动时,Solon框架会扫描META-INF/solon/
目录下的所有配置文件,加载并按照配置文件中的优先级排序插件。
插件的排除
有时,在项目中可能引入了多个插件包,但我们希望排除某些插件。Solon提供了solon.plugin.exclude
配置项来排除不需要的插件。
示例:排除插件
solon.plugin.exclude:- "org.noear.solon.data.integration.XPluginImp"
插件包的命名规范
Solon框架对插件包有一定的命名规范,以便于区分不同类型的插件:
插件命名规则 | 说明 |
---|---|
solon-* | 表示内部架构插件 |
*-solon-plugin | 表示外部适配插件 |
*-solon-cloud-plugin | 表示云接口外部适配插件 |
示例:Solon Data 插件
Solon Data插件提供了事务管理的能力,使用@Tran
注解可以自动管理事务。下面我们以Solon Data插件为例,演示如何实现一个插件。
插件实现类:XPluginImp.java
public class XPluginImp implements Plugin {@Overridepublic void start(AppContext context) {// 如果启用事务管理,则添加事务拦截器if (Solon.app().enableTransaction()) {context.beanInterceptorAdd(Tran.class, new TranInterceptor(), 120);}}
}
插件配置文件:META-INF/solon/solon.data.properties
# 配置插件实现类
solon.plugin=org.noear.solon.data.integration.XPluginImp
# 配置插件优先级
solon.plugin.priority=3
插件应用示例
在应用中,我们可以通过@Tran
注解来使用插件提供的事务管理能力。
@Component
public class AppService {@InjectAppMapper appMapper;@Tranpublic void addApp(App app) {// 事务管理:如果方法执行失败,将回滚事务appMapper.add(app);}
}
插件引入
在pom.xml
中引入solon-data
插件依赖:
<dependency><groupId>org.noear</groupId><artifactId>solon-data</artifactId><version>最新版本</version>
</dependency>
小结
Solon框架的插件扩展机制为我们提供了一种非常灵活、解耦且可扩展的方式来实现应用的功能扩展。通过插件机制,我们可以将通用的功能封装成插件,并且在应用中按需引入。插件的生命周期由Solon自动管理,而通过配置文件,我们可以灵活地管理插件的优先级、排除插件等。
插件体外扩展机制(E-Spi)
很多时候我们需要解决如何在 fatjar 模式部署时实现插件化扩展的问题。Solon框架提供了一个名为 E-Spi 的插件体外扩展机制,旨在解决将业务模块、配置文件等放在应用外部的问题。通过 E-Spi,我们能够将插件和配置与主程序解耦,从而使得后期的插件更新、配置修改等更加灵活。
E-Spi 插件体外扩展机制概述
E-Spi 机制的设计主要是为了应对以下场景:
- 将某些业务模块(如用户模块、订单模块等)打包成插件包放在外部。
- 将配置信息(如数据源配置、缓存配置等)单独放在外部,便于后期修改。
E-Spi 使得插件和配置文件可以在应用外部部署,且支持后续的灵活修改和热更新。这种机制具有高效的插件管理功能,并且能够与现有的 fatjar 部署模式无缝集成。
E-Spi 机制特点
1. 共享资源
所有插件包共享同一个 ClassLoader
、AppContext
和配置。也就是说,所有的插件都能访问相同的资源,保证插件之间的协作和互操作。
2. 灵活的打包方式
- 体外插件包:插件可以作为独立的包(如
.jar
文件)放到体外,供主应用加载。 - 内嵌插件包:插件也可以与主程序一起打包成一个 fatjar,支持两种方式的灵活切换。
3. 更新机制
- 更新体外插件包或配置文件时,需要重启主服务才能生效。
- 不同于直接打包在一起的插件,体外插件需要借助指定的扩展目录来加载。
4. 内核支持
E-Spi 是由 Solon 内核直接提供支持的,不需要额外的依赖,极大简化了扩展机制的使用。
E-Spi 插件扩展操作说明
配置扩展目录
为了让 Solon 知道从哪里加载外部插件和配置文件,我们需要在应用的配置文件中指定扩展目录。该目录可以手动创建,也可以通过配置让 Solon 自动创建。
示例配置:手动指定扩展目录
solon.extend: "demo_ext"
此时,demo_ext
目录需要手动创建,并且 Solon 会从这个目录加载插件和配置文件。
示例配置:自动创建扩展目录
solon.extend: "!demo_ext"
当使用 !
前缀时,Solon 会在启动时自动创建该扩展目录。
插件包和配置文件加载
插件体外扩展机制支持将不同类型的文件放置在扩展目录中,并进行加载。
1. 数据源配置文件
假设我们有一个数据库配置文件 _db.properties
,我们可以将它放置在扩展目录中,后续可以方便地进行修改而无需重新打包应用。
demo.jar
demo_ext/_db.properties
2. 插件包
除了配置文件外,我们还可以将业务模块做成插件包(如 .jar
文件),并将其放到扩展目录中。Solon会自动扫描并加载这些插件。
demo.jar
demo_ext/_db.properties
demo_ext/demo_user.jar
demo_ext/demo_order.jar
插件包的打包注意事项
插件包的管理是 E-Spi 机制中的关键部分。插件包可以选择以下两种打包方式:
1. 打成 FatJar
将插件包打成 fatjar(使用 maven-assembly-plugin
插件)是一种常见的方式。Fatjar 是包含所有依赖的单一 JAR 文件,这种方式方便部署,但文件较大。
2. 与主应用共享依赖
更推荐的方式是将插件包的公共依赖放入主应用中,而在插件包的 pom.xml
中将这些依赖标记为可选。这样可以避免重复依赖,提高应用的整体性能。
示例演示:E-Spi 扩展机制使用示例
目录结构
假设我们有如下的目录结构:
demo.jar
demo_ext/├── _db.properties├── demo_user.jar└── demo_order.jar
其中:
demo.jar
是主应用的 JAR 包。demo_ext/
目录包含了外部扩展的插件包和配置文件。
配置文件示例:_db.properties
# 数据源配置文件
db.url=jdbc:mysql://localhost:3306/demo
db.username=root
db.password=secret
插件包示例:demo_user.jar
demo_user.jar
可能包含了用户模块的相关功能,实现了业务逻辑或数据处理。
插件包示例:demo_order.jar
demo_order.jar
可能包含了订单模块的相关功能,同样是一个独立的插件包。
主应用代码示例
在主应用中,我们通过 @Inject
注解加载外部插件,并在应用中使用它们的功能。
@Component
public class AppService {@InjectAppMapper appMapper;@Tranpublic void addApp(App app) {appMapper.add(app);}
}
小结
Solon的插件体外扩展机制(E-Spi)为我们提供了一种非常灵活的方式来管理插件和配置文件。通过将插件和配置文件体外化,我们可以实现以下目标:
- 模块化:不同的业务模块可以作为独立的插件进行管理和部署。
- 灵活性:配置文件可以单独存放在外部,便于修改和热更新。
- 简化依赖管理:公共依赖可以由主应用来统一管理,避免重复。
E-Spi 机制不仅能支持传统的 fatjar 模式,也能够满足更高效的插件管理需求,是构建高可扩展性微服务系统的理想选择。
插件热插拔管理机制(H-Spi)
插件化和模块化管理已经成为了开发的重要方向。Solon框架提供了一个名为 H-Spi 的插件热插拔管理机制,它专注于提供隔离性、热插拔性和管理性。与 E-Spi 机制不同,H-Spi 更强调在生产环境下的灵活性,尤其是在支持插件热更新和模块隔离方面的优势。
H-Spi 插件热插拔管理机制概述
H-Spi 是一种为生产环境设计的扩展方案。它的核心目标是通过热插拔机制管理插件,并确保插件间的隔离性。通过 H-Spi,应用能够在运行时动态加载、卸载插件,而无需重启主服务。每个插件在运行时都拥有独立的资源和上下文,避免了插件间的相互干扰。
1. 特点说明
1.1 完全隔离
H-Spi 机制下,每个插件包都会拥有独立的 ClassLoader
、AppContext 和配置。插件之间的资源和环境完全隔离,可以避免不同插件之间的冲突或干扰。
- ClassLoader 隔离:插件包独享自己的 ClassLoader,确保插件间不会互相访问或修改其他插件的资源。
- AppContext 隔离:每个插件可以访问自己的上下文,但也可以通过 Solon.app()、Solon.cfg() 和 Solon.context() 等方式手动获取主程序或全局资源。
1.2 动态热插拔
H-Spi 机制支持热插拔,即可以在不重启主服务的情况下进行插件的动态加载和卸载。这大大提高了应用的灵活性,特别是在生产环境下进行插件更新时无需影响整个系统的稳定性。
1.3 资源独立管理
在插件开发过程中,开发者需要保证插件资源的独立性。如果插件启动时向系统添加了某些资源或对象,则在插件停止时必须移除这些资源,以支持热更新。
1.4 事件总线交互
为了保证插件之间的松耦合,插件之间的交互尽量通过事件总线(EventBus)进行。事件数据通常采用弱类型(如 Map 或 JSON 字符串)形式传递,以减少插件之间的依赖关系。推荐与 DamiBus 等工具结合使用,进一步解耦。
2. 关于 ClassLoader 隔离
H-Spi 的 ClassLoader 隔离 是其关键特性之一。在这种隔离下,插件只能访问自己的资源和类,无法直接访问其他插件的资源。这意味着,插件之间的交互需要更加小心,并避免直接通过类进行互相访问。
2.1 父子级 ClassLoader
- 父级 ClassLoader:通常用于存放公共资源,子级插件可以访问父级 ClassLoader 中的资源。
- 子级 ClassLoader:插件会使用自己的 ClassLoader,它无法直接访问同级插件的类或资源。
2.2 同级 ClassLoader
- 同级插件之间无法直接交互。如果需要交互,建议通过事件总线进行,避免显式的类交互。
- 交互的数据:可以使用父级 ClassLoader 的实体类,或者使用弱类型的数据,如 JSON 字符串等。
2.3 插件之间的独立性
建议尽量减少插件之间的交互,使每个插件都能独立运行。如果确实需要交互,最好通过事件总线(EventBus)来进行,且使用弱类型数据格式(如 Map 或 JSON)。
3. 插件开发注意事项
在 H-Spi 模式下,插件开发需要特别注意资源的添加与移除。特别是在插件停止时,必须清理所有为插件注册的资源,确保插件的热插拔能力得以保持。
3.1 插件资源的管理
例如,当插件启动时,可能会向系统添加配置文件、扫描 Bean 或注册静态文件等操作。这些资源在插件停止时需要被移除。
public class Plugin1Impl implements Plugin {private AppContext context;private StaticRepository staticRepository;@Overridepublic void start(AppContext context) {this.context = context;// 添加配置文件context.cfg().loadAdd("demo1011.plugin1.yml");// 扫描插件的 Beancontext.beanScan(Plugin1Impl.class);// 注册静态文件仓库staticRepository = new ClassPathStaticRepository(context.getClassLoader(), "plugin1_static");StaticMappings.add("/html/", staticRepository);}@Overridepublic void stop() throws Throwable {// 移除 HTTP 路由Solon.app().router().remove("/user");// 移除定时任务(如果有)JobManager.remove("job1");// 移除事件订阅context.beanForeach(bw -> {if (bw.raw() instanceof EventListener) {EventBus.unsubscribe(bw.raw());}});// 移除静态文件仓库StaticMappings.remove(staticRepository);}
}
3.2 资源清理
在插件停止时,务必执行清理工作,确保不会留下不必要的资源。这是 H-Spi 热插拔机制的关键。
3.3 模板渲染的 ClassLoader 问题
当涉及到模板渲染时,可能会遇到 ClassLoader 的问题。例如,如果使用 Freemarker 渲染模板时,必须确保模板加载的 ClassLoader
是正确的:
public class BaseController implements Render {// 要考虑模板所在的 classloaderstatic final FreemarkerRender viewRender = new FreemarkerRender(BaseController.class.getClassLoader());@Overridepublic void render(Object data, Context ctx) throws Throwable {if (data instanceof Throwable) {throw (Throwable) data;}if (data instanceof ModelAndView) {viewRender.render(data, ctx);} else {ctx.render(data);}}
}
4. 插件管理
在 H-Spi 中,插件不只是通过独立 ClassLoader 进行管理,还可以通过 solon-hotplug
插件来实现插件的仓库化和平台化。这样,插件可以通过中央仓库进行管理,并在平台上动态更新、卸载,进一步提升应用的灵活性和可扩展性。
通过插件管理工具,平台可以更好地控制插件的生命周期,支持对插件的版本管理、更新和回滚。
小结
H-Spi 插件热插拔管理机制是为生产环境设计的高效扩展方案,具有以下几个核心特点:
- 插件隔离性:每个插件包独享 ClassLoader、AppContext 和配置,避免了插件间的冲突。
- 热插拔:支持插件的动态加载和卸载,无需重启主服务,提升了系统的灵活性。
- 资源清理:插件在停止时必须移除自己注册的所有资源,以支持热更新。
- 事件总线交互:插件之间通过事件总线进行松耦合的交互,减少了插件之间的依赖。
H-Spi 提供了一个非常灵活的插件化架构,适用于需要高可扩展性和模块化管理的生产环境。通过 H-Spi,可以实现插件的热插拔和灵活更新,极大地提高了系统的可维护性和扩展能力。
Code
import org.noear.solon.Utils;
import org.noear.solon.core.PluginEntity;import java.net.URL;
import java.util.Collection;
import java.util.Properties;
import java.util.function.Consumer;/*** 插件工具** @author noear* @since 1.7*/
public class PluginUtil {/*** 扫描插件** @param classLoader 类加载器* @param excludeList 排除列表*/public static void scanPlugins(ClassLoader classLoader, Collection<String> excludeList, Consumer<PluginEntity> consumer) {//3.查找插件配置(如果出错,让它抛出异常)Collection<String> nameList = ScanUtil.scan(classLoader, "META-INF/solon", n -> n.endsWith(".properties"));for (String name : nameList) {URL res = ResourceUtil.getResource(classLoader, name);if (res == null) {// native 时,扫描出来的resource可能是不存在的(这种情况就是bug),需要给于用户提示,反馈给社区LogUtil.global().warn("Solon plugin: name=" + name + ", resource is null");} else {Properties props = Utils.loadProperties(res);findPlugins(classLoader, props, excludeList, consumer);}}}/*** 查找插件*/public static void findPlugins(ClassLoader classLoader, Properties props, Collection<String> excludeList,Consumer<PluginEntity> consumer) {String pluginStr = props.getProperty("solon.plugin");if (Utils.isNotEmpty(pluginStr)) {String priorityStr = props.getProperty("solon.plugin.priority");int priority = 0;if (Utils.isNotEmpty(priorityStr)) {priority = Integer.parseInt(priorityStr);}String[] plugins = pluginStr.trim().split(",");for (String clzName : plugins) {if (clzName.length() > 0) {if(excludeList.contains(clzName)) {continue;}PluginEntity ent = new PluginEntity(classLoader, clzName.trim(), props);ent.setPriority(priority);consumer.accept(ent);}}}}
}
import org.noear.solon.core.AppClassLoader;
import org.noear.solon.core.exception.ConstructionException;
import org.noear.solon.core.wrap.ClassWrap;import java.lang.reflect.*;
import java.util.Collection;
import java.util.Properties;/*** 类操作工具** @author noear* @since 2.2*/
public class ClassUtil {/*** 是否存在某个类** <pre><code>* if(ClassUtil.hasClass(()->DemoTestClass.class)){* ...* }* </code></pre>** @param test 检测函数*/public static boolean hasClass(SupplierEx<Class<?>> test) {try {test.get();return true;} catch (ClassNotFoundException | NoClassDefFoundError e) {return false;} catch (Throwable e) {throw new IllegalStateException(e);}}/*** 根据字符串加载为一个类(如果类不存在返回 null)** @param className 类名称*/public static Class<?> loadClass(String className) {return loadClass(null, className);}/*** 根据字符串加载为一个类(如果类不存在返回 null)** @param classLoader 类加载器* @param className 类名称*/public static Class<?> loadClass(ClassLoader classLoader, String className) {try {if (classLoader == null) {return Class.forName(className);} else {return classLoader.loadClass(className);}} catch (ClassNotFoundException | NoClassDefFoundError e) {return null;}}/*** 尝试根据类名实例化一个对象(如果类不存在返回 null)** @param className 类名称*/public static <T> T tryInstance(String className) {return tryInstance(className, null);}/*** 尝试根据类名实例化一个对象(如果类不存在返回 null)** @param className 类名称* @param prop 属性*/public static <T> T tryInstance(String className, Properties prop) {return tryInstance(AppClassLoader.global(), className, prop);}/*** 尝试根据类名实例化一个对象(如果类不存在返回 null)** @param classLoader 类加载器* @param className 类名称*/public static <T> T tryInstance(ClassLoader classLoader, String className) {return tryInstance(classLoader, className, null);}/*** 尝试根据类名实例化一个对象(如果类不存在返回 null)** @param classLoader 类加载器* @param className 类名称* @param prop 属性*/public static <T> T tryInstance(ClassLoader classLoader, String className, Properties prop) {Class<?> clz = loadClass(classLoader, className);return tryInstance(clz, prop);}public static <T> T tryInstance(Class<?> clz, Properties prop) {if (clz == null) {return null;} else {try {return newInstance(clz, prop);} catch (Exception e) {throw new IllegalStateException(e);}}}/*** 根据类名实例化一个对象** @param clz 类*/public static <T> T newInstance(Class<?> clz) throws ConstructionException {return newInstance(clz, null);}/*** 根据类名实例化一个对象** @param clz 类* @param prop 属性*/public static <T> T newInstance(Class<?> clz, Properties prop) throws ConstructionException {try {if (prop == null) {return (T) clz.getDeclaredConstructor().newInstance();} else {return (T) clz.getConstructor(Properties.class).newInstance(prop);}} catch (Exception e) {throw new ConstructionException(e);}}/*** 根据类名和参数类型实例化一个对象** @param clz 类* @param types 构建参数类型* @param args 参数*/public static Object newInstance(Class<?> clz, Class<?>[] types, Object[] args) {try {Constructor<?> constructor = clz.getDeclaredConstructor(types);return constructor.newInstance(args);} catch (Exception e) {throw new ConstructionException(e);}}/*** 根据构造函数实例化一个对象** @param constructor 构造器* @param args 参数*/public static Object newInstance(Constructor constructor, Object[] args) {try {return constructor.newInstance(args);} catch (Exception e) {throw new ConstructionException(e);}}/private static final ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();/*** 分析类加载器*/public static ClassLoader resolveClassLoader(Type type) {ClassLoader loader = AppClassLoader.global();if (type != null) {Class<?> clz = getTypeClass(type);if (clz != Object.class) {ClassLoader cl = clz.getClassLoader();if (cl != systemClassLoader) {loader = cl;}}}return loader;}/*** 获取类*/public static Class<?> getTypeClass(Type type) {if (type instanceof Class) {return (Class<?>) type;} else if (type instanceof ParameterizedType) {//ParameterizedTypereturn getTypeClass(((ParameterizedType) type).getRawType());} else {//TypeVariablereturn Object.class;}}/*** 比较参数类型*/public static boolean equalParamTypes(Class<?>[] params1, Class<?>[] params2) {if (params1.length == params2.length) {for (int i = 0; i < params1.length; i++) {if (params1[i] != params2[i])return false;}return true;}return false;}/*** 查找 method*/public static Collection<Method> findPublicMethods(Class<?> clz) {return ClassWrap.get(clz).findPublicMethods();}
}