# mplug-project **Repository Path**: beyondts/mplug-project ## Basic Information - **Project Name**: mplug-project - **Description**: mplug是一个基于注解方法且适用于SpringBoot的插件框架。其作用是为了解决一个项目的逻辑在不同的现场可能存在不同处理的问题。 - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 6 - **Forks**: 1 - **Created**: 2019-01-11 - **Last Updated**: 2023-12-22 ## Categories & Tags **Categories**: spring-boot-ext **Tags**: None ## README # mplug-project #### 介绍 --- mplug是一个基于注解方法且适用于SpringBoot的插件框架。其作用是为了解决一个项目的逻辑在不同的现场可能存在不同处理的问题。本插件框架支持两种定义扩展点的方法: 1. 使用@Extension注解:在需要扩展的方法上增加@Extension注解,同时定义该扩展点的ID ,即可使该方法成为扩展点。在未插入任何对此扩展点的插件时,该方法当前逻辑为默认实现。当插入针对此扩展点的插件后,插件中的方法实现将替代该方法中的默认实现。该方式支持插件的插件,即插件包中也可以有使用@Extension注解的扩展点。 2. 通过extensions配置文件定义扩展点,此时扩展点的ID必须为扩展点方法的签名(Signature)字符串。此方式相对于第1 种方式的好处是,增加扩展点时不必修改原代码,扩展点完全动态增加。但被扫描的所有方法执行效率将有一定下降(需要判断是否指定的扩展点)。该方式下要支持插件的插件,必须在上层插件中定义ConfigurableExtensionHandler ,并且将其注册为Spring的一个实例(参考使用说明5)。 * 注1:mplug框架支持插件的热插拔,但卸载后的插件依然被自己的ClassLoader占用,只是该ClassLoader已被关闭。因此即使插件被热卸载后,插件的jar仍然不可被移动。 * 注2:mplug使用JDK1.8编写,未在其它版本上测试过。 * 注3:mplug使用SpringBoot2.1.0版本,未在其它SpringBoot版本上测试过。 * 注4:由于Spring-AOP的特性,所有要可成为扩展点的方法所在的类,必须由Spring管理,即在调用时需要使用Spring的方式调用,如通过Autowired自动装配,或使用BeanFactory.getBean获取类实例后调用。 ##### 插件: > 一个插件可以包含多个jar包,但是其中只允许有一个主jar包。主jar包下至少必须有一个插件类,并且该jar包的META-INF目录下必须有一个名为plugin.info的文件。如果一个类是插件类,那么至少需要包含一个使用@Plugin注解的方法。主jar包格式必须是name-version.jar。一个插件可以包含多个@Plugin注解,但这些方法必须共同提供一系列相关的功能。一个@Plugin注解和一个@Extension注解对应。一个插件类必须有一个不带任何参数的默认构造方法或者可以由Spring自动管理。 ##### 扩展点: > 扩展点为任意类中的某个方法,该方法的原本实现将成为该扩展点的默认实现。当框架检测到有针对该扩展点的插件时,会自动使用插件方法替代扩展点默认实现。 #### 软件架构 --- mplug-project工程下包含3个子模块: 1. mplug模块为插件框架的实现模块,在使用时只需要引入该模块的jar包即可。 2. extension-example模块为扩展点示例。 3. plugin-example模块为插件实现示例。 4. another-lib模块为扩展点示例(测试jar包中的扩展点)。 插件目录结构: > plugins `插件的根目录,使用系统变量plugin.dir指定。` >> config `插件框架使用的配置文件所在目录。` >>> extensions `通过方式2自动配置扩展点时,记录所有已配置的扩展点的文件。一行一个扩展点。` >>> plugins `记录系统中所有已安装的插件的文件。一行一个插件。` >> jars `放置所有插件jar包的目录` >>> 插件主jar包 `插件的主jar包` >>> lib `如果插件需要引用第三方jar,则将所有第三方jar放置在该目录下,该目录下的类中即使有@Plugin注解也不会被识别` #### 安装教程 --- 1. 下载mplug-project源代码,编译打包mplug模块得到mplug-version.jar 2. 将mplug-version.jar引入到主工程中 3. 创建一个通过系统变量plugins.dir指定的插件主目录,该目录下有两个子目录config和jars。其中config目录下存放插件的配置plugins文件和扩展点配置extensions文件(用于第2种方式的扩展点定义)。jars目录存放所有插件,每个插件需要放在以自己插件jar包名称命名的子目录下,如plugin-example-1.0.0.jar插件包,需要放在plugin-example-1.0.0子目录下。如果插件需要其它第三方jar包支持,需要将第三方的所有jar包放在该插件子目录的lib子目录下,如plugin-example-1.0.0.jar需要依赖commons-io.jar,则可以将commons-io.jar放在plugin-example-1.0.0/lib目录下。 4. 在plugins文件中定义插件,每个插件名一行。如使用plugin-example-1.0.0.jar插件包,则在该文件中使用一行写入plugin-example-1.0.0。只有在plugins文件中定义的插件才会被安装至主工程,未安装的插件就算放在jars目录中也不会在主工程中生效。 5. 如果使用第2种方式自动增加扩展点,则在extensions文件中每行定义一个扩展点id(即扩展点方法的签名)。 6. 开发插件代码时,每个扩展点的对应实现方法只需要与扩展点方法的出入参数完全一致即可,对方法名本身无任何要求,同时在此方法上增加@Plugin注解,并通过注解的extension参数指定实现的扩展点ID。 * 注:包含@Plugin注解方法的类需要可以由Spring自动生成实例,或有一个不带任何参数的默认构造方法。会优先从SpringContext中获取该类的实例 7. 在插件的META-INF目录(resources目录下)增加plugin.info文件,用于描述当前插件信息。插件共支持4个属性:artifactId(插件的ArtifactId)、name(用于显示的可读名称)、version(版本)、description(描述),每行一个属性。如: ``` ## This file describe a plugin, 4 properties could be used: artifactId, version, name, description. artifactId:plugin-example version:1.0.0 name:测试插件 description:这是一个插件的说明 ``` 其中#号之后的内容均为注释 #### 使用说明 --- 1. 安装完毕后,在SpringBoot的入口类中增加@EnableAspectJAutoProxy注解,以让SpringBoot自动扫描带有@Aspect注解的扩展点处理逻辑类。 2. 在SpringBoot的配置文件中(application .properties或其它会被Spring自动加载的配置文件)增加属性bascPackagesToScan,该值为需要Spring自动扫描注解的basePackage(主工程自动扫描的BasePackage 和Mplug需要扫描的目录除外,后者已经在Mplug中定义的SpringAutoScanListener默认指定),该值可以配置多个扫描包,以英文逗号分隔。使用该方法让SpringBoot发现插件管理及扩展实现,为当前版本最佳方法。此时会默认启动一个RestController,该RestController会提供插件的热插拔功能及插件信息查询功能: * 插件热卸载:delete /plugins/uninstall/插件名 * 插件热加载:put /plugins/install/插件名 * 查看当前启用的所有插件名称:get /plugins/get/all * 查看某个插件的详细信息:get /plugins/get/插件名 3. 也可以通过@Bean定义AnnotatedExtensionHandler和ConfigurableExtensionHandler,此时需要手动创建PluginManager并注入到上述两个Handler。不建议使用此方法。 中。此时无法启动PluginManager上的RestController。 4. 要使用ConfigurableExtensionHandler通过配置自动增加扩展点,需要在主工程中创建一个继承自该Handler的类,并在类上增加@Aspect注解,同时在handle方法上增加@Around 注解以说明允许自动增加扩展点的基础包。如: ``` @Aspect @Component public class SomeConfigurableExtensionHandler extends ConfigurableExtensionHandler { public SomeConfigurableExtensionHandler() throws IOException { super(); } @Around("within(net.beyondts.example.mplug.extension..*)") public Object handle(ProceedingJoinPoint joinPoint) throws Throwable { return super.handle(joinPoint); } } ``` * 注:其中@Component注解只是为了使Spring自动启动Handler。子类的所有方法只需要直接调用super的相应方法即可。 5. 在插件工程中可以使用@Component(或任何包含@Component的注解)将类交给Spring管理,同时可以使用@Autowired向其注入其它Spring管理的插件工程中的对象。 6. 如果要在插件工程中使用主工程内的Spring管理的Bean的方法,使用SpringBeansInvocation工具类调用。 7. methodB被methodA调用,且两个方法都在ClassX中,如果methodB是个扩展点,正常使用this.methodB时methodB的插件无法生效,因为this指代的对象不是代理对象,而ClassX本身。要解决这个问题可以让ClassX继承Self类,并在methodA中使用Self类的invokeSelf方法调用methodB,或者使用BeanFactory.getBean(ClassX).methodB的方式调用methodB。 8. 一个扩展点可以有多个插件实现,多个插件实现通过Plugin注解的choose和compare进行选择,以找到匹配的方法。 * choose参数:某个方法参数(a:)或某个Spring的Bean(s:)的属性或方法(需要以()结尾),可多级 * compare参数:常量,choose的结果会与此参数值进行equals比较(按字符串规则),如果相等则选用当前插件方法 * 如果有多个插件方法均可匹配,则仅使用第一个匹配的方法 9. 每个插件可以在安装和卸载时分别执行一些SQL语句,以向数据库中写入必要信息。如需要使用此效果,需要在插件的META-INF下添加install.sql(安装时执行)和uninstall.sql(卸载时执行)。 * 注:每个sql文件中可以包含多条sql语句,但每条sql语句必须以;结尾 #### TODO List ~~* 测试Signature参数间隔是否有空格~~ ~~* 通过配置扩展Spring扫描基础包路径~~ ~~* 插件的插件:暂不考虑此情况~~ ~~* 默认插件,类似Spring配置文件的覆盖效果~~ ~~* 通过配置增加配置扩展点的切面包:无法实现~~ * 扩展点类型 #### 参与贡献 ---