├── .gitee
├── ISSUE_TEMPLATE.zh-CN.md
└── PULL_REQUEST_TEMPLATE.zh-CN.md
├── .gitignore
├── LICENSE
├── README.md
├── README.zh-cn.md
├── images
├── architecture.png
└── docker-img.png
├── pom.xml
├── spring-hot-plugin-common
├── .gitignore
├── pom.xml
└── src
│ └── main
│ └── java
│ └── vip
│ └── aliali
│ └── spring
│ └── plugin
│ └── constants
│ ├── PluginConstants.java
│ ├── PluginState.java
│ └── RuntimeMode.java
├── spring-hot-plugin-core
├── .gitignore
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── vip
│ │ └── aliali
│ │ └── spring
│ │ └── plugin
│ │ ├── AbstractPluginApplication.java
│ │ ├── DefaultPluginApplication.java
│ │ ├── DefaultPluginManager.java
│ │ ├── PluginAutoConfiguration.java
│ │ ├── PluginClassRegister.java
│ │ ├── PluginConfiguration.java
│ │ ├── PluginException.java
│ │ ├── PluginInfo.java
│ │ ├── PluginManager.java
│ │ ├── PluginStarter.java
│ │ ├── listener
│ │ ├── DefaultPluginListenerFactory.java
│ │ └── PluginListener.java
│ │ ├── register
│ │ ├── AbstractRegister.java
│ │ ├── ClearLoggerCache.java
│ │ ├── Register.java
│ │ ├── RegisterController.java
│ │ └── RegisterScheduled.java
│ │ └── util
│ │ └── DeployUtils.java
│ └── resources
│ └── META-INF
│ ├── spring-configuration-metadata.json
│ └── spring.factories
├── spring-hot-plugin-example
├── plugin-demo-2
│ ├── .gitignore
│ ├── pom.xml
│ └── src
│ │ └── main
│ │ └── java
│ │ └── csdn
│ │ └── itsaysay
│ │ └── demo
│ │ └── plugin2
│ │ ├── IndexController.java
│ │ ├── bean
│ │ ├── Person.java
│ │ └── User.java
│ │ ├── mapper
│ │ ├── TestMapper.java
│ │ └── xml
│ │ │ └── TestMapper.xml
│ │ ├── service
│ │ ├── TestService.java
│ │ └── impl
│ │ │ └── TestServiceImpl.java
│ │ └── task
│ │ └── TaskDemo.java
├── plugin-demo-mybatis
│ ├── .gitignore
│ ├── pom.xml
│ └── src
│ │ └── main
│ │ └── java
│ │ └── csdn
│ │ └── itsaysay
│ │ └── demo
│ │ └── mybatis
│ │ ├── IndexController.java
│ │ ├── bean
│ │ └── User.java
│ │ ├── mapper
│ │ ├── TestMapper.java
│ │ └── TestMapper.xml
│ │ └── service
│ │ ├── TestService.java
│ │ └── impl
│ │ └── TestServiceImpl.java
├── plugin-demo
│ ├── .gitignore
│ ├── pom.xml
│ └── src
│ │ └── main
│ │ ├── java
│ │ └── csdn
│ │ │ └── itsaysay
│ │ │ └── demo
│ │ │ └── plugin
│ │ │ ├── IndexController.java
│ │ │ ├── bean
│ │ │ ├── Person.java
│ │ │ └── User.java
│ │ │ ├── hik
│ │ │ └── HCNetSDK.java
│ │ │ ├── mapper
│ │ │ ├── TestMapper.java
│ │ │ └── TestMapper2.java
│ │ │ ├── service
│ │ │ ├── TestService.java
│ │ │ └── impl
│ │ │ │ └── TestServiceImpl.java
│ │ │ └── task
│ │ │ └── TaskDemo.java
│ │ └── resources
│ │ ├── csdn
│ │ └── itsaysay
│ │ │ └── demo
│ │ │ └── plugin
│ │ │ └── mapper
│ │ │ └── TestMapper.xml
│ │ ├── linux-x86-64
│ │ └── libXMTQRCode.so
│ │ └── win32-x86-64
│ │ ├── AudioRender.dll
│ │ ├── GdiPlus.dll
│ │ ├── GdiPlus.lib
│ │ ├── HCCore.dll
│ │ ├── HCCore.lib
│ │ ├── HCNetSDK.dll
│ │ ├── HCNetSDK.lib
│ │ ├── HCNetSDKCom
│ │ ├── AnalyzeData.dll
│ │ ├── AudioIntercom.dll
│ │ ├── AudioRender.dll
│ │ ├── HCAlarm.dll
│ │ ├── HCAlarm.lib
│ │ ├── HCCoreDevCfg.dll
│ │ ├── HCDisplay.dll
│ │ ├── HCGeneralCfgMgr.dll
│ │ ├── HCGeneralCfgMgr.lib
│ │ ├── HCIndustry.dll
│ │ ├── HCPlayBack.dll
│ │ ├── HCPreview.dll
│ │ ├── HCPreview.lib
│ │ ├── HCVoiceTalk.dll
│ │ ├── OpenAL32.dll
│ │ ├── StreamTransClient.dll
│ │ ├── SystemTransform.dll
│ │ └── libiconv2.dll
│ │ ├── HXVA.dll
│ │ ├── HmMerge.dll
│ │ ├── LocalSensorAdd.dat
│ │ ├── MP_Render.dll
│ │ ├── NPQos.dll
│ │ ├── OpenAL32.dll
│ │ ├── PlayCtrl.dll
│ │ ├── PlayCtrl.lib
│ │ ├── SuperRender.dll
│ │ ├── YUVProcess.dll
│ │ ├── examples.jar
│ │ ├── hlog.dll
│ │ ├── hpr.dll
│ │ ├── libcrypto-1_1-x64.dll
│ │ ├── libmmd.dll
│ │ ├── libssl-1_1-x64.dll
│ │ └── zlib1.dll
├── pom.xml
├── spring-hot-plugin-admin
│ ├── .gitignore
│ ├── pom.xml
│ └── src
│ │ └── main
│ │ ├── java
│ │ └── csdn
│ │ │ └── itsaysay
│ │ │ └── main
│ │ │ └── admin
│ │ │ └── AdminApplication.java
│ │ └── resources
│ │ └── application.yml
├── spring-hot-plugin-demo
│ ├── .gitignore
│ ├── Dockerfile
│ ├── pom.xml
│ └── src
│ │ └── main
│ │ ├── java
│ │ └── csdn
│ │ │ └── itsaysay
│ │ │ └── main
│ │ │ ├── DemoApplication.java
│ │ │ └── plugin
│ │ │ ├── PluginController.java
│ │ │ ├── req
│ │ │ └── PluginReq.java
│ │ │ ├── res
│ │ │ └── PluginRes.java
│ │ │ └── service
│ │ │ ├── PluginListener.java
│ │ │ ├── PluginService.java
│ │ │ └── impl
│ │ │ └── PluginServiceImpl.java
│ │ └── resources
│ │ ├── application.yml
│ │ ├── data.sql
│ │ └── schema.sql
├── spring-hot-plugin-example.iml
└── spring-hot-plugin-task-demo
│ ├── .gitignore
│ ├── Dockerfile
│ ├── pom.xml
│ └── src
│ └── main
│ ├── java
│ └── csdn
│ │ └── itsaysay
│ │ └── task
│ │ ├── TaskDemoApplication.java
│ │ └── service
│ │ └── TaskService.java
│ └── resources
│ └── application.yml
├── spring-hot-plugin-loader
├── .gitignore
├── pom.xml
└── src
│ ├── main
│ └── java
│ │ └── vip
│ │ └── aliali
│ │ └── spring
│ │ └── plugin
│ │ └── loader
│ │ ├── ClassPathIndexFile.java
│ │ ├── ExecutableArchiveLauncher.java
│ │ ├── JarLauncher.java
│ │ ├── LaunchedURLClassLoader.java
│ │ ├── Launcher.java
│ │ ├── MainMethodRunner.java
│ │ ├── PropertiesLauncher.java
│ │ ├── WarLauncher.java
│ │ ├── archive
│ │ ├── Archive.java
│ │ ├── ExplodedArchive.java
│ │ ├── JarFileArchive.java
│ │ └── package-info.java
│ │ ├── data
│ │ ├── RandomAccessData.java
│ │ ├── RandomAccessDataFile.java
│ │ └── package-info.java
│ │ ├── jar
│ │ ├── AbstractJarFile.java
│ │ ├── AsciiBytes.java
│ │ ├── Bytes.java
│ │ ├── CentralDirectoryEndRecord.java
│ │ ├── CentralDirectoryFileHeader.java
│ │ ├── CentralDirectoryParser.java
│ │ ├── CentralDirectoryVisitor.java
│ │ ├── FileHeader.java
│ │ ├── Handler.java
│ │ ├── JarEntry.java
│ │ ├── JarEntryCertification.java
│ │ ├── JarEntryFilter.java
│ │ ├── JarFile.java
│ │ ├── JarFileEntries.java
│ │ ├── JarFileWrapper.java
│ │ ├── JarURLConnection.java
│ │ ├── StringSequence.java
│ │ ├── ZipInflaterInputStream.java
│ │ └── package-info.java
│ │ ├── jarmode
│ │ ├── JarMode.java
│ │ ├── JarModeLauncher.java
│ │ ├── TestJarMode.java
│ │ └── package-info.java
│ │ ├── package-info.java
│ │ └── util
│ │ ├── SystemPropertyUtils.java
│ │ └── package-info.java
│ └── test
│ └── java
│ ├── AbstractExecutableArchiveLauncherTests.java
│ └── JarLauncherTest.java
├── spring-hot-plugin-maven
├── .gitignore
├── pom.xml
└── src
│ └── main
│ └── java
│ └── vip
│ └── aliali
│ └── spring
│ └── plugin
│ └── maven
│ ├── PackageConstants.java
│ └── PackagePlugin.java
└── spring-hot-plugin-mybatis
├── .gitignore
├── pom.xml
└── src
└── main
├── java
└── vip
│ └── aliali
│ └── spring
│ └── plugin
│ └── mybatis
│ ├── MybatisPluginConfiguration.java
│ ├── MybatisPlusUtil.java
│ ├── RegisterMybatis.java
│ └── util
│ └── MapperUtils.java
└── resources
└── META-INF
└── spring.factories
/.gitee/ISSUE_TEMPLATE.zh-CN.md:
--------------------------------------------------------------------------------
1 | ### 该问题是怎么引起的?
2 |
3 |
4 |
5 | ### 重现步骤
6 |
7 |
8 |
9 | ### 报错信息
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/.gitee/PULL_REQUEST_TEMPLATE.zh-CN.md:
--------------------------------------------------------------------------------
1 | ### 相关的Issue
2 |
3 |
4 | ### 原因(目的、解决的问题等)
5 |
6 |
7 | ### 描述(做了什么,变更了什么)
8 |
9 |
10 | ### 测试用例(新增、改动、可能影响的功能)
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled class file
2 | *.class
3 |
4 | # Log file
5 | *.log
6 |
7 | # BlueJ files
8 | *.ctxt
9 |
10 | # Mobile Tools for Java (J2ME)
11 | .mtj.tmp/
12 |
13 | # Package Files #
14 | *.jar
15 | *.war
16 | *.nar
17 | *.ear
18 | *.zip
19 | *.tar.gz
20 | *.rar
21 |
22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
23 | hs_err_pid*
24 |
25 | .idea
26 | .settings/
27 | .project
28 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | Spring Plugin Development Framework
4 |
5 |
6 | [🤔Reporting Issues][Issues-url] [📘中文][chinese-url]
7 |
8 | [![license][license-image]][license-url]
9 | [![build][build-image]][build-url]
10 | [![jdk][jdk-image]][jdk-url]
11 | [![hutool][hutool-image]][hutool-url]
12 |
13 |
14 | [license-image]: https://img.shields.io/badge/license-Apache%202.0-green
15 | [stars-image]: https://badgen.net/github/stars/jujunchen/spring-hot-plugin
16 | [build-image]: https://img.shields.io/badge/build-Spring%20Boot%202.7.18-45e91c
17 | [jdk-image]: https://img.shields.io/badge/JDK-8+-green
18 | [hutool-image]: https://img.shields.io/badge/hutool-5.8.4-green
19 |
20 | [license-url]: ./LICENSE
21 | [build-url]: https://github.com/spring-projects/spring-boot
22 | [jdk-url]: https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html
23 | [hutool-url]: https://github.com/dromara/hutool
24 | [Issues-url]: https://github.com/jujunchen/spring-hot-plugin/issues
25 | [chinese-url]: README.zh-cn.md
26 |
27 | ## Introduction
28 | A lightweight, fast, easy, and stable Spring-based plugin development framework. It does not require exposing core module code, reduces code coupling, supports hot reloading for dynamic updates, and improves development efficiency.
29 |
30 | Lightweight: Lightweight
31 |
32 | Fast: Fast startup
33 |
34 | Easy: Easy to use, native Spring programming
35 |
36 | Stable: Stable, strong compatibility
37 |
38 | ## Supported Features
39 | v1.2 (Under Development)
40 | - Support for distributed deployment
41 | - Support for Freemarker template engine
42 |
43 | v1.1.1
44 | - Support for using third-party dependencies in plugins, including jar and dll files
45 | - Support for MyBatis and MyBatisPlus
46 |
47 | v1.0
48 | - Support for subclasses referencing parent Spring Beans
49 | - Isolation of plugin code from main program code
50 | - Support for hot reloading of ordinary classes and various Spring Beans
51 | - Support for hot reloading of Controller controllers
52 | - Support for hot reloading of scheduled tasks
53 | - Support for using third-party dependencies in plugins
54 | - Support for the main program to listen to plugin startup and uninstallation events
55 |
56 | ## Principle
57 |
58 | 
59 |
60 | Based on Spring's applicationContext and classLoader, hot reloading of classes in plugins is performed. During uninstallation, efforts are made to cut off GC ROOTs to avoid memory leaks.
61 |
62 | ## Installation Guide
63 | - spring-hot-plugin-common: Plugin common package
64 | - spring-hot-plugin-core: Plugin core package
65 | - spring-hot-plugin-loader: Plugin dependency loading package
66 | - spring-hot-plugin-maven: Plugin Maven packaging tool
67 | - spring-hot-plugin-mybatis: Plugin MyBatis dependency package
68 | - spring-hot-plugin-example: Plugin example project
69 |
70 | ### Maven Installation
71 | #### Using controller, scheduled tasks, third-party dependencies
72 | ```xml
73 |
74 |
75 | vip.aliali.spring
76 | spring-hot-plugin-core
77 | ${lastVersion}
78 |
79 | ```
80 | #### Using Mybatis, Mybatis-plus
81 | ```xml
82 |
83 |
84 | vip.aliali.spring
85 | spring-hot-plugin-mybatis
86 | ${lastVersion}
87 |
88 | ```
89 | ### Source Code Build
90 | 1. git clone this project
91 | 2. Import the project in IDEA, run `mvn clean install` in the root directory (or upload to a private repository)
92 | 3. Introduce the plugin core package in the main program, modify the version to the latest version
93 |
94 | ```xml
95 |
96 |
97 | csdn.itsaysay.plugin
98 | spring-hot-plugin-core
99 | ${lastVersion}
100 |
101 | ```
102 |
103 | 4. Introduce other dependencies as needed
104 |
105 | ### Usage Instructions
106 | 1. Introduce plugin dependencies in the main program's pom.xml
107 | 2. In the main program, refer to the `spring-hot-plugin-demo` project to create an interface for installing plugins
108 | 3. Configure the plugin
109 |
110 | ```yml
111 | plugin:
112 | #Whether to enable plugin functionality
113 | enable:
114 | #Run mode, development environment: dev, production environment: prod
115 | runMode:
116 | #Backup directory after uninstalling the plugin
117 | backupPath:
118 | #Plugin path, if the plugin path exists, it will be automatically loaded
119 | pluginPath:
120 | #Package path to scan
121 | basePackage:
122 | ```
123 | 4. Plugin Development
124 | >- Refer to `plugin-demo`, introduce the main program with `provided ` lifecycle in Maven, so that the plugin can reference the main program's Beans.
125 | >Other development methods are the same as usual
126 | >- Packaging tool, refer to the pom file configuration of `plugin-demo` for `spring-hot-plugin-maven`
127 | 5. Install Plugin
128 | - Perform `dynamic` installation through the previously created interface, select the jar package with `-repackage` suffix (**Recommended**)
129 | - Directly place it in the plugin installation directory, requires restarting the main program
130 |
131 | ## Performance Test
132 | Test Case:
133 | Simulate frequent plugin installation, uninstallation, and data operations to observe memory consumption.
134 |
135 | Docker container maximum memory: 256MB
136 |
137 | 1. Install plugin-demo-mybatis plugin
138 | 2. Add data
139 | 3. Query data
140 | 4. Delete data
141 | 5. Uninstall plugin-demo-mybatis plugin
142 |
143 | 
144 |
145 | ## Contribution
146 |
147 | 1. Fork this repository
148 | 2. Create a new Feat_xxx branch
149 | 3. Submit code
150 | 4. Create a Pull Request
151 |
--------------------------------------------------------------------------------
/README.zh-cn.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Spring 插件化开发框架
5 |
6 |
7 | [🤔Reporting Issues][Issues-url] [📘English Documentation][english-url]
8 |
9 | [![license][license-image]][license-url]
10 | [![build][build-image]][build-url]
11 | [![jdk][jdk-image]][jdk-url]
12 | [![hutool][hutool-image]][hutool-url]
13 |
14 |
15 | [license-image]: https://img.shields.io/badge/license-Apache%202.0-green
16 | [stars-image]: https://badgen.net/github/stars/jujunchen/spring-hot-plugin
17 | [build-image]: https://img.shields.io/badge/build-Spring%20Boot%202.7.18-45e91c
18 | [jdk-image]: https://img.shields.io/badge/JDK-8+-green
19 | [hutool-image]: https://img.shields.io/badge/hutool-5.8.4-green
20 |
21 | [license-url]: ./LICENSE
22 | [build-url]: https://github.com/spring-projects/spring-boot
23 | [jdk-url]: https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html
24 | [hutool-url]: https://github.com/dromara/hutool
25 | [Issues-url]: https://github.com/jujunchen/spring-hot-plugin/issues
26 | [english-url]: README.md
27 |
28 | ## 介绍
29 | 基于Spring 的插件化开发框架,轻、快、易、稳,无需暴露核心模块代码,降低代码耦合,热加载动态更新,提高开发效率。
30 |
31 | 轻:轻量
32 |
33 | 快:启动速度快
34 |
35 | 易:使用简单,原生spring编程
36 |
37 | 稳:稳定,兼容性强
38 |
39 | ## 支持特性
40 | v1.2(开发中)
41 | - 支持分布式部署
42 | - 支持freemarker模板引擎
43 |
44 | v1.1.1
45 | - 支持插件中使用第三方依赖,jar、dll文件
46 | - 支持Mybatis、MybatisPlus
47 |
48 | v1.0
49 | - 支持子类引用父类Spring Bean
50 | - 插件代码与主程序代码隔离
51 | - 支持热加载普通类、各类Spring Bean
52 | - 支持热加载Controller控制器
53 | - 支持热加载定时任务
54 | - 支持插件中使用第三方依赖
55 | - 支持主程序监听插件启动卸载事件
56 |
57 | ## 原理
58 |
59 | 
60 |
61 | 基于Spring的 applicationContext 和 classLoader 对插件中的类进行热加载,卸载的时候尽可能切断GC ROOT,避免内存泄露。
62 |
63 | ## 安装教程
64 | - spring-hot-plugin-common 插件公共包
65 | - spring-hot-plugin-core 插件核心包
66 | - spring-hot-plugin-loader 插件依赖加载包
67 | - spring-hot-plugin-maven 插件maven打包工具
68 | - spring-hot-plugin-mybatis 插件mybatis依赖包
69 | - spring-hot-plugin-example 插件示例项目
70 |
71 | ### Maven 安装
72 | #### 使用controller、定时任务、第三方依赖
73 | ```xml
74 |
75 |
76 | vip.aliali.spring
77 | spring-hot-plugin-core
78 | ${lastVersion}
79 |
80 | ```
81 | #### 使用mybatis、mybatis-plus
82 | ```xml
83 |
84 |
85 | vip.aliali.spring
86 | spring-hot-plugin-mybatis
87 | ${lastVersion}
88 |
89 | ```
90 |
91 |
92 | ### 源码构建
93 | 1. git clone 本项目
94 | 2. IDEA导入项目,根目录运行 mvn clean install(或者上传到私服)
95 | 3. 主程序中引入插件核心包,修改版本为最新版本
96 | ```xml
97 |
98 |
99 | csdn.itsaysay.plugin
100 | spring-hot-plugin-core
101 | ${lastVersion}
102 |
103 | ```
104 | 4. 其他按需引入依赖
105 |
106 |
107 | ### 使用说明
108 | 1. 在主程序pom.xml中引入插件依赖
109 | 1. 在主程序中,参考`spring-hot-plugin-demo`项目创建一个安装插件的接口
110 | 2. 配置插件
111 | ```yml
112 | plugin:
113 | #是否启用插件功能
114 | enable:
115 | #运行模式,开发环境: dev,生产环境: prod
116 | runMode:
117 | #在卸载插件后, 备份插件的目录
118 | backupPath:
119 | #插件的路径,如果插件路径下存在插件会自动加载
120 | pluginPath:
121 | #扫描的包路径
122 | basePackage:
123 | ```
124 | 3. 插件开发
125 | >- 参考`plugin-demo`,将主程序以`provided `的生命周期引入maven,这样就可以在插件中引用主程序的Bean。
126 | >其他就如同平时开发方式一样
127 | >- 打包工具,同样参考plugin-demo 的 pom文件配置 `spring-hot-plugin-maven`
128 | 4. 安装插件
129 | - 通过前面创建的接口进行`动态`安装,选择`-repackage`结尾的jar包(**推荐**)
130 | - 直接放入插件安装目录,需要重启主程序
131 |
132 | ## 性能测试
133 | 测试用例:
134 | 模拟实际频繁的插件安装卸载和增删改数据操作,观察内存消耗情况。
135 |
136 | Docker 容器最大内容:256MB
137 |
138 | 1. 安装plugin-demo-mybatis插件
139 | 2. 新增数据
140 | 3. 查询数据
141 | 4. 删除数据
142 | 5. 卸载plugin-demo-mybatis插件
143 |
144 | 
145 |
146 |
147 | ## 参与贡献
148 |
149 | 1. Fork 本仓库
150 | 2. 新建 Feat_xxx 分支
151 | 3. 提交代码
152 | 4. 新建 Pull Request
153 |
--------------------------------------------------------------------------------
/images/architecture.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jujunchen/spring-hot-plugin/5c066a4030b3fe269583fc2f24320821c2883e75/images/architecture.png
--------------------------------------------------------------------------------
/images/docker-img.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jujunchen/spring-hot-plugin/5c066a4030b3fe269583fc2f24320821c2883e75/images/docker-img.png
--------------------------------------------------------------------------------
/spring-hot-plugin-common/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | !.mvn/wrapper/maven-wrapper.jar
3 | !**/src/main/**/target/
4 | !**/src/test/**/target/
5 |
6 | ### IntelliJ IDEA ###
7 | .idea/modules.xml
8 | .idea/jarRepositories.xml
9 | .idea/compiler.xml
10 | .idea/libraries/
11 | *.iws
12 | *.iml
13 | *.ipr
14 |
15 | ### Eclipse ###
16 | .apt_generated
17 | .classpath
18 | .factorypath
19 | .project
20 | .settings
21 | .springBeans
22 | .sts4-cache
23 |
24 | ### NetBeans ###
25 | /nbproject/private/
26 | /nbbuild/
27 | /dist/
28 | /nbdist/
29 | /.nb-gradle/
30 | build/
31 | !**/src/main/**/build/
32 | !**/src/test/**/build/
33 |
34 | ### VS Code ###
35 | .vscode/
36 |
37 | ### Mac OS ###
38 | .DS_Store
--------------------------------------------------------------------------------
/spring-hot-plugin-common/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | vip.aliali.spring
8 | spring-hot-plugin-parent
9 | 1.1.1
10 |
11 |
12 | spring-hot-plugin-common
13 | 插件公共组件
14 |
15 |
16 | 8
17 | 8
18 | UTF-8
19 |
20 |
21 |
22 |
23 | cn.hutool
24 | hutool-core
25 | ${hutool.version}
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/spring-hot-plugin-common/src/main/java/vip/aliali/spring/plugin/constants/PluginConstants.java:
--------------------------------------------------------------------------------
1 | package vip.aliali.spring.plugin.constants;
2 |
3 |
4 | /**
5 | * 插件常量
6 | * @author jujunChen
7 | *
8 | */
9 | public class PluginConstants {
10 |
11 | public static final String TARGET = "target";
12 |
13 | public static final String POM = "pom.xml";
14 |
15 | public static final String JAR_SUFFIX = ".jar";
16 |
17 | public static final String REPACKAGE = "repackage";
18 |
19 | public static final String CLASSES = "classes";
20 |
21 | public static final String LIB = "lib";
22 |
23 | public static final String CLASS_SUFFIX = ".class";
24 |
25 | public static final String MANIFEST = "MANIFEST.MF";
26 |
27 | public static final String PLUGINID = "pluginId";
28 |
29 | public static final String PLUGINVERSION = "pluginVersion";
30 |
31 | public static final String PLUGINDESCRIPTION = "pluginDescription";
32 |
33 | public static final String PLUGIN_POM = "plugins/pom.xml";
34 | public static final String FILE_PREFIX = "file:/";
35 |
36 | public static final String JAR_DELIMITER = "!/";
37 |
38 | public static final String JAR_File_DELIMITER = "/";
39 |
40 | public static final String JAR_File_ACROSS = "-";
41 |
42 | public static final String JAR_NAME = "jar";
43 | }
44 |
--------------------------------------------------------------------------------
/spring-hot-plugin-common/src/main/java/vip/aliali/spring/plugin/constants/PluginState.java:
--------------------------------------------------------------------------------
1 | package vip.aliali.spring.plugin.constants;
2 |
3 | /**
4 | * 插件状态
5 | * @author jujunChen
6 | *
7 | */
8 | public enum PluginState {
9 | /**
10 | * 被禁用状态
11 | */
12 | DISABLED("DISABLED"),
13 |
14 | /**
15 | * 启动状态
16 | */
17 | STARTED("STARTED"),
18 |
19 |
20 | /**
21 | * 停止状态
22 | */
23 | STOPPED("STOPPED");
24 |
25 | private final String status;
26 |
27 | PluginState(String status) {
28 | this.status = status;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/spring-hot-plugin-common/src/main/java/vip/aliali/spring/plugin/constants/RuntimeMode.java:
--------------------------------------------------------------------------------
1 | package vip.aliali.spring.plugin.constants;
2 |
3 |
4 | /**
5 | * 插件运行环境
6 | * @author jujunChen
7 | *
8 | */
9 | public enum RuntimeMode {
10 |
11 | /**
12 | * 开发环境
13 | */
14 | DEV("dev"),
15 |
16 | /**
17 | * 生产环境
18 | */
19 | PROD("prod");
20 |
21 | private final String mode;
22 |
23 | public static RuntimeMode byName(String model){
24 | if(DEV.name().equalsIgnoreCase(model)){
25 | return RuntimeMode.DEV;
26 | } else {
27 | return RuntimeMode.PROD;
28 | }
29 | }
30 |
31 | RuntimeMode(String mode) {
32 | this.mode = mode;
33 | }
34 |
35 | public String getMode() {
36 | return mode;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/spring-hot-plugin-core/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | !.mvn/wrapper/maven-wrapper.jar
3 | !**/src/main/**/target/
4 | !**/src/test/**/target/
5 |
6 | ### IntelliJ IDEA ###
7 | .idea/modules.xml
8 | .idea/jarRepositories.xml
9 | .idea/compiler.xml
10 | .idea/libraries/
11 | *.iws
12 | *.iml
13 | *.ipr
14 |
15 | ### Eclipse ###
16 | .apt_generated
17 | .classpath
18 | .factorypath
19 | .project
20 | .settings
21 | .springBeans
22 | .sts4-cache
23 |
24 | ### NetBeans ###
25 | /nbproject/private/
26 | /nbbuild/
27 | /dist/
28 | /nbdist/
29 | /.nb-gradle/
30 | build/
31 | !**/src/main/**/build/
32 | !**/src/test/**/build/
33 |
34 | ### VS Code ###
35 | .vscode/
36 |
37 | ### Mac OS ###
38 | .DS_Store
--------------------------------------------------------------------------------
/spring-hot-plugin-core/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | vip.aliali.spring
8 | spring-hot-plugin-parent
9 | 1.1.1
10 |
11 |
12 | spring-hot-plugin-core
13 | jar
14 |
15 | 插件核心模块
16 |
17 |
18 | 8
19 | 8
20 | UTF-8
21 |
22 |
23 |
24 |
25 | org.springframework.boot
26 | spring-boot-starter-web
27 | provided
28 | true
29 |
30 |
31 |
32 | org.apache.maven
33 | maven-model
34 | 3.8.4
35 |
36 |
37 |
38 | vip.aliali.spring
39 | spring-hot-plugin-common
40 | ${project.version}
41 |
42 |
43 |
44 |
45 | vip.aliali.spring
46 | spring-hot-plugin-loader
47 | ${project.version}
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/spring-hot-plugin-core/src/main/java/vip/aliali/spring/plugin/AbstractPluginApplication.java:
--------------------------------------------------------------------------------
1 | package vip.aliali.spring.plugin;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 | import org.springframework.beans.factory.BeanCreationException;
6 | import org.springframework.context.ApplicationContext;
7 |
8 | import java.util.Objects;
9 | import java.util.concurrent.atomic.AtomicBoolean;
10 |
11 | public abstract class AbstractPluginApplication {
12 |
13 | private final Logger log = LoggerFactory.getLogger(this.getClass());
14 |
15 | private final AtomicBoolean beInitialized = new AtomicBoolean(false);
16 |
17 | public synchronized void initialize(ApplicationContext applicationContext) {
18 | Objects.requireNonNull(applicationContext, "ApplicationContext can't be null");
19 | if(beInitialized.get()) {
20 | throw new RuntimeException("Plugin has been initialized");
21 | }
22 | //获取配置
23 | PluginAutoConfiguration configuration = getConfiguration(applicationContext);
24 |
25 | if (Boolean.FALSE.equals(configuration.getEnable())) {
26 | log.info("插件已禁用");
27 | return;
28 | }
29 |
30 | try {
31 | log.info("插件加载环境: {},插件目录: {}", configuration.getRunMode(), String.join(",", configuration.getPluginPath()));
32 |
33 | DefaultPluginManager pluginManager = getPluginManager(applicationContext);
34 | pluginManager.createPluginListenerFactory();
35 | pluginManager.loadPlugins();
36 | beInitialized.set(true);
37 |
38 | log.info("插件启动完成");
39 | } catch (Exception e) {
40 | log.error("初始化插件异常", e);
41 | }
42 | }
43 |
44 | protected PluginAutoConfiguration getConfiguration(ApplicationContext applicationContext) {
45 | PluginAutoConfiguration configuration = null;
46 | try {
47 | configuration = applicationContext.getBean(PluginAutoConfiguration.class);
48 | } catch (Exception e){
49 | // no show exception
50 | }
51 | if(configuration == null){
52 | throw new BeanCreationException("没有发现 Bean");
53 | }
54 | return configuration;
55 | }
56 |
57 | protected DefaultPluginManager getPluginManager(ApplicationContext applicationContext) {
58 | DefaultPluginManager pluginManager = null;
59 | try {
60 | pluginManager = applicationContext.getBean(DefaultPluginManager.class);
61 | } catch (Exception e){
62 | // no show exception
63 | }
64 | if(pluginManager == null){
65 | throw new BeanCreationException("没有发现 Bean");
66 | }
67 | return pluginManager;
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/spring-hot-plugin-core/src/main/java/vip/aliali/spring/plugin/DefaultPluginApplication.java:
--------------------------------------------------------------------------------
1 | package vip.aliali.spring.plugin;
2 |
3 | import org.springframework.beans.BeansException;
4 | import org.springframework.boot.context.event.ApplicationStartedEvent;
5 | import org.springframework.context.ApplicationContext;
6 | import org.springframework.context.ApplicationContextAware;
7 | import org.springframework.context.ApplicationListener;
8 | import org.springframework.context.annotation.Import;
9 |
10 | @Import(PluginConfiguration.class)
11 | public class DefaultPluginApplication extends AbstractPluginApplication implements ApplicationContextAware, ApplicationListener {
12 |
13 | private ApplicationContext applicationContext;
14 |
15 | //主程序启动后加载插件
16 | @Override
17 | public void onApplicationEvent(ApplicationStartedEvent event) {
18 | super.initialize(applicationContext);
19 | }
20 |
21 | @Override
22 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
23 | this.applicationContext = applicationContext;
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/spring-hot-plugin-core/src/main/java/vip/aliali/spring/plugin/PluginAutoConfiguration.java:
--------------------------------------------------------------------------------
1 | package vip.aliali.spring.plugin;
2 |
3 | import vip.aliali.spring.plugin.constants.RuntimeMode;
4 | import org.springframework.beans.factory.annotation.Value;
5 | import org.springframework.boot.context.properties.ConfigurationProperties;
6 |
7 | /**
8 | * 插件自动化配置
9 | * @author jujunChen
10 | *
11 | */
12 | @ConfigurationProperties(prefix = "plugin")
13 | public class PluginAutoConfiguration {
14 |
15 | /**
16 | * 是否启用插件功能
17 | */
18 | @Value("${enable:true}")
19 | private Boolean enable;
20 |
21 | /**
22 | * 运行模式
23 | * 开发环境: dev
24 | * 生产环境: prod
25 | */
26 | @Value("${runMode:dev}")
27 | private String runMode;
28 |
29 | /**
30 | * 插件的路径
31 | */
32 | private String pluginPath;
33 |
34 | /**
35 | * 在卸载插件后, 备份插件的目录
36 | */
37 | @Value("${backupPath:backupPlugin}")
38 | private String backupPath;
39 |
40 | /**
41 | * 扫描的包路径
42 | */
43 | @Value("${basePackage:csdn.itsaysay.plugin}")
44 | private String basePackage;
45 |
46 | public RuntimeMode environment() {
47 | return RuntimeMode.byName(runMode);
48 | }
49 |
50 | public Boolean getEnable() {
51 | return enable;
52 | }
53 |
54 | public void setEnable(Boolean enable) {
55 | this.enable = enable;
56 | }
57 |
58 | public String getRunMode() {
59 | return runMode;
60 | }
61 |
62 | public void setRunMode(String runMode) {
63 | this.runMode = runMode;
64 | }
65 |
66 | public String getPluginPath() {
67 | return pluginPath;
68 | }
69 |
70 | public void setPluginPath(String pluginPath) {
71 | this.pluginPath = pluginPath;
72 | }
73 |
74 | public String getBackupPath() {
75 | return backupPath;
76 | }
77 |
78 | public void setBackupPath(String backupPath) {
79 | this.backupPath = backupPath;
80 | }
81 |
82 | public String getBasePackage() {
83 | return basePackage;
84 | }
85 |
86 | public void setBasePackage(String basePackage) {
87 | this.basePackage = basePackage;
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/spring-hot-plugin-core/src/main/java/vip/aliali/spring/plugin/PluginClassRegister.java:
--------------------------------------------------------------------------------
1 | package vip.aliali.spring.plugin;
2 |
3 | import cn.hutool.core.util.ReflectUtil;
4 | import vip.aliali.spring.plugin.loader.LaunchedURLClassLoader;
5 | import vip.aliali.spring.plugin.loader.jar.JarFile;
6 | import vip.aliali.spring.plugin.register.Register;
7 | import lombok.extern.slf4j.Slf4j;
8 | import org.springframework.context.ApplicationContext;
9 | import org.springframework.context.annotation.AnnotationConfigApplicationContext;
10 |
11 | import java.io.IOException;
12 | import java.net.URL;
13 | import java.net.URLClassLoader;
14 | import java.security.ProtectionDomain;
15 | import java.util.Map;
16 | import java.util.Vector;
17 |
18 | /**
19 | * 插件注册
20 | */
21 | @Slf4j
22 | public class PluginClassRegister {
23 | private ApplicationContext applicationContext;
24 | private PluginAutoConfiguration configuration;
25 | private Map pluginBeans;
26 |
27 | public PluginClassRegister(ApplicationContext applicationContext, PluginAutoConfiguration configuration, Map pluginBeans) {
28 | this.applicationContext = applicationContext;
29 | this.configuration = configuration;
30 | this.pluginBeans = pluginBeans;
31 | }
32 |
33 |
34 | public ApplicationContext register(PluginInfo pluginInfo) {
35 | ApplicationContext pluginApplicationContext = registerBean(pluginInfo);
36 | pluginBeans.put(pluginInfo.getId(), pluginApplicationContext);
37 | return pluginApplicationContext;
38 | }
39 |
40 | public boolean unRegister(PluginInfo pluginInfo) throws IOException {
41 | return unRegisterBean(pluginInfo);
42 | }
43 |
44 | private boolean unRegisterBean(PluginInfo pluginInfo) throws IOException {
45 | AnnotationConfigApplicationContext pluginApplicationContext = (AnnotationConfigApplicationContext) pluginBeans.get(pluginInfo.getId());
46 | //取消注册
47 | applyUnRegister(pluginApplicationContext, pluginInfo);
48 | clearClassLoader(pluginApplicationContext);
49 | clearApplicationContext(pluginApplicationContext);
50 | pluginBeans.remove(pluginInfo.getId());
51 | //及时回收掉
52 | System.gc();
53 | return true;
54 | }
55 |
56 | /**
57 | * 清理applicationContext
58 | * @param pluginApplicationContext
59 | */
60 | private void clearApplicationContext(AnnotationConfigApplicationContext pluginApplicationContext) {
61 | pluginApplicationContext.setClassLoader(null);
62 | pluginApplicationContext.close();
63 | }
64 |
65 | /**
66 | * 清理classLoader
67 | * @param pluginApplicationContext
68 | */
69 | private void clearClassLoader(AnnotationConfigApplicationContext pluginApplicationContext) {
70 | LaunchedURLClassLoader launchedURLClassLoader = ((LaunchedURLClassLoader)pluginApplicationContext.getClassLoader());
71 | ProtectionDomain protectionDomain = ((ProtectionDomain) ReflectUtil.getFieldValue(launchedURLClassLoader, "defaultDomain"));
72 | ReflectUtil.setFieldValue(protectionDomain, "classloader", null);
73 | ((Map)ReflectUtil.getFieldValue(launchedURLClassLoader, "packages")).clear();
74 | ((Map)ReflectUtil.getFieldValue(launchedURLClassLoader, "pdcache")).clear();
75 | ((Vector)ReflectUtil.getFieldValue(launchedURLClassLoader, "classes")).clear();
76 | launchedURLClassLoader.close();
77 | }
78 |
79 | private ApplicationContext registerBean(PluginInfo pluginInfo) {
80 | URLClassLoader classLoader = null;
81 | try {
82 | URL[] dependenciesURLs = pluginInfo.getDependenciesPath();
83 | //插件的类路径加入到自定义classLoader中
84 | classLoader = createClassLoader(dependenciesURLs);
85 |
86 | //一个插件创建一个applicationContext
87 | AnnotationConfigApplicationContext pluginApplicationContext = new AnnotationConfigApplicationContext();
88 | pluginApplicationContext.setClassLoader(classLoader);
89 | pluginApplicationContext.setParent(applicationContext);
90 | pluginApplicationContext.scan(configuration.getBasePackage());
91 | //上下文刷新前的操作
92 | applyRefreshBeforeRegister(pluginApplicationContext, pluginInfo);
93 | pluginApplicationContext.refresh();
94 | //上下文刷新后的操作
95 | applyRefreshAfterRegister(pluginApplicationContext, pluginInfo);
96 |
97 | return pluginApplicationContext;
98 | } catch (Exception | Error e) {
99 | throw new PluginException("注册bean异常", e);
100 | }
101 | }
102 |
103 | private URLClassLoader createClassLoader(URL[] newPath) {
104 | JarFile.registerUrlProtocolHandler();
105 | return new LaunchedURLClassLoader(newPath, getClass().getClassLoader());
106 | }
107 |
108 | private void applyUnRegister(ApplicationContext pluginApplicationContext, PluginInfo pluginInfo) {
109 | Map registers = applicationContext.getBeansOfType(Register.class);
110 | registers.forEach((name, register) -> {
111 | register.unRegister(pluginApplicationContext, pluginInfo);
112 | });
113 | }
114 |
115 | private void applyRefreshAfterRegister(ApplicationContext pluginApplicationContext, PluginInfo pluginInfo) {
116 | Map registers = applicationContext.getBeansOfType(Register.class);
117 | registers.forEach((name, register) -> {
118 | register.refreshAfterRegister(pluginApplicationContext, pluginInfo);
119 | });
120 | }
121 |
122 | private void applyRefreshBeforeRegister(AnnotationConfigApplicationContext pluginApplicationContext, PluginInfo pluginInfo) {
123 | Map registers = applicationContext.getBeansOfType(Register.class);
124 | registers.forEach((name, register) -> {
125 | register.refreshBeforeRegister(pluginApplicationContext, pluginInfo);
126 | });
127 | }
128 |
129 | }
130 |
--------------------------------------------------------------------------------
/spring-hot-plugin-core/src/main/java/vip/aliali/spring/plugin/PluginConfiguration.java:
--------------------------------------------------------------------------------
1 | package vip.aliali.spring.plugin;
2 |
3 | import vip.aliali.spring.plugin.register.ClearLoggerCache;
4 | import vip.aliali.spring.plugin.register.RegisterController;
5 | import vip.aliali.spring.plugin.register.RegisterScheduled;
6 | import org.springframework.context.ApplicationContext;
7 | import org.springframework.context.annotation.Bean;
8 | import org.springframework.context.annotation.Configuration;
9 |
10 | @Configuration
11 | public class PluginConfiguration {
12 |
13 |
14 | @Bean
15 | public PluginManager createPluginManager(PluginAutoConfiguration configuration, ApplicationContext applicationContext) {
16 | return new DefaultPluginManager(configuration, applicationContext);
17 | }
18 |
19 | @Bean
20 | public RegisterController createRegisterController(ApplicationContext main) {
21 | return new RegisterController(main);
22 | }
23 |
24 |
25 | @Bean
26 | public RegisterScheduled createRegisterScheduled(ApplicationContext main) {
27 | return new RegisterScheduled(main);
28 | }
29 |
30 | @Bean
31 | public ClearLoggerCache createClearLoggerCache(ApplicationContext main) {
32 | return new ClearLoggerCache(main);
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/spring-hot-plugin-core/src/main/java/vip/aliali/spring/plugin/PluginException.java:
--------------------------------------------------------------------------------
1 | package vip.aliali.spring.plugin;
2 |
3 |
4 | import java.util.function.Supplier;
5 |
6 | public class PluginException extends RuntimeException {
7 | public PluginException() {
8 | super();
9 | }
10 |
11 | public PluginException(String message) {
12 | super(message);
13 | }
14 |
15 | public PluginException(Throwable cause) {
16 | super(cause);
17 | }
18 |
19 | public PluginException(String message, Throwable cause, String... arg) {
20 | super(String.format(message, arg), cause);
21 | }
22 |
23 | public static PluginException getPluginException(Throwable throwable, Supplier getException){
24 | if(throwable instanceof PluginException){
25 | return (PluginException) throwable;
26 | }
27 | return getException.get();
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/spring-hot-plugin-core/src/main/java/vip/aliali/spring/plugin/PluginInfo.java:
--------------------------------------------------------------------------------
1 | package vip.aliali.spring.plugin;
2 |
3 | import vip.aliali.spring.plugin.constants.PluginState;
4 | import lombok.Builder;
5 | import lombok.Data;
6 |
7 | import java.net.URL;
8 | import java.util.Objects;
9 |
10 | /**
11 | * 插件信息
12 | * @author jujunChen
13 | *
14 | */
15 | @Builder
16 | @Data
17 | public class PluginInfo {
18 |
19 | /**
20 | * 插件id
21 | */
22 | private String id;
23 |
24 | /**
25 | * 版本
26 | */
27 | private String version;
28 |
29 | /**
30 | * 描述
31 | */
32 | private String description;
33 |
34 | /**
35 | * 插件路径
36 | */
37 | private String path;
38 |
39 | /**
40 | * 插件的依赖所在路径
41 | */
42 | private URL[] dependenciesPath;
43 |
44 | /**
45 | * 插件启动状态
46 | */
47 | private PluginState pluginState;
48 |
49 | @Override
50 | public boolean equals(Object obj) {
51 | if (this == obj)
52 | return true;
53 | if (obj == null)
54 | return false;
55 | if (getClass() != obj.getClass())
56 | return false;
57 | PluginInfo other = (PluginInfo) obj;
58 | return Objects.equals(id, other.id);
59 | }
60 |
61 | @Override
62 | public int hashCode() {
63 | return Objects.hash(id);
64 | }
65 |
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/spring-hot-plugin-core/src/main/java/vip/aliali/spring/plugin/PluginManager.java:
--------------------------------------------------------------------------------
1 | package vip.aliali.spring.plugin;
2 |
3 | import org.springframework.context.ApplicationContext;
4 | import org.springframework.web.multipart.MultipartFile;
5 |
6 | import java.lang.annotation.Annotation;
7 | import java.util.List;
8 |
9 | /**
10 | * 插件管理接口
11 | * @author jujunChen
12 | *
13 | */
14 | public interface PluginManager {
15 |
16 | /**
17 | * 加载配置目录中全部插件
18 | * @return
19 | */
20 | List loadPlugins() throws Exception;
21 |
22 | /**
23 | * 安装插件,覆盖安装,并备份原版本,只适用于prod环境
24 | * @param file 插件输入流
25 | * @return
26 | */
27 | PluginInfo install(MultipartFile file);
28 |
29 | /**
30 | * 卸载插件,停止插件,删除插件,只适用于prod环境
31 | * @param pluginId 插件id
32 | */
33 | void uninstall(String pluginId);
34 |
35 | /**
36 | * 启动插件
37 | * @param pluginId 插件id
38 | * @return
39 | */
40 | PluginInfo start(String pluginId);
41 |
42 | /**
43 | * 停止插件
44 | * @param pluginId 插件id
45 | * @return
46 | */
47 | PluginInfo stop(String pluginId);
48 |
49 | /**
50 | * 获取当前插件列表
51 | * @param pluginId 插件id
52 | * @return
53 | */
54 | List list(String pluginId);
55 |
56 |
57 | /**
58 | * 获取插件加载上下文
59 | * @param pluginId
60 | * @return
61 | */
62 | ApplicationContext getApplicationContext(String pluginId);
63 |
64 | /**
65 | * 从插件上下文中获取指定名称的bean
66 | * @param beanName bean名称
67 | * @return
68 | */
69 | Object getBeanFromApplicationContext(String beanName);
70 |
71 | /**
72 | * 通过注解获取具体插件中的 Bean
73 | * @param pluginId 插件id
74 | * @param annotationType 指定注解
75 | * @return
76 | */
77 | List getBeansWithAnnotation(String pluginId, Class extends Annotation> annotationType);
78 | }
79 |
--------------------------------------------------------------------------------
/spring-hot-plugin-core/src/main/java/vip/aliali/spring/plugin/PluginStarter.java:
--------------------------------------------------------------------------------
1 | package vip.aliali.spring.plugin;
2 |
3 | import org.springframework.boot.context.properties.EnableConfigurationProperties;
4 | import org.springframework.context.annotation.Configuration;
5 | import org.springframework.context.annotation.Import;
6 |
7 | @Configuration
8 | @EnableConfigurationProperties(PluginAutoConfiguration.class)
9 | @Import(DefaultPluginApplication.class)
10 | public class PluginStarter {
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/spring-hot-plugin-core/src/main/java/vip/aliali/spring/plugin/listener/DefaultPluginListenerFactory.java:
--------------------------------------------------------------------------------
1 | package vip.aliali.spring.plugin.listener;
2 |
3 | import vip.aliali.spring.plugin.PluginInfo;
4 | import org.springframework.context.ApplicationContext;
5 |
6 | import java.util.ArrayList;
7 | import java.util.List;
8 | import java.util.Map;
9 |
10 | /**
11 | * 插件监听工厂
12 | * @author jujunChen
13 | *
14 | */
15 | public class DefaultPluginListenerFactory implements PluginListener {
16 | private final List listeners;
17 |
18 | public DefaultPluginListenerFactory(ApplicationContext applicationContext){
19 | listeners = new ArrayList<>();
20 | addExtendPluginListener(applicationContext);
21 | }
22 |
23 | public DefaultPluginListenerFactory(){
24 | listeners = new ArrayList<>();
25 | }
26 |
27 |
28 | private void addExtendPluginListener(ApplicationContext applicationContext){
29 | Map beansOfTypeMap = applicationContext.getBeansOfType(PluginListener.class);
30 | if (!beansOfTypeMap.isEmpty()) {
31 | listeners.addAll(beansOfTypeMap.values());
32 | }
33 | }
34 |
35 | public synchronized void addPluginListener(PluginListener pluginListener) {
36 | if(pluginListener != null){
37 | listeners.add(pluginListener);
38 | }
39 | }
40 |
41 | public List getListeners() {
42 | return listeners;
43 | }
44 |
45 |
46 | @Override
47 | public void startSuccess(PluginInfo pluginInfo) {
48 | for (PluginListener listener : listeners) {
49 | try {
50 | listener.startSuccess(pluginInfo);
51 | } catch (Exception e) {
52 |
53 | }
54 | }
55 | }
56 |
57 | @Override
58 | public void startFailure(PluginInfo pluginInfo, Throwable throwable) {
59 | for (PluginListener listener : listeners) {
60 | try {
61 | listener.startFailure(pluginInfo, throwable);
62 | } catch (Exception e) {
63 |
64 | }
65 | }
66 | }
67 |
68 | @Override
69 | public void stopSuccess(PluginInfo pluginInfo) {
70 | for (PluginListener listener : listeners) {
71 | try {
72 | listener.stopSuccess(pluginInfo);
73 | } catch (Exception e) {
74 |
75 | }
76 | }
77 | }
78 |
79 | @Override
80 | public void stopFailure(PluginInfo pluginInfo, Throwable throwable) {
81 | for (PluginListener listener : listeners) {
82 | try {
83 | listener.stopFailure(pluginInfo, throwable);
84 | } catch (Exception e) {
85 |
86 | }
87 | }
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/spring-hot-plugin-core/src/main/java/vip/aliali/spring/plugin/listener/PluginListener.java:
--------------------------------------------------------------------------------
1 | package vip.aliali.spring.plugin.listener;
2 |
3 |
4 | import vip.aliali.spring.plugin.PluginInfo;
5 |
6 | /**
7 | * 插件监听器
8 | * @author jujunChen
9 | *
10 | */
11 | public interface PluginListener {
12 |
13 |
14 | /**
15 | * 注册插件成功
16 | * @param pluginInfo 插件信息
17 | */
18 | default void startSuccess(PluginInfo pluginInfo){}
19 |
20 |
21 | /**
22 | * 启动失败
23 | * @param pluginInfo 插件信息
24 | * @param throwable 异常信息
25 | */
26 | default void startFailure(PluginInfo pluginInfo, Throwable throwable){}
27 |
28 | /**
29 | * 卸载插件成功
30 | * @param pluginInfo 插件信息
31 | */
32 | default void stopSuccess(PluginInfo pluginInfo){}
33 |
34 |
35 | /**
36 | * 停止失败
37 | * @param pluginInfo 插件信息
38 | * @param throwable 异常信息
39 | */
40 | default void stopFailure(PluginInfo pluginInfo, Throwable throwable){}
41 | }
42 |
--------------------------------------------------------------------------------
/spring-hot-plugin-core/src/main/java/vip/aliali/spring/plugin/register/AbstractRegister.java:
--------------------------------------------------------------------------------
1 | package vip.aliali.spring.plugin.register;
2 |
3 | import vip.aliali.spring.plugin.PluginInfo;
4 | import org.springframework.context.ApplicationContext;
5 |
6 | public abstract class AbstractRegister implements Register{
7 | protected ApplicationContext main;
8 |
9 | public AbstractRegister(ApplicationContext main) {
10 | this.main = main;
11 | }
12 |
13 | @Override
14 | public void refreshBeforeRegister(ApplicationContext plugin, PluginInfo pluginInfo) {
15 |
16 | }
17 |
18 | @Override
19 | public void refreshAfterRegister(ApplicationContext plugin, PluginInfo pluginInfo) {
20 |
21 | }
22 |
23 | @Override
24 | public void unRegister(ApplicationContext plugin, PluginInfo pluginInfo) {
25 |
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/spring-hot-plugin-core/src/main/java/vip/aliali/spring/plugin/register/ClearLoggerCache.java:
--------------------------------------------------------------------------------
1 | package vip.aliali.spring.plugin.register;
2 |
3 | import ch.qos.logback.classic.Logger;
4 | import ch.qos.logback.classic.LoggerContext;
5 | import cn.hutool.core.collection.CollUtil;
6 | import cn.hutool.core.util.ReflectUtil;
7 | import vip.aliali.spring.plugin.PluginAutoConfiguration;
8 | import vip.aliali.spring.plugin.PluginInfo;
9 | import vip.aliali.spring.plugin.util.DeployUtils;
10 | import lombok.extern.slf4j.Slf4j;
11 | import org.slf4j.ILoggerFactory;
12 | import org.slf4j.LoggerFactory;
13 | import org.springframework.context.ApplicationContext;
14 |
15 | import java.util.*;
16 |
17 | /**
18 | * 清理logger缓存
19 | * @author jujun.chen
20 | */
21 | @Slf4j
22 | public class ClearLoggerCache extends AbstractRegister {
23 |
24 | private Map loggerCache;
25 |
26 | public ClearLoggerCache(ApplicationContext main) {
27 | super(main);
28 | }
29 |
30 | @Override
31 | public void unRegister(ApplicationContext plugin, PluginInfo pluginInfo) {
32 | try {
33 | ILoggerFactory iLoggerFactory = LoggerFactory.getILoggerFactory();
34 | if (iLoggerFactory instanceof LoggerContext) {
35 | LoggerContext loggerContext = (LoggerContext) iLoggerFactory;
36 | loggerCache = (Map) ReflectUtil.getFieldValue(loggerContext, "loggerCache");
37 | clearLoggerCache(pluginInfo);
38 | }
39 | } catch (Exception ex) {
40 | log.error("clear logger cache error", ex);
41 | }
42 | }
43 |
44 | private void clearLoggerCache(PluginInfo pluginInfo) {
45 | Set keysToRemove = new HashSet<>();
46 | List classesNames = new ArrayList<>(DeployUtils.readClassFile(pluginInfo.getPath()));
47 | if (CollUtil.isEmpty(classesNames)) {
48 | return;
49 | }
50 |
51 | for (String key : loggerCache.keySet()) {
52 | String nextPackage = getNextPackage(classesNames.get(0));
53 | if (key.startsWith(nextPackage)) {
54 | keysToRemove.add(key);
55 | }
56 | }
57 |
58 | for (String key : keysToRemove) {
59 | Logger logger = loggerCache.get(key);
60 | //从父级开始删除本身引用,删除后,类实例化的时候会重新创建
61 | Logger parent = (Logger) ReflectUtil.getFieldValue(logger, "parent");
62 | List childrenList = (List)ReflectUtil.getFieldValue(parent, "childrenList");
63 | if (CollUtil.isNotEmpty(childrenList)) {
64 | childrenList.remove(logger);
65 | }
66 | loggerCache.remove(key);
67 | }
68 | }
69 |
70 | /**
71 | * 从全限定名中获取basePackage的下一个包名
72 | * @param className 全限定类名
73 | * @return
74 | */
75 | private String getNextPackage(String className) {
76 | PluginAutoConfiguration pluginConfiguration = main.getBean(PluginAutoConfiguration.class);
77 | String basePackage = pluginConfiguration.getBasePackage();
78 | int startIndex = className.indexOf(basePackage);
79 | if (startIndex == -1) {
80 | return "";
81 | }
82 |
83 | // 从已知字符串的末尾开始查找下一个'.'
84 | int endIndex = className.indexOf('.', startIndex + basePackage.length() + 1);
85 | if (endIndex == -1) {
86 | return basePackage;
87 | }
88 |
89 | // 截取我们需要的字符串部分
90 | return className.substring(0, endIndex);
91 | }
92 |
93 |
94 | }
95 |
--------------------------------------------------------------------------------
/spring-hot-plugin-core/src/main/java/vip/aliali/spring/plugin/register/Register.java:
--------------------------------------------------------------------------------
1 | package vip.aliali.spring.plugin.register;
2 |
3 | import vip.aliali.spring.plugin.PluginInfo;
4 | import org.springframework.context.ApplicationContext;
5 |
6 | /**
7 | * 功能性bean 注册接口,比如controller接口、定时任务
8 | */
9 | public interface Register {
10 |
11 | void refreshBeforeRegister(ApplicationContext plugin, PluginInfo pluginInfo);
12 |
13 | void refreshAfterRegister(ApplicationContext plugin, PluginInfo pluginInfo);
14 |
15 | void unRegister(ApplicationContext plugin, PluginInfo pluginInfo);
16 | }
17 |
--------------------------------------------------------------------------------
/spring-hot-plugin-core/src/main/java/vip/aliali/spring/plugin/register/RegisterController.java:
--------------------------------------------------------------------------------
1 | package vip.aliali.spring.plugin.register;
2 |
3 | import cn.hutool.core.util.ReflectUtil;
4 | import vip.aliali.spring.plugin.PluginInfo;
5 | import vip.aliali.spring.plugin.util.DeployUtils;
6 | import lombok.extern.slf4j.Slf4j;
7 | import org.springframework.beans.factory.support.DefaultListableBeanFactory;
8 | import org.springframework.cglib.core.ReflectUtils;
9 | import org.springframework.context.ApplicationContext;
10 | import org.springframework.context.annotation.AnnotationConfigApplicationContext;
11 | import org.springframework.stereotype.Controller;
12 | import org.springframework.web.method.HandlerMethod;
13 | import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
14 | import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
15 | import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
16 |
17 | import java.lang.reflect.Method;
18 | import java.util.HashSet;
19 | import java.util.Map;
20 | import java.util.Objects;
21 | import java.util.Set;
22 | import java.util.concurrent.ConcurrentHashMap;
23 |
24 | /**
25 | * controller 接口注册程序
26 | */
27 | @Slf4j
28 | public class RegisterController extends AbstractRegister {
29 |
30 | private Map> requestMappings = new ConcurrentHashMap<>();
31 |
32 | public RegisterController(ApplicationContext main) {
33 | super(main);
34 | }
35 | @Override
36 | public void refreshAfterRegister(ApplicationContext plugin, PluginInfo pluginInfo) {
37 | try {
38 | Set classNames = DeployUtils.readClassFile(pluginInfo.getPath());
39 | Set pluginRequestMappings = new HashSet<>();
40 | for (String className : classNames) {
41 | Class> aClass = Class.forName(className, false, plugin.getClassLoader());
42 | if (DeployUtils.isController(aClass)) {
43 | Object bean = plugin.getBean(aClass);
44 | Set requestMappingInfos = registerController(bean);
45 | printRegisterSuccessController(pluginInfo, requestMappingInfos);
46 | pluginRequestMappings.addAll(requestMappingInfos);
47 | }
48 | }
49 | //注册成功的缓存起来
50 | requestMappings.put(pluginInfo.getId(), pluginRequestMappings);
51 | } catch (Exception ex) {
52 | log.error("register controller error", ex);
53 | }
54 | }
55 |
56 | @Override
57 | public void unRegister(ApplicationContext plugin, PluginInfo pluginInfo) {
58 | Set requestMappingInfoSet = requestMappings.get(pluginInfo.getId());
59 | if (requestMappingInfoSet != null) {
60 | requestMappingInfoSet.forEach(this::unRegisterController);
61 | }
62 | //删除RequestMappingHandlerAdapter的缓存,否则不销毁class
63 | RequestMappingHandlerAdapter requestMappingHandlerAdapter = main.getBean(RequestMappingHandlerAdapter.class);
64 |
65 | //销毁单例Bean
66 | Map controllerBeans = plugin.getBeansWithAnnotation(Controller.class);
67 | controllerBeans.forEach((name, bean) -> {
68 | ((Map)ReflectUtil.getFieldValue(requestMappingHandlerAdapter, "sessionAttributesHandlerCache")).remove(bean.getClass());
69 | ((Map)ReflectUtil.getFieldValue(requestMappingHandlerAdapter, "initBinderCache")).remove(bean.getClass());
70 | ((Map)ReflectUtil.getFieldValue(requestMappingHandlerAdapter, "modelAttributeCache")).remove(bean.getClass());
71 | ((DefaultListableBeanFactory)((AnnotationConfigApplicationContext)plugin).getBeanFactory()).destroySingleton(name);
72 | });
73 | requestMappings.remove(pluginInfo.getId());
74 | }
75 |
76 |
77 | private void printRegisterSuccessController(PluginInfo pluginInfo, Set requestMappingInfos) {
78 | requestMappingInfos.forEach(requestMappingInfo -> log.info("插件{}注册接口{}", pluginInfo.getId(), requestMappingInfo));
79 | }
80 |
81 |
82 | private Set registerController(Object bean) {
83 | Set requestMappingInfos = new HashSet<>();
84 | Method[] methods = bean.getClass().getDeclaredMethods();
85 | for (Method method : methods) {
86 | if (DeployUtils.isHaveRequestMapping(method)) {
87 | try {
88 | //解释接口参数
89 | RequestMappingInfo requestMappingInfo = (RequestMappingInfo)
90 | Objects.requireNonNull(getMappingForMethod()).invoke(getRequestMappingHandlerMapping(), method, bean.getClass());
91 | //注册接口
92 | getRequestMappingHandlerMapping().registerMapping(requestMappingInfo, bean , method);
93 | requestMappingInfos.add(requestMappingInfo);
94 | } catch (Exception e){
95 | log.error("接口注册异常", e);
96 | }
97 | }
98 | }
99 | return requestMappingInfos;
100 | }
101 |
102 | private void unRegisterController(RequestMappingInfo requestMappingInfo) {
103 | Map handlerMethodMap = getRequestMappingHandlerMapping().getHandlerMethods();
104 | HandlerMethod handlerMethod = handlerMethodMap.get(requestMappingInfo);
105 | //取消方法Bean的对象引用
106 | ReflectUtil.setFieldValue(handlerMethod, "bean", new Object());
107 | ReflectUtil.setFieldValue(handlerMethod, "beanType", Object.class);
108 | //取消方法的映射
109 | getRequestMappingHandlerMapping().unregisterMapping(requestMappingInfo);
110 | }
111 |
112 | private Method getMappingForMethod() {
113 | try {
114 | Method method = ReflectUtils.findDeclaredMethod(getRequestMappingHandlerMapping().getClass(), "getMappingForMethod", new Class[] { Method.class, Class.class });
115 | method.setAccessible(true);
116 | return method;
117 | } catch (Exception ex) {
118 | log.error("反射获取detectHandlerMethods异常", ex);
119 | }
120 | return null;
121 | }
122 |
123 | private RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
124 | return (RequestMappingHandlerMapping) main.getBean("requestMappingHandlerMapping");
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/spring-hot-plugin-core/src/main/java/vip/aliali/spring/plugin/register/RegisterScheduled.java:
--------------------------------------------------------------------------------
1 | package vip.aliali.spring.plugin.register;
2 |
3 | import vip.aliali.spring.plugin.PluginInfo;
4 | import org.springframework.context.ApplicationContext;
5 | import org.springframework.context.annotation.AnnotationConfigApplicationContext;
6 | import org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor;
7 | import org.springframework.scheduling.config.TaskManagementConfigUtils;
8 |
9 | /**
10 | * 定时任务控制程序
11 | */
12 | public class RegisterScheduled extends AbstractRegister {
13 |
14 | public RegisterScheduled(ApplicationContext main) {
15 | super(main);
16 | }
17 |
18 | @Override
19 | public void refreshBeforeRegister(ApplicationContext plugin, PluginInfo pluginInfo) {
20 | if (plugin instanceof AnnotationConfigApplicationContext) {
21 | ((AnnotationConfigApplicationContext)plugin).registerBean(TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME, ScheduledAnnotationBeanPostProcessor.class);
22 | }
23 | }
24 |
25 | @Override
26 | public void unRegister(ApplicationContext plugin, PluginInfo pluginInfo) {
27 | Object bean = plugin.getBean(TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME);
28 | ((ScheduledAnnotationBeanPostProcessor)bean).destroy();
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/spring-hot-plugin-core/src/main/resources/META-INF/spring-configuration-metadata.json:
--------------------------------------------------------------------------------
1 | {
2 | "groups": [
3 | {
4 | "name": "plugin",
5 | "type": "csdn.itsaysay.plugin.PluginAutoConfiguration",
6 | "sourceType": "csdn.itsaysay.plugin.PluginAutoConfiguration"
7 | }
8 | ],
9 | "properties": [
10 | {
11 | "name": "plugin.enable",
12 | "type": "java.lang.Boolean",
13 | "sourceType": "csdn.itsaysay.plugin.PluginAutoConfiguration",
14 | "description": "是否启用插件功能",
15 | "defaultValue": true
16 | },
17 | {
18 | "name": "plugin.runMode",
19 | "type": "java.lang.String",
20 | "sourceType": "csdn.itsaysay.plugin.PluginAutoConfiguration",
21 | "description": "运行模式. 开发环境: dev; 生产/部署 环境: prod",
22 | "defaultValue": "dev"
23 | },
24 | {
25 | "name": "plugin.pluginPath",
26 | "type": "java.lang.String",
27 | "sourceType": "csdn.itsaysay.plugin.PluginAutoConfiguration",
28 | "description": "插件的路径. 开发环境下配置为插件模块上级目录; 生产环境下配置到插件jar包存放目录。建议配置绝对路径",
29 | "defaultValue": "~/plugins/"
30 | },
31 | {
32 | "name": "plugin.backupPath",
33 | "type": "java.lang.String",
34 | "sourceType": "csdn.itsaysay.plugin.PluginAutoConfiguration",
35 | "description": "卸载插件后, 备份插件的目录"
36 | },
37 | {
38 | "name": "plugin.basePackage",
39 | "type": "java.lang.String",
40 | "sourceType": "csdn.itsaysay.plugin.PluginAutoConfiguration",
41 | "description": "插件扫描包路径,使用统一包路径,默认:csdn.itsaysay.plugin"
42 | }
43 | ]
44 | }
--------------------------------------------------------------------------------
/spring-hot-plugin-core/src/main/resources/META-INF/spring.factories:
--------------------------------------------------------------------------------
1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
2 | vip.aliali.spring.plugin.PluginStarter
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo-2/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | !.mvn/wrapper/maven-wrapper.jar
3 | !**/src/main/**/target/
4 | !**/src/test/**/target/
5 |
6 | ### IntelliJ IDEA ###
7 | .idea/modules.xml
8 | .idea/jarRepositories.xml
9 | .idea/compiler.xml
10 | .idea/libraries/
11 | *.iws
12 | *.iml
13 | *.ipr
14 |
15 | ### Eclipse ###
16 | .apt_generated
17 | .classpath
18 | .factorypath
19 | .project
20 | .settings
21 | .springBeans
22 | .sts4-cache
23 |
24 | ### NetBeans ###
25 | /nbproject/private/
26 | /nbbuild/
27 | /dist/
28 | /nbdist/
29 | /.nb-gradle/
30 | build/
31 | !**/src/main/**/build/
32 | !**/src/test/**/build/
33 |
34 | ### VS Code ###
35 | .vscode/
36 |
37 | ### Mac OS ###
38 | .DS_Store
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo-2/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | csdn.itsaysay.plugin
8 | spring-hot-plugin-example
9 | 1.1
10 |
11 |
12 | plugin-demo-2
13 | 测试插件Demo2
14 |
15 |
16 | 8
17 | 8
18 | UTF-8
19 |
20 |
21 |
22 |
23 | csdn.itsaysay.plugin
24 | spring-hot-plugin-demo
25 | ${project.version}
26 | provided
27 | true
28 |
29 |
30 |
31 | com.alibaba
32 | fastjson
33 | 2.0.39
34 |
35 |
36 |
37 | com.baomidou
38 | mybatis-plus-boot-starter
39 | 3.5.7
40 |
41 |
42 |
43 |
44 | com.hik
45 | examples
46 | 1.0
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | vip.aliali.spring
55 | spring-hot-plugin-maven
56 | ${project.version}
57 |
58 |
59 | repackage
60 | package
61 |
62 | repackage
63 |
64 |
65 |
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo-2/src/main/java/csdn/itsaysay/demo/plugin2/IndexController.java:
--------------------------------------------------------------------------------
1 | package csdn.itsaysay.demo.plugin2;
2 |
3 | import com.alibaba.fastjson.JSON;
4 | import csdn.itsaysay.demo.plugin2.bean.Person;
5 | import csdn.itsaysay.demo.plugin2.bean.User;
6 | import csdn.itsaysay.demo.plugin2.service.TestService;
7 | import lombok.extern.slf4j.Slf4j;
8 | import org.springframework.web.bind.annotation.GetMapping;
9 | import org.springframework.web.bind.annotation.PathVariable;
10 | import org.springframework.web.bind.annotation.RequestMapping;
11 | import org.springframework.web.bind.annotation.RestController;
12 |
13 | import javax.annotation.Resource;
14 |
15 | @Slf4j
16 | @RestController
17 | @RequestMapping("/demo2")
18 | public class IndexController {
19 |
20 | @Resource
21 | private TestService testService;
22 |
23 | @GetMapping({"/hello"})
24 | public String hello() {
25 | return "Hello itsaysay!";
26 | }
27 |
28 | @GetMapping({"/hello/name"})
29 | public Person helloName() {
30 | Person person = new Person();
31 | person.setName("Human");
32 | String json = JSON.toJSONString(person);
33 | log.info(json);
34 | return person;
35 | }
36 |
37 |
38 | @GetMapping("/mybatis/getUser/{id}")
39 | public User getUser(@PathVariable("id") Integer id) {
40 | return testService.getUser(id);
41 | }
42 |
43 | @GetMapping("/mybatis/getUserXml/{id}")
44 | public User getUserXml(@PathVariable("id") Integer id) {
45 | return testService.getUserXml(id);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo-2/src/main/java/csdn/itsaysay/demo/plugin2/bean/Person.java:
--------------------------------------------------------------------------------
1 | package csdn.itsaysay.demo.plugin2.bean;
2 |
3 |
4 | import lombok.Data;
5 |
6 | @Data
7 | public class Person {
8 |
9 | private String name;
10 | }
11 |
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo-2/src/main/java/csdn/itsaysay/demo/plugin2/bean/User.java:
--------------------------------------------------------------------------------
1 | package csdn.itsaysay.demo.plugin2.bean;
2 |
3 | import com.baomidou.mybatisplus.annotation.TableId;
4 | import com.baomidou.mybatisplus.annotation.TableName;
5 | import lombok.Data;
6 |
7 | @Data
8 | @TableName("usertb")
9 | public class User {
10 |
11 | @TableId
12 | private Integer id;
13 |
14 | private String name;
15 |
16 | private Integer age;
17 | }
18 |
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo-2/src/main/java/csdn/itsaysay/demo/plugin2/mapper/TestMapper.java:
--------------------------------------------------------------------------------
1 | package csdn.itsaysay.demo.plugin2.mapper;
2 |
3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper;
4 | import csdn.itsaysay.demo.plugin2.bean.User;
5 | import org.apache.ibatis.annotations.Mapper;
6 | import org.apache.ibatis.annotations.Param;
7 |
8 | @Mapper
9 | public interface TestMapper extends BaseMapper {
10 |
11 | User selectByIdXml(@Param("id") Integer id);
12 | }
13 |
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo-2/src/main/java/csdn/itsaysay/demo/plugin2/mapper/xml/TestMapper.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 | select * from usertb where id = #{id}
8 |
9 |
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo-2/src/main/java/csdn/itsaysay/demo/plugin2/service/TestService.java:
--------------------------------------------------------------------------------
1 | package csdn.itsaysay.demo.plugin2.service;
2 |
3 | import csdn.itsaysay.demo.plugin2.bean.User;
4 |
5 | public interface TestService {
6 | User getUser(Integer id);
7 |
8 | User getUserXml(Integer id);
9 | }
10 |
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo-2/src/main/java/csdn/itsaysay/demo/plugin2/service/impl/TestServiceImpl.java:
--------------------------------------------------------------------------------
1 | package csdn.itsaysay.demo.plugin2.service.impl;
2 |
3 | import csdn.itsaysay.demo.plugin2.bean.User;
4 | import csdn.itsaysay.demo.plugin2.service.TestService;
5 | import csdn.itsaysay.demo.plugin2.mapper.TestMapper;
6 | import org.springframework.stereotype.Service;
7 |
8 | import javax.annotation.Resource;
9 |
10 | @Service
11 | public class TestServiceImpl implements TestService {
12 |
13 | @Resource
14 | private TestMapper testMapper;
15 |
16 |
17 | @Override
18 | public User getUser(Integer id) {
19 | return testMapper.selectById(id);
20 | }
21 |
22 | @Override
23 | public User getUserXml(Integer id) {
24 | return testMapper.selectByIdXml(id);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo-2/src/main/java/csdn/itsaysay/demo/plugin2/task/TaskDemo.java:
--------------------------------------------------------------------------------
1 | package csdn.itsaysay.demo.plugin2.task;
2 |
3 | import lombok.extern.slf4j.Slf4j;
4 | import org.springframework.scheduling.annotation.Scheduled;
5 | import org.springframework.stereotype.Component;
6 |
7 | /**
8 | * 定时任务
9 | */
10 | @Slf4j
11 | @Component
12 | public class TaskDemo {
13 |
14 | @Scheduled(cron = "0/1 * * * * ?")
15 | public void task() {
16 | log.info("demo2 task starting");
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo-mybatis/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | !.mvn/wrapper/maven-wrapper.jar
3 | !**/src/main/**/target/
4 | !**/src/test/**/target/
5 |
6 | ### IntelliJ IDEA ###
7 | .idea/modules.xml
8 | .idea/jarRepositories.xml
9 | .idea/compiler.xml
10 | .idea/libraries/
11 | *.iws
12 | *.iml
13 | *.ipr
14 |
15 | ### Eclipse ###
16 | .apt_generated
17 | .classpath
18 | .factorypath
19 | .project
20 | .settings
21 | .springBeans
22 | .sts4-cache
23 |
24 | ### NetBeans ###
25 | /nbproject/private/
26 | /nbbuild/
27 | /dist/
28 | /nbdist/
29 | /.nb-gradle/
30 | build/
31 | !**/src/main/**/build/
32 | !**/src/test/**/build/
33 |
34 | ### VS Code ###
35 | .vscode/
36 |
37 | ### Mac OS ###
38 | .DS_Store
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo-mybatis/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | csdn.itsaysay.plugin
8 | spring-hot-plugin-example
9 | 1.1
10 |
11 |
12 | plugin-demo-mybatis
13 | 测试插件Demo-mybatis
14 |
15 |
16 | 8
17 | 8
18 | UTF-8
19 |
20 |
21 |
22 |
23 | csdn.itsaysay.plugin
24 | spring-hot-plugin-demo
25 | ${project.version}
26 | provided
27 | true
28 |
29 |
30 |
31 | com.alibaba
32 | fastjson
33 | 2.0.39
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | src/main/java
43 |
44 | **/*.xml
45 |
46 |
47 |
48 | src/main/resources
49 |
50 | **/*
51 |
52 |
53 |
54 |
55 |
56 | vip.aliali.spring
57 | spring-hot-plugin-maven
58 | ${project.version}
59 |
60 |
61 | repackage
62 | package
63 |
64 | repackage
65 |
66 |
67 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo-mybatis/src/main/java/csdn/itsaysay/demo/mybatis/IndexController.java:
--------------------------------------------------------------------------------
1 | package csdn.itsaysay.demo.mybatis;
2 |
3 | import csdn.itsaysay.demo.mybatis.bean.User;
4 | import csdn.itsaysay.demo.mybatis.service.TestService;
5 | import lombok.extern.slf4j.Slf4j;
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.web.bind.annotation.DeleteMapping;
8 | import org.springframework.web.bind.annotation.GetMapping;
9 | import org.springframework.web.bind.annotation.PathVariable;
10 | import org.springframework.web.bind.annotation.RestController;
11 |
12 | @Slf4j
13 | @RestController
14 | public class IndexController {
15 |
16 | @Autowired
17 | private TestService testService;
18 |
19 | @GetMapping("/mybatis/getUser/{id}")
20 | public User getUser(@PathVariable("id") Integer id) {
21 | return testService.getUser(id);
22 | }
23 |
24 | @GetMapping("/mybatis/getUserXml/{id}")
25 | public User getUserXml(@PathVariable("id") Integer id) {
26 | return testService.getUserXml(id);
27 | }
28 |
29 | @GetMapping("/mybatis/insert")
30 | public Integer insert() {
31 | return testService.insert();
32 | }
33 |
34 | @DeleteMapping("/mybatis/delete/{id}")
35 | public void delete(@PathVariable("id") Integer id) {
36 | testService.delete(id);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo-mybatis/src/main/java/csdn/itsaysay/demo/mybatis/bean/User.java:
--------------------------------------------------------------------------------
1 | package csdn.itsaysay.demo.mybatis.bean;
2 |
3 | import lombok.Data;
4 |
5 | @Data
6 | public class User {
7 |
8 | private Integer id;
9 |
10 | private String name;
11 |
12 | private Integer age;
13 | }
14 |
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo-mybatis/src/main/java/csdn/itsaysay/demo/mybatis/mapper/TestMapper.java:
--------------------------------------------------------------------------------
1 | package csdn.itsaysay.demo.mybatis.mapper;
2 |
3 | import csdn.itsaysay.demo.mybatis.bean.User;
4 | import org.apache.ibatis.annotations.*;
5 |
6 | @Mapper
7 | public interface TestMapper {
8 |
9 | @Select("select * from usertb where id = #{idd}")
10 | User selectById(@Param("idd") Integer idd);
11 |
12 | User selectByIdXml(@Param("id") Integer id);
13 |
14 | @Insert("insert into usertb(name,age) values(#{user.name},#{user.age})")
15 | @Options(useGeneratedKeys = true, keyProperty = "id")
16 | int insert(@Param("user") User user);
17 |
18 | @Delete("delete from usertb where id = #{id}")
19 | void deleteById(@Param("id") Integer id);
20 | }
21 |
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo-mybatis/src/main/java/csdn/itsaysay/demo/mybatis/mapper/TestMapper.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 | select * from usertb where id = #{id}
8 |
9 |
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo-mybatis/src/main/java/csdn/itsaysay/demo/mybatis/service/TestService.java:
--------------------------------------------------------------------------------
1 | package csdn.itsaysay.demo.mybatis.service;
2 |
3 | import csdn.itsaysay.demo.mybatis.bean.User;
4 |
5 | public interface TestService {
6 | User getUser(Integer id);
7 |
8 | User getUserXml(Integer id);
9 |
10 | Integer insert();
11 |
12 | void delete(Integer id);
13 | }
14 |
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo-mybatis/src/main/java/csdn/itsaysay/demo/mybatis/service/impl/TestServiceImpl.java:
--------------------------------------------------------------------------------
1 | package csdn.itsaysay.demo.mybatis.service.impl;
2 |
3 | import csdn.itsaysay.demo.mybatis.bean.User;
4 | import csdn.itsaysay.demo.mybatis.mapper.TestMapper;
5 | import csdn.itsaysay.demo.mybatis.service.TestService;
6 | import org.springframework.stereotype.Service;
7 |
8 | import javax.annotation.Resource;
9 |
10 | @Service
11 | public class TestServiceImpl implements TestService {
12 |
13 | @Resource
14 | private TestMapper testMapper;
15 |
16 | @Override
17 | public User getUser(Integer id) {
18 | return testMapper.selectById(id);
19 | }
20 |
21 | @Override
22 | public User getUserXml(Integer id) {
23 | return testMapper.selectByIdXml(id);
24 | }
25 |
26 | @Override
27 | public Integer insert() {
28 | User user = new User();
29 | user.setName("test");
30 | user.setAge(18);
31 | testMapper.insert(user);
32 | return user.getId();
33 | }
34 |
35 | @Override
36 | public void delete(Integer id) {
37 | testMapper.deleteById(id);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | !.mvn/wrapper/maven-wrapper.jar
3 | !**/src/main/**/target/
4 | !**/src/test/**/target/
5 |
6 | ### IntelliJ IDEA ###
7 | .idea/modules.xml
8 | .idea/jarRepositories.xml
9 | .idea/compiler.xml
10 | .idea/libraries/
11 | *.iws
12 | *.iml
13 | *.ipr
14 |
15 | ### Eclipse ###
16 | .apt_generated
17 | .classpath
18 | .factorypath
19 | .project
20 | .settings
21 | .springBeans
22 | .sts4-cache
23 |
24 | ### NetBeans ###
25 | /nbproject/private/
26 | /nbbuild/
27 | /dist/
28 | /nbdist/
29 | /.nb-gradle/
30 | build/
31 | !**/src/main/**/build/
32 | !**/src/test/**/build/
33 |
34 | ### VS Code ###
35 | .vscode/
36 |
37 | ### Mac OS ###
38 | .DS_Store
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | csdn.itsaysay.plugin
8 | spring-hot-plugin-example
9 | 1.1
10 |
11 |
12 | plugin-demo
13 | 测试插件Demo
14 |
15 |
16 | 8
17 | 8
18 | UTF-8
19 |
20 |
21 |
22 |
23 | csdn.itsaysay.plugin
24 | spring-hot-plugin-demo
25 | ${project.version}
26 | provided
27 | true
28 |
29 |
30 |
31 | com.alibaba
32 | fastjson
33 | 2.0.39
34 |
35 |
36 |
37 | com.baomidou
38 | mybatis-plus-boot-starter
39 | 3.5.7
40 | provided
41 |
42 |
43 |
44 |
45 | com.hik
46 | examples
47 | 1.0
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | vip.aliali.spring
56 | spring-hot-plugin-maven
57 | ${project.version}
58 |
59 |
60 | repackage
61 | package
62 |
63 | repackage
64 |
65 |
66 |
67 |
68 |
69 |
70 |
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/java/csdn/itsaysay/demo/plugin/IndexController.java:
--------------------------------------------------------------------------------
1 | package csdn.itsaysay.demo.plugin;
2 |
3 | import com.alibaba.fastjson.JSON;
4 | import com.sun.jna.Native;
5 | import csdn.itsaysay.demo.plugin.bean.Person;
6 | import csdn.itsaysay.demo.plugin.bean.User;
7 | import csdn.itsaysay.demo.plugin.hik.HCNetSDK;
8 | import csdn.itsaysay.demo.plugin.service.TestService;
9 | import lombok.extern.slf4j.Slf4j;
10 | import org.springframework.web.bind.annotation.GetMapping;
11 | import org.springframework.web.bind.annotation.PathVariable;
12 | import org.springframework.web.bind.annotation.RestController;
13 |
14 | import javax.annotation.Resource;
15 |
16 | @Slf4j
17 | @RestController
18 | public class IndexController {
19 |
20 | @Resource
21 | private TestService testService;
22 |
23 | @GetMapping({"/hello"})
24 | public String hello() {
25 | return "Hello itsaysay!";
26 | }
27 |
28 | @GetMapping({"/hello/name"})
29 | public Person helloName() {
30 | Person person = new Person();
31 | person.setName("Human");
32 | String json = JSON.toJSONString(person);
33 | log.info(json);
34 | return person;
35 | }
36 |
37 | @GetMapping("/hik/sdk/init")
38 | public Boolean hikSDKInit() {
39 | //海康的SDK dll依赖了其他的dll,需要指定目录,放插件包没用
40 | //只有一个dll的sdk,不需要,可以放在插件包内
41 | //这是java native的限制
42 | HCNetSDK hCNetSDK = (HCNetSDK) Native.loadLibrary("D:\\javaprojects\\spring-hot-plugin\\spring-hot-plugin-example\\plugin-demo\\target\\classes\\win32-x86-64\\HCNetSDK.dll", HCNetSDK.class);
43 | if (hCNetSDK == null) {
44 | return false;
45 | }
46 | return true;
47 | }
48 |
49 | @GetMapping("/mp/getUser/{id}")
50 | public User getUser(@PathVariable("id") Integer id) {
51 | return testService.getUser(id);
52 | }
53 |
54 | @GetMapping("/mp/getUserXml/{id}")
55 | public User getUserXml(@PathVariable("id") Integer id) {
56 | return testService.getUserXml(id);
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/java/csdn/itsaysay/demo/plugin/bean/Person.java:
--------------------------------------------------------------------------------
1 | package csdn.itsaysay.demo.plugin.bean;
2 |
3 |
4 | import lombok.Data;
5 |
6 | @Data
7 | public class Person {
8 |
9 | private String name;
10 | }
11 |
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/java/csdn/itsaysay/demo/plugin/bean/User.java:
--------------------------------------------------------------------------------
1 | package csdn.itsaysay.demo.plugin.bean;
2 |
3 | import com.baomidou.mybatisplus.annotation.TableId;
4 | import com.baomidou.mybatisplus.annotation.TableName;
5 | import lombok.Data;
6 |
7 | @Data
8 | @TableName("usertb")
9 | public class User {
10 |
11 | @TableId
12 | private Integer id;
13 |
14 | private String name;
15 |
16 | private Integer age;
17 | }
18 |
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/java/csdn/itsaysay/demo/plugin/mapper/TestMapper.java:
--------------------------------------------------------------------------------
1 | package csdn.itsaysay.demo.plugin.mapper;
2 |
3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper;
4 | import csdn.itsaysay.demo.plugin.bean.User;
5 | import org.apache.ibatis.annotations.Mapper;
6 | import org.apache.ibatis.annotations.Param;
7 |
8 | @Mapper
9 | public interface TestMapper extends BaseMapper {
10 |
11 | User selectByIdXml(@Param("id") Integer id);
12 | }
13 |
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/java/csdn/itsaysay/demo/plugin/mapper/TestMapper2.java:
--------------------------------------------------------------------------------
1 | package csdn.itsaysay.demo.plugin.mapper;
2 |
3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper;
4 | import csdn.itsaysay.demo.plugin.bean.User;
5 | import org.apache.ibatis.annotations.Mapper;
6 |
7 | @Mapper
8 | public interface TestMapper2 extends BaseMapper {
9 | }
10 |
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/java/csdn/itsaysay/demo/plugin/service/TestService.java:
--------------------------------------------------------------------------------
1 | package csdn.itsaysay.demo.plugin.service;
2 |
3 | import csdn.itsaysay.demo.plugin.bean.User;
4 |
5 | public interface TestService {
6 | User getUser(Integer id);
7 |
8 | User getUserXml(Integer id);
9 | }
10 |
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/java/csdn/itsaysay/demo/plugin/service/impl/TestServiceImpl.java:
--------------------------------------------------------------------------------
1 | package csdn.itsaysay.demo.plugin.service.impl;
2 |
3 | import csdn.itsaysay.demo.plugin.bean.User;
4 | import csdn.itsaysay.demo.plugin.mapper.TestMapper;
5 | import csdn.itsaysay.demo.plugin.mapper.TestMapper2;
6 | import csdn.itsaysay.demo.plugin.service.TestService;
7 | import org.springframework.stereotype.Service;
8 |
9 | import javax.annotation.Resource;
10 |
11 | @Service
12 | public class TestServiceImpl implements TestService {
13 |
14 | @Resource
15 | private TestMapper testMapper;
16 | @Resource
17 | private TestMapper2 testMapper2;
18 |
19 |
20 | @Override
21 | public User getUser(Integer id) {
22 | return testMapper.selectById(id);
23 | }
24 |
25 | @Override
26 | public User getUserXml(Integer id) {
27 | return testMapper.selectByIdXml(id);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/java/csdn/itsaysay/demo/plugin/task/TaskDemo.java:
--------------------------------------------------------------------------------
1 | package csdn.itsaysay.demo.plugin.task;
2 |
3 | import lombok.extern.slf4j.Slf4j;
4 | import org.springframework.scheduling.annotation.Scheduled;
5 | import org.springframework.stereotype.Component;
6 |
7 | /**
8 | * 定时任务
9 | */
10 | @Slf4j
11 | @Component
12 | public class TaskDemo {
13 |
14 | @Scheduled(cron = "0/1 * * * * ?")
15 | public void task() {
16 | log.info("task starting");
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/resources/csdn/itsaysay/demo/plugin/mapper/TestMapper.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 | select * from usertb where id = #{id}
8 |
9 |
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/resources/linux-x86-64/libXMTQRCode.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jujunchen/spring-hot-plugin/5c066a4030b3fe269583fc2f24320821c2883e75/spring-hot-plugin-example/plugin-demo/src/main/resources/linux-x86-64/libXMTQRCode.so
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/AudioRender.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jujunchen/spring-hot-plugin/5c066a4030b3fe269583fc2f24320821c2883e75/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/AudioRender.dll
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/GdiPlus.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jujunchen/spring-hot-plugin/5c066a4030b3fe269583fc2f24320821c2883e75/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/GdiPlus.dll
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/GdiPlus.lib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jujunchen/spring-hot-plugin/5c066a4030b3fe269583fc2f24320821c2883e75/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/GdiPlus.lib
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/HCCore.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jujunchen/spring-hot-plugin/5c066a4030b3fe269583fc2f24320821c2883e75/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/HCCore.dll
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/HCCore.lib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jujunchen/spring-hot-plugin/5c066a4030b3fe269583fc2f24320821c2883e75/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/HCCore.lib
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/HCNetSDK.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jujunchen/spring-hot-plugin/5c066a4030b3fe269583fc2f24320821c2883e75/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/HCNetSDK.dll
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/HCNetSDK.lib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jujunchen/spring-hot-plugin/5c066a4030b3fe269583fc2f24320821c2883e75/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/HCNetSDK.lib
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/HCNetSDKCom/AnalyzeData.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jujunchen/spring-hot-plugin/5c066a4030b3fe269583fc2f24320821c2883e75/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/HCNetSDKCom/AnalyzeData.dll
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/HCNetSDKCom/AudioIntercom.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jujunchen/spring-hot-plugin/5c066a4030b3fe269583fc2f24320821c2883e75/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/HCNetSDKCom/AudioIntercom.dll
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/HCNetSDKCom/AudioRender.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jujunchen/spring-hot-plugin/5c066a4030b3fe269583fc2f24320821c2883e75/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/HCNetSDKCom/AudioRender.dll
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/HCNetSDKCom/HCAlarm.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jujunchen/spring-hot-plugin/5c066a4030b3fe269583fc2f24320821c2883e75/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/HCNetSDKCom/HCAlarm.dll
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/HCNetSDKCom/HCAlarm.lib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jujunchen/spring-hot-plugin/5c066a4030b3fe269583fc2f24320821c2883e75/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/HCNetSDKCom/HCAlarm.lib
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/HCNetSDKCom/HCCoreDevCfg.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jujunchen/spring-hot-plugin/5c066a4030b3fe269583fc2f24320821c2883e75/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/HCNetSDKCom/HCCoreDevCfg.dll
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/HCNetSDKCom/HCDisplay.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jujunchen/spring-hot-plugin/5c066a4030b3fe269583fc2f24320821c2883e75/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/HCNetSDKCom/HCDisplay.dll
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/HCNetSDKCom/HCGeneralCfgMgr.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jujunchen/spring-hot-plugin/5c066a4030b3fe269583fc2f24320821c2883e75/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/HCNetSDKCom/HCGeneralCfgMgr.dll
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/HCNetSDKCom/HCGeneralCfgMgr.lib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jujunchen/spring-hot-plugin/5c066a4030b3fe269583fc2f24320821c2883e75/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/HCNetSDKCom/HCGeneralCfgMgr.lib
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/HCNetSDKCom/HCIndustry.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jujunchen/spring-hot-plugin/5c066a4030b3fe269583fc2f24320821c2883e75/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/HCNetSDKCom/HCIndustry.dll
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/HCNetSDKCom/HCPlayBack.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jujunchen/spring-hot-plugin/5c066a4030b3fe269583fc2f24320821c2883e75/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/HCNetSDKCom/HCPlayBack.dll
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/HCNetSDKCom/HCPreview.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jujunchen/spring-hot-plugin/5c066a4030b3fe269583fc2f24320821c2883e75/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/HCNetSDKCom/HCPreview.dll
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/HCNetSDKCom/HCPreview.lib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jujunchen/spring-hot-plugin/5c066a4030b3fe269583fc2f24320821c2883e75/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/HCNetSDKCom/HCPreview.lib
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/HCNetSDKCom/HCVoiceTalk.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jujunchen/spring-hot-plugin/5c066a4030b3fe269583fc2f24320821c2883e75/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/HCNetSDKCom/HCVoiceTalk.dll
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/HCNetSDKCom/OpenAL32.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jujunchen/spring-hot-plugin/5c066a4030b3fe269583fc2f24320821c2883e75/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/HCNetSDKCom/OpenAL32.dll
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/HCNetSDKCom/StreamTransClient.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jujunchen/spring-hot-plugin/5c066a4030b3fe269583fc2f24320821c2883e75/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/HCNetSDKCom/StreamTransClient.dll
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/HCNetSDKCom/SystemTransform.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jujunchen/spring-hot-plugin/5c066a4030b3fe269583fc2f24320821c2883e75/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/HCNetSDKCom/SystemTransform.dll
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/HCNetSDKCom/libiconv2.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jujunchen/spring-hot-plugin/5c066a4030b3fe269583fc2f24320821c2883e75/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/HCNetSDKCom/libiconv2.dll
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/HXVA.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jujunchen/spring-hot-plugin/5c066a4030b3fe269583fc2f24320821c2883e75/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/HXVA.dll
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/HmMerge.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jujunchen/spring-hot-plugin/5c066a4030b3fe269583fc2f24320821c2883e75/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/HmMerge.dll
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/LocalSensorAdd.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jujunchen/spring-hot-plugin/5c066a4030b3fe269583fc2f24320821c2883e75/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/LocalSensorAdd.dat
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/MP_Render.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jujunchen/spring-hot-plugin/5c066a4030b3fe269583fc2f24320821c2883e75/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/MP_Render.dll
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/NPQos.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jujunchen/spring-hot-plugin/5c066a4030b3fe269583fc2f24320821c2883e75/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/NPQos.dll
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/OpenAL32.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jujunchen/spring-hot-plugin/5c066a4030b3fe269583fc2f24320821c2883e75/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/OpenAL32.dll
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/PlayCtrl.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jujunchen/spring-hot-plugin/5c066a4030b3fe269583fc2f24320821c2883e75/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/PlayCtrl.dll
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/PlayCtrl.lib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jujunchen/spring-hot-plugin/5c066a4030b3fe269583fc2f24320821c2883e75/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/PlayCtrl.lib
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/SuperRender.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jujunchen/spring-hot-plugin/5c066a4030b3fe269583fc2f24320821c2883e75/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/SuperRender.dll
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/YUVProcess.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jujunchen/spring-hot-plugin/5c066a4030b3fe269583fc2f24320821c2883e75/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/YUVProcess.dll
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/examples.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jujunchen/spring-hot-plugin/5c066a4030b3fe269583fc2f24320821c2883e75/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/examples.jar
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/hlog.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jujunchen/spring-hot-plugin/5c066a4030b3fe269583fc2f24320821c2883e75/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/hlog.dll
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/hpr.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jujunchen/spring-hot-plugin/5c066a4030b3fe269583fc2f24320821c2883e75/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/hpr.dll
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/libcrypto-1_1-x64.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jujunchen/spring-hot-plugin/5c066a4030b3fe269583fc2f24320821c2883e75/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/libcrypto-1_1-x64.dll
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/libmmd.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jujunchen/spring-hot-plugin/5c066a4030b3fe269583fc2f24320821c2883e75/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/libmmd.dll
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/libssl-1_1-x64.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jujunchen/spring-hot-plugin/5c066a4030b3fe269583fc2f24320821c2883e75/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/libssl-1_1-x64.dll
--------------------------------------------------------------------------------
/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/zlib1.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jujunchen/spring-hot-plugin/5c066a4030b3fe269583fc2f24320821c2883e75/spring-hot-plugin-example/plugin-demo/src/main/resources/win32-x86-64/zlib1.dll
--------------------------------------------------------------------------------
/spring-hot-plugin-example/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 | csdn.itsaysay.plugin
6 | spring-hot-plugin-example
7 | 1.1
8 | pom
9 |
10 |
11 | spring-hot-plugin-demo
12 | plugin-demo
13 | plugin-demo-2
14 | plugin-demo-mybatis
15 | spring-hot-plugin-admin
16 | spring-hot-plugin-task-demo
17 |
18 |
19 |
20 | UTF-8
21 | 1.8
22 | UTF-8
23 | ${java.version}
24 | ${java.version}
25 |
26 | 1.18.20
27 | 2.7.18
28 |
29 |
30 |
31 |
32 |
33 |
34 | org.projectlombok
35 | lombok
36 | provided
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | org.springframework.boot
45 | spring-boot-dependencies
46 | ${springboot.version}
47 | pom
48 | import
49 |
50 |
51 |
52 |
53 |
54 |
55 | aliyun-repository
56 | aliyun nexus
57 | https://maven.aliyun.com/nexus/content/groups/public/
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/spring-hot-plugin-example/spring-hot-plugin-admin/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | !.mvn/wrapper/maven-wrapper.jar
3 | !**/src/main/**/target/
4 | !**/src/test/**/target/
5 |
6 | ### IntelliJ IDEA ###
7 | .idea/modules.xml
8 | .idea/jarRepositories.xml
9 | .idea/compiler.xml
10 | .idea/libraries/
11 | *.iws
12 | *.iml
13 | *.ipr
14 |
15 | ### Eclipse ###
16 | .apt_generated
17 | .classpath
18 | .factorypath
19 | .project
20 | .settings
21 | .springBeans
22 | .sts4-cache
23 |
24 | ### NetBeans ###
25 | /nbproject/private/
26 | /nbbuild/
27 | /dist/
28 | /nbdist/
29 | /.nb-gradle/
30 | build/
31 | !**/src/main/**/build/
32 | !**/src/test/**/build/
33 |
34 | ### VS Code ###
35 | .vscode/
36 |
37 | ### Mac OS ###
38 | .DS_Store
--------------------------------------------------------------------------------
/spring-hot-plugin-example/spring-hot-plugin-admin/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | csdn.itsaysay.plugin
8 | spring-hot-plugin-example
9 | 1.1
10 |
11 |
12 | spring-hot-plugin-admin
13 | 监控-测试插件主程序
14 |
15 |
16 | 8
17 | 8
18 | UTF-8
19 | 2.4.5
20 |
21 |
22 |
23 |
24 | org.springframework.boot
25 | spring-boot-starter-web
26 |
27 |
28 |
29 | de.codecentric
30 | spring-boot-admin-starter-server
31 | 2.6.11
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/spring-hot-plugin-example/spring-hot-plugin-admin/src/main/java/csdn/itsaysay/main/admin/AdminApplication.java:
--------------------------------------------------------------------------------
1 | package csdn.itsaysay.main.admin;
2 |
3 | import de.codecentric.boot.admin.server.config.EnableAdminServer;
4 | import org.springframework.boot.SpringApplication;
5 | import org.springframework.boot.autoconfigure.SpringBootApplication;
6 |
7 | @SpringBootApplication
8 | @EnableAdminServer
9 | public class AdminApplication {
10 |
11 | public static void main(String[] args) {
12 | SpringApplication.run(AdminApplication.class, args);
13 | }
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/spring-hot-plugin-example/spring-hot-plugin-admin/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | server:
2 | port: 8081
3 | servlet:
4 | context-path: /
5 |
--------------------------------------------------------------------------------
/spring-hot-plugin-example/spring-hot-plugin-demo/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | !.mvn/wrapper/maven-wrapper.jar
3 | !**/src/main/**/target/
4 | !**/src/test/**/target/
5 |
6 | ### IntelliJ IDEA ###
7 | .idea/modules.xml
8 | .idea/jarRepositories.xml
9 | .idea/compiler.xml
10 | .idea/libraries/
11 | *.iws
12 | *.iml
13 | *.ipr
14 |
15 | ### Eclipse ###
16 | .apt_generated
17 | .classpath
18 | .factorypath
19 | .project
20 | .settings
21 | .springBeans
22 | .sts4-cache
23 |
24 | ### NetBeans ###
25 | /nbproject/private/
26 | /nbbuild/
27 | /dist/
28 | /nbdist/
29 | /.nb-gradle/
30 | build/
31 | !**/src/main/**/build/
32 | !**/src/test/**/build/
33 |
34 | ### VS Code ###
35 | .vscode/
36 |
37 | ### Mac OS ###
38 | .DS_Store
--------------------------------------------------------------------------------
/spring-hot-plugin-example/spring-hot-plugin-demo/Dockerfile:
--------------------------------------------------------------------------------
1 | # 指定 java 环境镜像
2 | FROM openjdk:8
3 | # 维护者信息
4 | LABEL service-name=spring-hot-plugin-demo
5 | # 复制文件到容器
6 | COPY target/*.jar /application.jar
7 |
8 | RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' > /etc/timezone
9 |
10 | ENTRYPOINT ["java", "-jar", "-Xms256m", "-Xmx256m", "/application.jar"]
11 |
--------------------------------------------------------------------------------
/spring-hot-plugin-example/spring-hot-plugin-demo/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | csdn.itsaysay.plugin
8 | spring-hot-plugin-example
9 | 1.1
10 |
11 |
12 | spring-hot-plugin-demo
13 |
14 |
15 | 8
16 | 8
17 | UTF-8
18 | 2.4.5
19 |
20 |
21 |
22 |
23 | org.springframework.boot
24 | spring-boot-starter-web
25 |
26 |
27 |
28 | com.baomidou
29 | mybatis-plus-boot-starter
30 | 3.5.7
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | mysql
48 | mysql-connector-java
49 | 8.0.31
50 |
51 |
52 |
53 |
54 | de.codecentric
55 | spring-boot-admin-starter-client
56 | 2.6.11
57 |
58 |
59 |
60 |
61 |
62 | vip.aliali.spring
63 | spring-hot-plugin-core
64 | 1.1.1
65 |
66 |
67 |
68 | vip.aliali.spring
69 | spring-hot-plugin-mybatis
70 | 1.1.1
71 |
72 |
73 |
74 | com.alibaba
75 | fastjson
76 | 2.0.39
77 |
78 |
79 |
80 | net.java.dev.jna
81 | jna
82 | 5.14.0
83 |
84 |
85 |
86 |
87 |
88 |
89 | ${project.artifactId}
90 |
91 |
92 | org.springframework.boot
93 | spring-boot-maven-plugin
94 | ${spring-boot-maven-plugin.version}
95 |
96 |
97 |
98 | repackage
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
--------------------------------------------------------------------------------
/spring-hot-plugin-example/spring-hot-plugin-demo/src/main/java/csdn/itsaysay/main/DemoApplication.java:
--------------------------------------------------------------------------------
1 | package csdn.itsaysay.main;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication()
7 | public class DemoApplication {
8 |
9 | public static void main(String[] args) {
10 | SpringApplication.run(DemoApplication.class, args);
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/spring-hot-plugin-example/spring-hot-plugin-demo/src/main/java/csdn/itsaysay/main/plugin/PluginController.java:
--------------------------------------------------------------------------------
1 | package csdn.itsaysay.main.plugin;
2 |
3 | import csdn.itsaysay.main.plugin.service.PluginService;
4 | import csdn.itsaysay.main.plugin.req.PluginReq;
5 | import csdn.itsaysay.main.plugin.res.PluginRes;
6 | import org.springframework.web.bind.annotation.*;
7 | import org.springframework.web.multipart.MultipartFile;
8 |
9 | import javax.annotation.Resource;
10 | import java.util.List;
11 |
12 | @RestController
13 | @RequestMapping("/plugin")
14 | public class PluginController {
15 |
16 | @Resource
17 | PluginService pluginService;
18 |
19 | /**
20 | * 安装插件
21 | * @param file
22 | */
23 | @PostMapping("/install")
24 | public void install(@RequestParam("file") MultipartFile file) {
25 | pluginService.install(file);
26 | }
27 |
28 | /**
29 | * 卸载插件
30 | * @param pluginId
31 | */
32 | @PostMapping("/uninstall")
33 | public void uninstall(@RequestParam("pluginId") String pluginId) {
34 | pluginService.uninstall(pluginId);
35 | }
36 |
37 | /**
38 | * 启动插件
39 | * @param pluginId
40 | * @return
41 | */
42 | @PostMapping("/start")
43 | public Boolean start(@RequestParam("pluginId")String pluginId) {
44 | return pluginService.start(pluginId);
45 | }
46 |
47 | /**
48 | * 暂停插件
49 | * @param pluginId
50 | * @return
51 | */
52 | @PostMapping("/stop")
53 | public Boolean stop(@RequestParam("pluginId")String pluginId) {
54 | return pluginService.stop(pluginId);
55 | }
56 |
57 | /**
58 | * 获取插件列表
59 | * @param req
60 | * @return
61 | */
62 | @PostMapping("/list")
63 | public List list(@RequestBody PluginReq req) {
64 | return pluginService.list(req);
65 | }
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/spring-hot-plugin-example/spring-hot-plugin-demo/src/main/java/csdn/itsaysay/main/plugin/req/PluginReq.java:
--------------------------------------------------------------------------------
1 | package csdn.itsaysay.main.plugin.req;
2 |
3 | import lombok.Data;
4 |
5 | @Data
6 | public class PluginReq {
7 |
8 | private String pluginId;
9 | }
10 |
--------------------------------------------------------------------------------
/spring-hot-plugin-example/spring-hot-plugin-demo/src/main/java/csdn/itsaysay/main/plugin/res/PluginRes.java:
--------------------------------------------------------------------------------
1 | package csdn.itsaysay.main.plugin.res;
2 |
3 | import vip.aliali.spring.plugin.constants.PluginState;
4 | import lombok.Data;
5 |
6 | @Data
7 | public class PluginRes {
8 |
9 | /**
10 | * 插件id
11 | */
12 | private String id;
13 |
14 | /**
15 | * 版本
16 | */
17 | private String version;
18 |
19 | /**
20 | * 描述
21 | */
22 | private String description;
23 |
24 | /**
25 | * 插件启动状态
26 | */
27 | private PluginState pluginState;
28 | }
29 |
--------------------------------------------------------------------------------
/spring-hot-plugin-example/spring-hot-plugin-demo/src/main/java/csdn/itsaysay/main/plugin/service/PluginListener.java:
--------------------------------------------------------------------------------
1 | package csdn.itsaysay.main.plugin.service;
2 |
3 | import lombok.extern.slf4j.Slf4j;
4 | import org.springframework.stereotype.Component;
5 | import vip.aliali.spring.plugin.PluginInfo;
6 |
7 | /**
8 | * 插件事件监听
9 | * @author jujun.chen
10 | */
11 | @Slf4j
12 | @Component
13 | public class PluginListener implements vip.aliali.spring.plugin.listener.PluginListener {
14 |
15 | @Override
16 | public void startSuccess(PluginInfo pluginInfo) {
17 | log.info("{}--->启动成功", pluginInfo.getId());
18 | }
19 |
20 | @Override
21 | public void startFailure(PluginInfo pluginInfo, Throwable throwable) {
22 | log.info("{}--->启动失败", pluginInfo.getId());
23 | }
24 |
25 | @Override
26 | public void stopSuccess(PluginInfo pluginInfo) {
27 | log.info("{}--->停止成功", pluginInfo.getId());
28 | }
29 |
30 | @Override
31 | public void stopFailure(PluginInfo pluginInfo, Throwable throwable) {
32 | log.info("{}--->停止失败", pluginInfo.getId());
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/spring-hot-plugin-example/spring-hot-plugin-demo/src/main/java/csdn/itsaysay/main/plugin/service/PluginService.java:
--------------------------------------------------------------------------------
1 | package csdn.itsaysay.main.plugin.service;
2 |
3 | import csdn.itsaysay.main.plugin.req.PluginReq;
4 | import csdn.itsaysay.main.plugin.res.PluginRes;
5 | import org.springframework.web.multipart.MultipartFile;
6 |
7 | import java.util.List;
8 |
9 | public interface PluginService {
10 |
11 | void install(MultipartFile file);
12 |
13 | Boolean start(String pluginId);
14 |
15 | Boolean stop(String pluginId);
16 |
17 | void uninstall(String pluginId);
18 |
19 | List list(PluginReq req);
20 | }
21 |
--------------------------------------------------------------------------------
/spring-hot-plugin-example/spring-hot-plugin-demo/src/main/java/csdn/itsaysay/main/plugin/service/impl/PluginServiceImpl.java:
--------------------------------------------------------------------------------
1 | package csdn.itsaysay.main.plugin.service.impl;
2 |
3 | import cn.hutool.core.convert.Convert;
4 | import csdn.itsaysay.main.plugin.service.PluginService;
5 | import csdn.itsaysay.main.plugin.req.PluginReq;
6 | import csdn.itsaysay.main.plugin.res.PluginRes;
7 | import vip.aliali.spring.plugin.PluginAutoConfiguration;
8 | import vip.aliali.spring.plugin.PluginInfo;
9 | import vip.aliali.spring.plugin.PluginManager;
10 | import lombok.extern.slf4j.Slf4j;
11 | import org.springframework.stereotype.Service;
12 | import org.springframework.web.multipart.MultipartFile;
13 |
14 | import javax.annotation.Resource;
15 | import java.util.List;
16 |
17 | @Slf4j
18 | @Service
19 | public class PluginServiceImpl implements PluginService {
20 |
21 | @Resource
22 | PluginManager pluginManager;
23 | @Resource
24 | PluginAutoConfiguration pluginAutoConfiguration;
25 |
26 | @Override
27 | public void install(MultipartFile file) {
28 | try {
29 | String firstPath = pluginAutoConfiguration.getPluginPath() + "/tmp";
30 | //安装
31 | pluginManager.install(file);
32 | } catch (Exception ex) {
33 | throw new RuntimeException(ex.getMessage(), ex);
34 | }
35 | }
36 |
37 | @Override
38 | public void uninstall(String pluginId) {
39 | pluginManager.uninstall(pluginId);
40 | }
41 |
42 | @Override
43 | public List list(PluginReq req) {
44 | List pluginInfoList = pluginManager.list(req.getPluginId());
45 | return Convert.toList(PluginRes.class, pluginInfoList);
46 | }
47 |
48 | @Override
49 | public Boolean start(String pluginId) {
50 | return null;
51 | }
52 |
53 | @Override
54 | public Boolean stop(String pluginId) {
55 | return null;
56 | }
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/spring-hot-plugin-example/spring-hot-plugin-demo/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | server:
2 | port: 8080
3 | servlet:
4 | context-path: /
5 |
6 | spring:
7 | servlet:
8 | multipart:
9 | max-file-size: 100MB
10 | max-request-size: 100MB
11 | application:
12 | name: spring-hot-plugin-demo
13 | main:
14 | allow-bean-definition-overriding: true
15 | mvc:
16 | throw-exception-if-no-handler-found: true
17 | static-path-pattern: /static/**
18 | datasource:
19 | # url: jdbc:h2:mem:testdb
20 | # driver-class-name: org.h2.Driver
21 | # username: sa
22 | # password:
23 | url: jdbc:mysql://172.17.0.2:3306/testdb?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
24 | driver-class-name: com.mysql.cj.jdbc.Driver
25 | username: root
26 | password: root123
27 |
28 | # admin
29 | # boot:
30 | # admin:
31 | # client:
32 | # url: http://localhost:8081
33 | management:
34 | endpoints:
35 | web:
36 | exposure:
37 | include: "*"
38 | info:
39 | env:
40 | enabled: true
41 |
42 |
43 | # 开启调式日志
44 | logging:
45 | file:
46 | name: logs/spring-hot-plugin-demo.log
47 | level:
48 | csdn.itsaysay.plugin: DEBUG
49 |
50 | #插件配置
51 | plugin:
52 | runMode: prod
53 | backup-path: /home/cjj/spring-hot-plugin-demo/plugin-path/back
54 | pluginPath: /home/cjj/spring-hot-plugin-demo/plugin-path
55 | basePackage: csdn.itsaysay.demo
56 |
--------------------------------------------------------------------------------
/spring-hot-plugin-example/spring-hot-plugin-demo/src/main/resources/data.sql:
--------------------------------------------------------------------------------
1 | insert into usertb (id, name, age) values(1, 'spring-hot-plugin', 18);
--------------------------------------------------------------------------------
/spring-hot-plugin-example/spring-hot-plugin-demo/src/main/resources/schema.sql:
--------------------------------------------------------------------------------
1 | -- 创建一张用户表,包含id,name,age,id是主键
2 | CREATE TABLE IF NOT EXISTS usertb (
3 | id INTEGER AUTO_INCREMENT PRIMARY KEY,
4 | name VARCHAR(255),
5 | age INTEGER
6 | );
--------------------------------------------------------------------------------
/spring-hot-plugin-example/spring-hot-plugin-example.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/spring-hot-plugin-example/spring-hot-plugin-task-demo/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | !.mvn/wrapper/maven-wrapper.jar
3 | !**/src/main/**/target/
4 | !**/src/test/**/target/
5 |
6 | ### IntelliJ IDEA ###
7 | .idea/modules.xml
8 | .idea/jarRepositories.xml
9 | .idea/compiler.xml
10 | .idea/libraries/
11 | *.iws
12 | *.iml
13 | *.ipr
14 |
15 | ### Eclipse ###
16 | .apt_generated
17 | .classpath
18 | .factorypath
19 | .project
20 | .settings
21 | .springBeans
22 | .sts4-cache
23 |
24 | ### NetBeans ###
25 | /nbproject/private/
26 | /nbbuild/
27 | /dist/
28 | /nbdist/
29 | /.nb-gradle/
30 | build/
31 | !**/src/main/**/build/
32 | !**/src/test/**/build/
33 |
34 | ### VS Code ###
35 | .vscode/
36 |
37 | ### Mac OS ###
38 | .DS_Store
--------------------------------------------------------------------------------
/spring-hot-plugin-example/spring-hot-plugin-task-demo/Dockerfile:
--------------------------------------------------------------------------------
1 | # 指定 java 环境镜像
2 | FROM openjdk:8
3 | # 维护者信息
4 | LABEL service-name=spring-hot-plugin-task-demo
5 | # 复制文件到容器
6 | COPY target/*.jar /application.jar
7 |
8 | RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' > /etc/timezone
9 |
10 | ENTRYPOINT ["java", "-jar", "-Xms256m", "-Xmx256m", "/application.jar"]
11 |
--------------------------------------------------------------------------------
/spring-hot-plugin-example/spring-hot-plugin-task-demo/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | csdn.itsaysay.plugin
8 | spring-hot-plugin-example
9 | 1.1
10 |
11 |
12 | spring-hot-plugin-task-demo
13 | 性能测试,定时执行安装卸载,查询任务
14 |
15 |
16 | 8
17 | 8
18 | UTF-8
19 | 2.4.5
20 |
21 |
22 |
23 |
24 | org.springframework.boot
25 | spring-boot-starter-web
26 |
27 |
28 |
29 | com.baomidou
30 | mybatis-plus-boot-starter
31 | 3.5.7
32 |
33 |
34 |
35 |
36 | mysql
37 | mysql-connector-java
38 | 8.0.31
39 |
40 |
41 |
42 | com.alibaba
43 | fastjson
44 | 2.0.39
45 |
46 |
47 |
48 | cn.hutool
49 | hutool-all
50 | 5.8.32
51 |
52 |
53 |
54 |
55 |
56 |
57 | ${project.artifactId}
58 |
59 |
60 | org.springframework.boot
61 | spring-boot-maven-plugin
62 | ${spring-boot-maven-plugin.version}
63 |
64 |
65 |
66 | repackage
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/spring-hot-plugin-example/spring-hot-plugin-task-demo/src/main/java/csdn/itsaysay/task/TaskDemoApplication.java:
--------------------------------------------------------------------------------
1 | package csdn.itsaysay.task;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication()
7 | public class TaskDemoApplication {
8 |
9 | public static void main(String[] args) {
10 | SpringApplication.run(TaskDemoApplication.class, args);
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/spring-hot-plugin-example/spring-hot-plugin-task-demo/src/main/java/csdn/itsaysay/task/service/TaskService.java:
--------------------------------------------------------------------------------
1 | package csdn.itsaysay.task.service;
2 |
3 | import cn.hutool.core.thread.ThreadUtil;
4 | import cn.hutool.http.HttpRequest;
5 | import lombok.extern.slf4j.Slf4j;
6 | import org.springframework.scheduling.annotation.EnableScheduling;
7 | import org.springframework.scheduling.annotation.Scheduled;
8 | import org.springframework.stereotype.Component;
9 |
10 | import java.io.File;
11 |
12 | /**
13 | * @author jujun.chen
14 | */
15 | @Slf4j
16 | @Component
17 | @EnableScheduling
18 | public class TaskService {
19 |
20 | private static final String TASK_URL = "http://172.17.0.5:8080";
21 |
22 | public static final String INSALL_PLUGIN = "/plugin/install";
23 | public static final String UNINSTALL_PLUGIN = "/plugin/uninstall";
24 | public static final String MYBATIS_INSERT = "/mybatis/insert";
25 | public static final String MYBATIS_QUERY = "/mybatis/getUserXml/%s";
26 | public static final String MYBATIS_DELETE = "/mybatis/delete/%s";
27 |
28 | //每10分钟执行一次,新增,查询,删除操作
29 | @Scheduled(cron = "0 0/10 * * * ?")
30 | public void task() {
31 | //插件路径
32 | File file = new File("/home/cjj/spring-hot-plugin-demo/plugin-path/back/plugin-demo-mybatis-1.1-repackage.jar");
33 |
34 | //安装插件
35 | log.info("安装插件");
36 | String response = HttpRequest.post(TASK_URL + INSALL_PLUGIN)
37 | .form("file", file)
38 | .execute().body();
39 | log.info("执行1000次,增查删");
40 | //循环查询1000次
41 | for (int i = 0; i < 1000; i++) {
42 | //调用插入数据接口
43 | String insertId = HttpRequest.get(TASK_URL + MYBATIS_INSERT)
44 | .execute().body();
45 | log.info("插入数据:{}", insertId);
46 | // ThreadUtil.sleep(1000);
47 | //查询数据
48 | String query = HttpRequest.get(TASK_URL + String.format(MYBATIS_QUERY, insertId))
49 | .execute().body();
50 | log.info("查询数据:{}", query);
51 | // ThreadUtil.sleep(1000);
52 | //删除数据
53 | HttpRequest.delete(TASK_URL + String.format(MYBATIS_DELETE, insertId))
54 | .execute();
55 | }
56 |
57 | //卸载插件,参数是pluginId
58 | log.info("卸载插件");
59 | HttpRequest.post(TASK_URL + UNINSTALL_PLUGIN)
60 | .form("pluginId", "plugin-demo-mybatis")
61 | .execute();
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/spring-hot-plugin-example/spring-hot-plugin-task-demo/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | server:
2 | port: 8080
3 | servlet:
4 | context-path: /
5 |
6 | spring:
7 | servlet:
8 | multipart:
9 | max-file-size: 100MB
10 | max-request-size: 100MB
11 | application:
12 | name: spring-hot-plugin-task-demo
13 | main:
14 | allow-bean-definition-overriding: true
15 | mvc:
16 | throw-exception-if-no-handler-found: true
17 | static-path-pattern: /static/**
18 | datasource:
19 | # url: jdbc:h2:mem:testdb
20 | # driver-class-name: org.h2.Driver
21 | # username: sa
22 | # password:
23 | url: jdbc:mysql://172.17.0.2:3306/testdb?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
24 | driver-class-name: com.mysql.cj.jdbc.Driver
25 | username: root
26 | password: root123
27 |
28 |
29 |
30 | # 开启调式日志
31 | logging:
32 | file:
33 | name: logs/spring-hot-plugin-task-demo.log
34 | level:
35 | csdn.itsaysay.plugin: DEBUG
36 |
37 |
--------------------------------------------------------------------------------
/spring-hot-plugin-loader/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | !.mvn/wrapper/maven-wrapper.jar
3 | !**/src/main/**/target/
4 | !**/src/test/**/target/
5 |
6 | ### IntelliJ IDEA ###
7 | .idea/modules.xml
8 | .idea/jarRepositories.xml
9 | .idea/compiler.xml
10 | .idea/libraries/
11 | *.iws
12 | *.iml
13 | *.ipr
14 |
15 | ### Eclipse ###
16 | .apt_generated
17 | .classpath
18 | .factorypath
19 | .project
20 | .settings
21 | .springBeans
22 | .sts4-cache
23 |
24 | ### NetBeans ###
25 | /nbproject/private/
26 | /nbbuild/
27 | /dist/
28 | /nbdist/
29 | /.nb-gradle/
30 | build/
31 | !**/src/main/**/build/
32 | !**/src/test/**/build/
33 |
34 | ### VS Code ###
35 | .vscode/
36 |
37 | ### Mac OS ###
38 | .DS_Store
--------------------------------------------------------------------------------
/spring-hot-plugin-loader/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | vip.aliali.spring
8 | spring-hot-plugin-parent
9 | 1.1.1
10 |
11 |
12 | spring-hot-plugin-loader
13 | 插件依赖包加载组件
14 |
15 |
16 | 8
17 | 8
18 | UTF-8
19 |
20 |
21 |
22 |
23 | org.springframework
24 | spring-core
25 | provided
26 | true
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/spring-hot-plugin-loader/src/main/java/vip/aliali/spring/plugin/loader/ClassPathIndexFile.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2020 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package vip.aliali.spring.plugin.loader;
18 |
19 | import java.io.*;
20 | import java.net.MalformedURLException;
21 | import java.net.URISyntaxException;
22 | import java.net.URL;
23 | import java.nio.charset.StandardCharsets;
24 | import java.util.ArrayList;
25 | import java.util.Collections;
26 | import java.util.List;
27 | import java.util.stream.Collectors;
28 |
29 | /**
30 | * A class path index file that provides ordering information for JARs.
31 | *
32 | * @author Madhura Bhave
33 | * @author Phillip Webb
34 | */
35 | final class ClassPathIndexFile {
36 |
37 | private final File root;
38 |
39 | private final List lines;
40 |
41 | private ClassPathIndexFile(File root, List lines) {
42 | this.root = root;
43 | this.lines = lines.stream().map(this::extractName).collect(Collectors.toList());
44 | }
45 |
46 | private String extractName(String line) {
47 | if (line.startsWith("- \"") && line.endsWith("\"")) {
48 | return line.substring(3, line.length() - 1);
49 | }
50 | throw new IllegalStateException("Malformed classpath index line [" + line + "]");
51 | }
52 |
53 | int size() {
54 | return this.lines.size();
55 | }
56 |
57 | boolean containsEntry(String name) {
58 | if (name == null || name.isEmpty()) {
59 | return false;
60 | }
61 | return this.lines.contains(name);
62 | }
63 |
64 | List getUrls() {
65 | return Collections.unmodifiableList(this.lines.stream().map(this::asUrl).collect(Collectors.toList()));
66 | }
67 |
68 | private URL asUrl(String line) {
69 | try {
70 | return new File(this.root, line).toURI().toURL();
71 | }
72 | catch (MalformedURLException ex) {
73 | throw new IllegalStateException(ex);
74 | }
75 | }
76 |
77 | static ClassPathIndexFile loadIfPossible(URL root, String location) throws IOException {
78 | return loadIfPossible(asFile(root), location);
79 | }
80 |
81 | private static ClassPathIndexFile loadIfPossible(File root, String location) throws IOException {
82 | return loadIfPossible(root, new File(root, location));
83 | }
84 |
85 | private static ClassPathIndexFile loadIfPossible(File root, File indexFile) throws IOException {
86 | if (indexFile.exists() && indexFile.isFile()) {
87 | try (InputStream inputStream = new FileInputStream(indexFile)) {
88 | return new ClassPathIndexFile(root, loadLines(inputStream));
89 | }
90 | }
91 | return null;
92 | }
93 |
94 | private static List loadLines(InputStream inputStream) throws IOException {
95 | List lines = new ArrayList<>();
96 | BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
97 | String line = reader.readLine();
98 | while (line != null) {
99 | if (!line.trim().isEmpty()) {
100 | lines.add(line);
101 | }
102 | line = reader.readLine();
103 | }
104 | return Collections.unmodifiableList(lines);
105 | }
106 |
107 | private static File asFile(URL url) {
108 | if (!"file".equals(url.getProtocol())) {
109 | throw new IllegalArgumentException("URL does not reference a file");
110 | }
111 | try {
112 | return new File(url.toURI());
113 | }
114 | catch (URISyntaxException ex) {
115 | return new File(url.getPath());
116 | }
117 | }
118 |
119 | }
120 |
--------------------------------------------------------------------------------
/spring-hot-plugin-loader/src/main/java/vip/aliali/spring/plugin/loader/JarLauncher.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2021 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package vip.aliali.spring.plugin.loader;
18 |
19 | import vip.aliali.spring.plugin.loader.archive.Archive;
20 |
21 | import java.util.Iterator;
22 |
23 | /**
24 | * {@link Launcher} for JAR based archives. This launcher assumes that dependency jars are
25 | * included inside a {@code /BOOT-INF/lib} directory and that application classes are
26 | * included inside a {@code /BOOT-INF/classes} directory.
27 | *
28 | * @author Phillip Webb
29 | * @author Andy Wilkinson
30 | * @author Madhura Bhave
31 | * @author Scott Frederick
32 | * @since 1.0.0
33 | */
34 | public class JarLauncher extends ExecutableArchiveLauncher {
35 |
36 | static final Archive.EntryFilter NESTED_ARCHIVE_ENTRY_FILTER = (entry) -> {
37 | if (entry.isDirectory()) {
38 | return entry.getName().equals("classes/");
39 | }
40 | return entry.getName().startsWith("lib/");
41 | };
42 |
43 | public JarLauncher() {
44 | }
45 |
46 | public JarLauncher(Archive archive) {
47 | super(archive);
48 | }
49 |
50 | @Override
51 | protected boolean isPostProcessingClassPathArchives() {
52 | return false;
53 | }
54 |
55 | @Override
56 | protected boolean isNestedArchive(Archive.Entry entry) {
57 | return NESTED_ARCHIVE_ENTRY_FILTER.matches(entry);
58 | }
59 |
60 | @Override
61 | protected String getArchiveEntryPathPrefix() {
62 | return null;
63 | }
64 |
65 | public Iterator getClassPathArchivesIterator() throws Exception {
66 | return super.getClassPathArchivesIterator();
67 | }
68 |
69 | public static void main(String[] args) throws Exception {
70 | new JarLauncher().launch(args);
71 | }
72 |
73 | }
74 |
--------------------------------------------------------------------------------
/spring-hot-plugin-loader/src/main/java/vip/aliali/spring/plugin/loader/MainMethodRunner.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2020 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package vip.aliali.spring.plugin.loader;
18 |
19 | import java.lang.reflect.Method;
20 |
21 | /**
22 | * Utility class that is used by {@link Launcher}s to call a main method. The class
23 | * containing the main method is loaded using the thread context class loader.
24 | *
25 | * @author Phillip Webb
26 | * @author Andy Wilkinson
27 | * @since 1.0.0
28 | */
29 | public class MainMethodRunner {
30 |
31 | private final String mainClassName;
32 |
33 | private final String[] args;
34 |
35 | /**
36 | * Create a new {@link MainMethodRunner} instance.
37 | * @param mainClass the main class
38 | * @param args incoming arguments
39 | */
40 | public MainMethodRunner(String mainClass, String[] args) {
41 | this.mainClassName = mainClass;
42 | this.args = (args != null) ? args.clone() : null;
43 | }
44 |
45 | public void run() throws Exception {
46 | Class> mainClass = Class.forName(this.mainClassName, false, Thread.currentThread().getContextClassLoader());
47 | Method mainMethod = mainClass.getDeclaredMethod("main", String[].class);
48 | mainMethod.setAccessible(true);
49 | mainMethod.invoke(null, new Object[] { this.args });
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/spring-hot-plugin-loader/src/main/java/vip/aliali/spring/plugin/loader/WarLauncher.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2021 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package vip.aliali.spring.plugin.loader;
18 |
19 | import vip.aliali.spring.plugin.loader.archive.Archive;
20 |
21 | /**
22 | * {@link Launcher} for WAR based archives. This launcher for standard WAR archives.
23 | * Supports dependencies in {@code WEB-INF/lib} as well as {@code WEB-INF/lib-provided},
24 | * classes are loaded from {@code WEB-INF/classes}.
25 | *
26 | * @author Phillip Webb
27 | * @author Andy Wilkinson
28 | * @author Scott Frederick
29 | * @since 1.0.0
30 | */
31 | public class WarLauncher extends ExecutableArchiveLauncher {
32 |
33 | public WarLauncher() {
34 | }
35 |
36 | protected WarLauncher(Archive archive) {
37 | super(archive);
38 | }
39 |
40 | @Override
41 | protected boolean isPostProcessingClassPathArchives() {
42 | return false;
43 | }
44 |
45 | @Override
46 | public boolean isNestedArchive(Archive.Entry entry) {
47 | if (entry.isDirectory()) {
48 | return entry.getName().equals("WEB-INF/classes/");
49 | }
50 | return entry.getName().startsWith("WEB-INF/lib/") || entry.getName().startsWith("WEB-INF/lib-provided/");
51 | }
52 |
53 | @Override
54 | protected String getArchiveEntryPathPrefix() {
55 | return "WEB-INF/";
56 | }
57 |
58 | public static void main(String[] args) throws Exception {
59 | new WarLauncher().launch(args);
60 | }
61 |
62 | }
63 |
--------------------------------------------------------------------------------
/spring-hot-plugin-loader/src/main/java/vip/aliali/spring/plugin/loader/archive/Archive.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2021 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package vip.aliali.spring.plugin.loader.archive;
18 |
19 |
20 | import vip.aliali.spring.plugin.loader.Launcher;
21 | import vip.aliali.spring.plugin.loader.jar.JarFile;
22 |
23 | import java.io.IOException;
24 | import java.net.MalformedURLException;
25 | import java.net.URL;
26 | import java.util.*;
27 | import java.util.function.Consumer;
28 | import java.util.jar.Manifest;
29 |
30 | /**
31 | * An archive that can be launched by the {@link Launcher}.
32 | *
33 | * @author Phillip Webb
34 | * @since 1.0.0
35 | * @see JarFileArchive
36 | */
37 | public interface Archive extends Iterable, AutoCloseable {
38 |
39 | /**
40 | * Returns a URL that can be used to load the archive.
41 | * @return the archive URL
42 | * @throws MalformedURLException if the URL is malformed
43 | */
44 | URL getUrl() throws MalformedURLException;
45 |
46 | /**
47 | * Returns the manifest of the archive.
48 | * @return the manifest
49 | * @throws IOException if the manifest cannot be read
50 | */
51 | Manifest getManifest() throws IOException;
52 |
53 | /**
54 | * Returns nested {@link Archive}s for entries that match the specified filters.
55 | * @param searchFilter filter used to limit when additional sub-entry searching is
56 | * required or {@code null} if all entries should be considered.
57 | * @param includeFilter filter used to determine which entries should be included in
58 | * the result or {@code null} if all entries should be included
59 | * @return the nested archives
60 | * @throws IOException on IO error
61 | * @since 2.3.0
62 | */
63 | default Iterator getNestedArchives(EntryFilter searchFilter, EntryFilter includeFilter)
64 | throws IOException {
65 | EntryFilter combinedFilter = (entry) -> (searchFilter == null || searchFilter.matches(entry))
66 | && (includeFilter == null || includeFilter.matches(entry));
67 | List nestedArchives = getNestedArchives(combinedFilter);
68 | return nestedArchives.iterator();
69 | }
70 |
71 | /**
72 | * Returns nested {@link Archive}s for entries that match the specified filter.
73 | * @param filter the filter used to limit entries
74 | * @return nested archives
75 | * @throws IOException if nested archives cannot be read
76 | * @deprecated since 2.3.0 for removal in 2.5.0 in favor of
77 | * {@link #getNestedArchives(EntryFilter, EntryFilter)}
78 | */
79 | @Deprecated
80 | default List getNestedArchives(EntryFilter filter) throws IOException {
81 | throw new IllegalStateException("Unexpected call to getNestedArchives(filter)");
82 | }
83 |
84 | /**
85 | * Return a new iterator for the archive entries.
86 | * @deprecated since 2.3.0 for removal in 2.5.0 in favor of using
87 | * {@link JarFile} to access entries and
88 | * {@link #getNestedArchives(EntryFilter, EntryFilter)} for accessing nested archives.
89 | * @see Iterable#iterator()
90 | */
91 | @Deprecated
92 | @Override
93 | Iterator iterator();
94 |
95 | /**
96 | * Performs the given action for each element of the {@code Iterable} until all
97 | * elements have been processed or the action throws an exception.
98 | * @deprecated since 2.3.0 for removal in 2.5.0 in favor of using
99 | * {@link JarFile} to access entries and
100 | * {@link #getNestedArchives(EntryFilter, EntryFilter)} for accessing nested archives.
101 | * @see Iterable#forEach
102 | */
103 | @Deprecated
104 | @Override
105 | default void forEach(Consumer super Entry> action) {
106 | Objects.requireNonNull(action);
107 | for (Entry entry : this) {
108 | action.accept(entry);
109 | }
110 | }
111 |
112 | /**
113 | * Creates a {@link Spliterator} over the elements described by this {@code Iterable}.
114 | * @deprecated since 2.3.0 for removal in 2.5.0 in favor of using
115 | * {@link JarFile} to access entries and
116 | * {@link #getNestedArchives(EntryFilter, EntryFilter)} for accessing nested archives.
117 | * @see Iterable#spliterator
118 | */
119 | @Deprecated
120 | @Override
121 | default Spliterator spliterator() {
122 | return Spliterators.spliteratorUnknownSize(iterator(), 0);
123 | }
124 |
125 | /**
126 | * Return if the archive is exploded (already unpacked).
127 | * @return if the archive is exploded
128 | * @since 2.3.0
129 | */
130 | default boolean isExploded() {
131 | return false;
132 | }
133 |
134 | /**
135 | * Closes the {@code Archive}, releasing any open resources.
136 | * @throws Exception if an error occurs during close processing
137 | * @since 2.2.0
138 | */
139 | @Override
140 | default void close() throws Exception {
141 |
142 | }
143 |
144 | /**
145 | * Represents a single entry in the archive.
146 | */
147 | interface Entry {
148 |
149 | /**
150 | * Returns {@code true} if the entry represents a directory.
151 | * @return if the entry is a directory
152 | */
153 | boolean isDirectory();
154 |
155 | /**
156 | * Returns the name of the entry.
157 | * @return the name of the entry
158 | */
159 | String getName();
160 |
161 | }
162 |
163 | /**
164 | * Strategy interface to filter {@link Entry Entries}.
165 | */
166 | @FunctionalInterface
167 | interface EntryFilter {
168 |
169 | /**
170 | * Apply the jar entry filter.
171 | * @param entry the entry to filter
172 | * @return {@code true} if the filter matches
173 | */
174 | boolean matches(Entry entry);
175 |
176 | }
177 |
178 | }
179 |
--------------------------------------------------------------------------------
/spring-hot-plugin-loader/src/main/java/vip/aliali/spring/plugin/loader/archive/package-info.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2020 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | /**
18 | * Abstraction over logical Archives be they backed by a JAR file or unpacked into a
19 | * directory.
20 | *
21 | * @see vip.aliali.spring.plugin.loader.archive.Archive
22 | */
23 | package vip.aliali.spring.plugin.loader.archive;
24 |
--------------------------------------------------------------------------------
/spring-hot-plugin-loader/src/main/java/vip/aliali/spring/plugin/loader/data/RandomAccessData.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2019 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package vip.aliali.spring.plugin.loader.data;
18 |
19 | import java.io.EOFException;
20 | import java.io.IOException;
21 | import java.io.InputStream;
22 |
23 | /**
24 | * Interface that provides read-only random access to some underlying data.
25 | * Implementations must allow concurrent reads in a thread-safe manner.
26 | *
27 | * @author Phillip Webb
28 | * @since 1.0.0
29 | */
30 | public interface RandomAccessData {
31 |
32 | /**
33 | * Returns an {@link InputStream} that can be used to read the underlying data. The
34 | * caller is responsible close the underlying stream.
35 | * @return a new input stream that can be used to read the underlying data.
36 | * @throws IOException if the stream cannot be opened
37 | */
38 | InputStream getInputStream() throws IOException;
39 |
40 | /**
41 | * Returns a new {@link RandomAccessData} for a specific subsection of this data.
42 | * @param offset the offset of the subsection
43 | * @param length the length of the subsection
44 | * @return the subsection data
45 | */
46 | RandomAccessData getSubsection(long offset, long length);
47 |
48 | /**
49 | * Reads all the data and returns it as a byte array.
50 | * @return the data
51 | * @throws IOException if the data cannot be read
52 | */
53 | byte[] read() throws IOException;
54 |
55 | /**
56 | * Reads the {@code length} bytes of data starting at the given {@code offset}.
57 | * @param offset the offset from which data should be read
58 | * @param length the number of bytes to be read
59 | * @return the data
60 | * @throws IOException if the data cannot be read
61 | * @throws IndexOutOfBoundsException if offset is beyond the end of the file or
62 | * subsection
63 | * @throws EOFException if offset plus length is greater than the length of the file
64 | * or subsection
65 | */
66 | byte[] read(long offset, long length) throws IOException;
67 |
68 | /**
69 | * Returns the size of the data.
70 | * @return the size
71 | */
72 | long getSize();
73 |
74 | }
75 |
--------------------------------------------------------------------------------
/spring-hot-plugin-loader/src/main/java/vip/aliali/spring/plugin/loader/data/package-info.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2021 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | /**
18 | * Classes and interfaces to allow random access to a block of data.
19 | *
20 | * @see vip.aliali.spring.plugin.loader.data.RandomAccessData
21 | */
22 | package vip.aliali.spring.plugin.loader.data;
23 |
--------------------------------------------------------------------------------
/spring-hot-plugin-loader/src/main/java/vip/aliali/spring/plugin/loader/jar/AbstractJarFile.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2020 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package vip.aliali.spring.plugin.loader.jar;
18 |
19 | import java.io.File;
20 | import java.io.IOException;
21 | import java.io.InputStream;
22 | import java.net.MalformedURLException;
23 | import java.net.URL;
24 | import java.security.Permission;
25 |
26 | /**
27 | * Base class for extended variants of {@link java.util.jar.JarFile}.
28 | *
29 | * @author Phillip Webb
30 | */
31 | abstract class AbstractJarFile extends java.util.jar.JarFile {
32 |
33 | /**
34 | * Create a new {@link AbstractJarFile}.
35 | * @param file the root jar file.
36 | * @throws IOException on IO error
37 | */
38 | AbstractJarFile(File file) throws IOException {
39 | super(file);
40 | }
41 |
42 | /**
43 | * Return a URL that can be used to access this JAR file. NOTE: the specified URL
44 | * cannot be serialized and or cloned.
45 | * @return the URL
46 | * @throws MalformedURLException if the URL is malformed
47 | */
48 | abstract URL getUrl() throws MalformedURLException;
49 |
50 | /**
51 | * Return the {@link JarFileType} of this instance.
52 | * @return the jar file type
53 | */
54 | abstract JarFileType getType();
55 |
56 | /**
57 | * Return the security permission for this JAR.
58 | * @return the security permission.
59 | */
60 | abstract Permission getPermission();
61 |
62 | /**
63 | * Return an {@link InputStream} for the entire jar contents.
64 | * @return the contents input stream
65 | * @throws IOException on IO error
66 | */
67 | abstract InputStream getInputStream() throws IOException;
68 |
69 | /**
70 | * The type of a {@link JarFile}.
71 | */
72 | enum JarFileType {
73 |
74 | DIRECT, NESTED_DIRECTORY, NESTED_JAR
75 |
76 | }
77 |
78 | }
79 |
--------------------------------------------------------------------------------
/spring-hot-plugin-loader/src/main/java/vip/aliali/spring/plugin/loader/jar/Bytes.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2019 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package vip.aliali.spring.plugin.loader.jar;
18 |
19 | /**
20 | * Utilities for dealing with bytes from ZIP files.
21 | *
22 | * @author Phillip Webb
23 | */
24 | final class Bytes {
25 |
26 | private Bytes() {
27 | }
28 |
29 | static long littleEndianValue(byte[] bytes, int offset, int length) {
30 | long value = 0;
31 | for (int i = length - 1; i >= 0; i--) {
32 | value = ((value << 8) | (bytes[offset + i] & 0xFF));
33 | }
34 | return value;
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/spring-hot-plugin-loader/src/main/java/vip/aliali/spring/plugin/loader/jar/CentralDirectoryParser.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2021 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package vip.aliali.spring.plugin.loader.jar;
18 |
19 | import vip.aliali.spring.plugin.loader.data.RandomAccessData;
20 |
21 | import java.io.IOException;
22 | import java.util.ArrayList;
23 | import java.util.List;
24 |
25 | /**
26 | * Parses the central directory from a JAR file.
27 | *
28 | * @author Phillip Webb
29 | * @author Andy Wilkinson
30 | * @see CentralDirectoryVisitor
31 | */
32 | class CentralDirectoryParser {
33 |
34 | private static final int CENTRAL_DIRECTORY_HEADER_BASE_SIZE = 46;
35 |
36 | private final List visitors = new ArrayList<>();
37 |
38 | T addVisitor(T visitor) {
39 | this.visitors.add(visitor);
40 | return visitor;
41 | }
42 |
43 | /**
44 | * Parse the source data, triggering {@link CentralDirectoryVisitor visitors}.
45 | * @param data the source data
46 | * @param skipPrefixBytes if prefix bytes should be skipped
47 | * @return the actual archive data without any prefix bytes
48 | * @throws IOException on error
49 | */
50 | RandomAccessData parse(RandomAccessData data, boolean skipPrefixBytes) throws IOException {
51 | CentralDirectoryEndRecord endRecord = new CentralDirectoryEndRecord(data);
52 | if (skipPrefixBytes) {
53 | data = getArchiveData(endRecord, data);
54 | }
55 | RandomAccessData centralDirectoryData = endRecord.getCentralDirectory(data);
56 | visitStart(endRecord, centralDirectoryData);
57 | parseEntries(endRecord, centralDirectoryData);
58 | visitEnd();
59 | return data;
60 | }
61 |
62 | private void parseEntries(CentralDirectoryEndRecord endRecord, RandomAccessData centralDirectoryData)
63 | throws IOException {
64 | byte[] bytes = centralDirectoryData.read(0, centralDirectoryData.getSize());
65 | CentralDirectoryFileHeader fileHeader = new CentralDirectoryFileHeader();
66 | int dataOffset = 0;
67 | for (int i = 0; i < endRecord.getNumberOfRecords(); i++) {
68 | fileHeader.load(bytes, dataOffset, null, 0, null);
69 | visitFileHeader(dataOffset, fileHeader);
70 | dataOffset += CENTRAL_DIRECTORY_HEADER_BASE_SIZE + fileHeader.getName().length()
71 | + fileHeader.getComment().length() + fileHeader.getExtra().length;
72 | }
73 | }
74 |
75 | private RandomAccessData getArchiveData(CentralDirectoryEndRecord endRecord, RandomAccessData data) {
76 | long offset = endRecord.getStartOfArchive(data);
77 | if (offset == 0) {
78 | return data;
79 | }
80 | return data.getSubsection(offset, data.getSize() - offset);
81 | }
82 |
83 | private void visitStart(CentralDirectoryEndRecord endRecord, RandomAccessData centralDirectoryData) {
84 | for (CentralDirectoryVisitor visitor : this.visitors) {
85 | visitor.visitStart(endRecord, centralDirectoryData);
86 | }
87 | }
88 |
89 | private void visitFileHeader(long dataOffset, CentralDirectoryFileHeader fileHeader) {
90 | for (CentralDirectoryVisitor visitor : this.visitors) {
91 | visitor.visitFileHeader(fileHeader, dataOffset);
92 | }
93 | }
94 |
95 | private void visitEnd() {
96 | for (CentralDirectoryVisitor visitor : this.visitors) {
97 | visitor.visitEnd();
98 | }
99 | }
100 |
101 | }
102 |
--------------------------------------------------------------------------------
/spring-hot-plugin-loader/src/main/java/vip/aliali/spring/plugin/loader/jar/CentralDirectoryVisitor.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2021 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package vip.aliali.spring.plugin.loader.jar;
18 |
19 | import vip.aliali.spring.plugin.loader.data.RandomAccessData;
20 |
21 | /**
22 | * Callback visitor triggered by {@link CentralDirectoryParser}.
23 | *
24 | * @author Phillip Webb
25 | */
26 | interface CentralDirectoryVisitor {
27 |
28 | void visitStart(CentralDirectoryEndRecord endRecord, RandomAccessData centralDirectoryData);
29 |
30 | void visitFileHeader(CentralDirectoryFileHeader fileHeader, long dataOffset);
31 |
32 | void visitEnd();
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/spring-hot-plugin-loader/src/main/java/vip/aliali/spring/plugin/loader/jar/FileHeader.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2019 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package vip.aliali.spring.plugin.loader.jar;
18 |
19 | import java.util.zip.ZipEntry;
20 |
21 | /**
22 | * A file header record that has been loaded from a Jar file.
23 | *
24 | * @author Phillip Webb
25 | * @see JarEntry
26 | * @see CentralDirectoryFileHeader
27 | */
28 | interface FileHeader {
29 |
30 | /**
31 | * Returns {@code true} if the header has the given name.
32 | * @param name the name to test
33 | * @param suffix an additional suffix (or {@code 0})
34 | * @return {@code true} if the header has the given name
35 | */
36 | boolean hasName(CharSequence name, char suffix);
37 |
38 | /**
39 | * Return the offset of the load file header within the archive data.
40 | * @return the local header offset
41 | */
42 | long getLocalHeaderOffset();
43 |
44 | /**
45 | * Return the compressed size of the entry.
46 | * @return the compressed size.
47 | */
48 | long getCompressedSize();
49 |
50 | /**
51 | * Return the uncompressed size of the entry.
52 | * @return the uncompressed size.
53 | */
54 | long getSize();
55 |
56 | /**
57 | * Return the method used to compress the data.
58 | * @return the zip compression method
59 | * @see ZipEntry#STORED
60 | * @see ZipEntry#DEFLATED
61 | */
62 | int getMethod();
63 |
64 | }
65 |
--------------------------------------------------------------------------------
/spring-hot-plugin-loader/src/main/java/vip/aliali/spring/plugin/loader/jar/JarEntry.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2020 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package vip.aliali.spring.plugin.loader.jar;
18 |
19 | import java.io.IOException;
20 | import java.net.MalformedURLException;
21 | import java.net.URL;
22 | import java.security.CodeSigner;
23 | import java.security.cert.Certificate;
24 | import java.util.jar.Attributes;
25 | import java.util.jar.Manifest;
26 |
27 | /**
28 | * Extended variant of {@link java.util.jar.JarEntry} returned by {@link JarFile}s.
29 | *
30 | * @author Phillip Webb
31 | * @author Andy Wilkinson
32 | */
33 | class JarEntry extends java.util.jar.JarEntry implements FileHeader {
34 |
35 | private final int index;
36 |
37 | private final AsciiBytes name;
38 |
39 | private final AsciiBytes headerName;
40 |
41 | private final JarFile jarFile;
42 |
43 | private long localHeaderOffset;
44 |
45 | private volatile JarEntryCertification certification;
46 |
47 | JarEntry(JarFile jarFile, int index, CentralDirectoryFileHeader header, AsciiBytes nameAlias) {
48 | super((nameAlias != null) ? nameAlias.toString() : header.getName().toString());
49 | this.index = index;
50 | this.name = (nameAlias != null) ? nameAlias : header.getName();
51 | this.headerName = header.getName();
52 | this.jarFile = jarFile;
53 | this.localHeaderOffset = header.getLocalHeaderOffset();
54 | setCompressedSize(header.getCompressedSize());
55 | setMethod(header.getMethod());
56 | setCrc(header.getCrc());
57 | setComment(header.getComment().toString());
58 | setSize(header.getSize());
59 | setTime(header.getTime());
60 | if (header.hasExtra()) {
61 | setExtra(header.getExtra());
62 | }
63 | }
64 |
65 | int getIndex() {
66 | return this.index;
67 | }
68 |
69 | AsciiBytes getAsciiBytesName() {
70 | return this.name;
71 | }
72 |
73 | @Override
74 | public boolean hasName(CharSequence name, char suffix) {
75 | return this.headerName.matches(name, suffix);
76 | }
77 |
78 | /**
79 | * Return a {@link URL} for this {@link JarEntry}.
80 | * @return the URL for the entry
81 | * @throws MalformedURLException if the URL is not valid
82 | */
83 | URL getUrl() throws MalformedURLException {
84 | return new URL(this.jarFile.getUrl(), getName());
85 | }
86 |
87 | @Override
88 | public Attributes getAttributes() throws IOException {
89 | Manifest manifest = this.jarFile.getManifest();
90 | return (manifest != null) ? manifest.getAttributes(getName()) : null;
91 | }
92 |
93 | @Override
94 | public Certificate[] getCertificates() {
95 | return getCertification().getCertificates();
96 | }
97 |
98 | @Override
99 | public CodeSigner[] getCodeSigners() {
100 | return getCertification().getCodeSigners();
101 | }
102 |
103 | private JarEntryCertification getCertification() {
104 | if (!this.jarFile.isSigned()) {
105 | return JarEntryCertification.NONE;
106 | }
107 | JarEntryCertification certification = this.certification;
108 | if (certification == null) {
109 | certification = this.jarFile.getCertification(this);
110 | this.certification = certification;
111 | }
112 | return certification;
113 | }
114 |
115 | @Override
116 | public long getLocalHeaderOffset() {
117 | return this.localHeaderOffset;
118 | }
119 |
120 | }
121 |
--------------------------------------------------------------------------------
/spring-hot-plugin-loader/src/main/java/vip/aliali/spring/plugin/loader/jar/JarEntryCertification.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2020 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package vip.aliali.spring.plugin.loader.jar;
18 |
19 | import java.security.CodeSigner;
20 | import java.security.cert.Certificate;
21 |
22 | /**
23 | * {@link Certificate} and {@link CodeSigner} details for a {@link JarEntry} from a signed
24 | * {@link JarFile}.
25 | *
26 | * @author Phillip Webb
27 | */
28 | class JarEntryCertification {
29 |
30 | static final JarEntryCertification NONE = new JarEntryCertification(null, null);
31 |
32 | private final Certificate[] certificates;
33 |
34 | private final CodeSigner[] codeSigners;
35 |
36 | JarEntryCertification(Certificate[] certificates, CodeSigner[] codeSigners) {
37 | this.certificates = certificates;
38 | this.codeSigners = codeSigners;
39 | }
40 |
41 | Certificate[] getCertificates() {
42 | return (this.certificates != null) ? this.certificates.clone() : null;
43 | }
44 |
45 | CodeSigner[] getCodeSigners() {
46 | return (this.codeSigners != null) ? this.codeSigners.clone() : null;
47 | }
48 |
49 | static JarEntryCertification from(java.util.jar.JarEntry certifiedEntry) {
50 | Certificate[] certificates = (certifiedEntry != null) ? certifiedEntry.getCertificates() : null;
51 | CodeSigner[] codeSigners = (certifiedEntry != null) ? certifiedEntry.getCodeSigners() : null;
52 | if (certificates == null && codeSigners == null) {
53 | return NONE;
54 | }
55 | return new JarEntryCertification(certificates, codeSigners);
56 | }
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/spring-hot-plugin-loader/src/main/java/vip/aliali/spring/plugin/loader/jar/JarEntryFilter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2019 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package vip.aliali.spring.plugin.loader.jar;
18 |
19 | /**
20 | * Interface that can be used to filter and optionally rename jar entries.
21 | *
22 | * @author Phillip Webb
23 | */
24 | interface JarEntryFilter {
25 |
26 | /**
27 | * Apply the jar entry filter.
28 | * @param name the current entry name. This may be different that the original entry
29 | * name if a previous filter has been applied
30 | * @return the new name of the entry or {@code null} if the entry should not be
31 | * included.
32 | */
33 | AsciiBytes apply(AsciiBytes name);
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/spring-hot-plugin-loader/src/main/java/vip/aliali/spring/plugin/loader/jar/JarFileWrapper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2022 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package vip.aliali.spring.plugin.loader.jar;
18 |
19 | import java.io.IOException;
20 | import java.io.InputStream;
21 | import java.net.MalformedURLException;
22 | import java.net.URL;
23 | import java.security.Permission;
24 | import java.util.Enumeration;
25 | import java.util.jar.JarEntry;
26 | import java.util.jar.Manifest;
27 | import java.util.stream.Stream;
28 | import java.util.zip.ZipEntry;
29 |
30 | /**
31 | * A wrapper used to create a copy of a {@link JarFile} so that it can be safely closed
32 | * without closing the original.
33 | *
34 | * @author Phillip Webb
35 | */
36 | public class JarFileWrapper extends AbstractJarFile {
37 |
38 | private final JarFile parent;
39 |
40 | JarFileWrapper(JarFile parent) throws IOException {
41 | super(parent.getRootJarFile().getFile());
42 | this.parent = parent;
43 | if (System.getSecurityManager() == null) {
44 | super.close();
45 | }
46 | }
47 |
48 | @Override
49 | URL getUrl() throws MalformedURLException {
50 | return this.parent.getUrl();
51 | }
52 |
53 | @Override
54 | JarFileType getType() {
55 | return this.parent.getType();
56 | }
57 |
58 | @Override
59 | Permission getPermission() {
60 | return this.parent.getPermission();
61 | }
62 |
63 | @Override
64 | public Manifest getManifest() throws IOException {
65 | return this.parent.getManifest();
66 | }
67 |
68 | @Override
69 | public Enumeration entries() {
70 | return this.parent.entries();
71 | }
72 |
73 | @Override
74 | public Stream stream() {
75 | return this.parent.stream();
76 | }
77 |
78 | @Override
79 | public JarEntry getJarEntry(String name) {
80 | return this.parent.getJarEntry(name);
81 | }
82 |
83 | @Override
84 | public ZipEntry getEntry(String name) {
85 | return this.parent.getEntry(name);
86 | }
87 |
88 | @Override
89 | InputStream getInputStream() throws IOException {
90 | return this.parent.getInputStream();
91 | }
92 |
93 | @Override
94 | public synchronized InputStream getInputStream(ZipEntry ze) throws IOException {
95 | return this.parent.getInputStream(ze);
96 | }
97 |
98 | @Override
99 | public String getComment() {
100 | return this.parent.getComment();
101 | }
102 |
103 | @Override
104 | public int size() {
105 | return this.parent.size();
106 | }
107 |
108 | @Override
109 | public String toString() {
110 | return this.parent.toString();
111 | }
112 |
113 | @Override
114 | public String getName() {
115 | return this.parent.getName();
116 | }
117 |
118 | static JarFile unwrap(java.util.jar.JarFile jarFile) {
119 | if (jarFile instanceof JarFile) {
120 | return (JarFile) jarFile;
121 | }
122 | if (jarFile instanceof JarFileWrapper) {
123 | return unwrap(((JarFileWrapper) jarFile).parent);
124 | }
125 | throw new IllegalStateException("Not a JarFile or Wrapper");
126 | }
127 |
128 | }
129 |
--------------------------------------------------------------------------------
/spring-hot-plugin-loader/src/main/java/vip/aliali/spring/plugin/loader/jar/StringSequence.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2020 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package vip.aliali.spring.plugin.loader.jar;
18 |
19 | import java.util.Objects;
20 |
21 | /**
22 | * A {@link CharSequence} backed by a single shared {@link String}. Unlike a regular
23 | * {@link String}, {@link #subSequence(int, int)} operations will not copy the underlying
24 | * character array.
25 | *
26 | * @author Phillip Webb
27 | */
28 | final class StringSequence implements CharSequence {
29 |
30 | private final String source;
31 |
32 | private final int start;
33 |
34 | private final int end;
35 |
36 | private int hash;
37 |
38 | StringSequence(String source) {
39 | this(source, 0, (source != null) ? source.length() : -1);
40 | }
41 |
42 | StringSequence(String source, int start, int end) {
43 | Objects.requireNonNull(source, "Source must not be null");
44 | if (start < 0) {
45 | throw new StringIndexOutOfBoundsException(start);
46 | }
47 | if (end > source.length()) {
48 | throw new StringIndexOutOfBoundsException(end);
49 | }
50 | this.source = source;
51 | this.start = start;
52 | this.end = end;
53 | }
54 |
55 | StringSequence subSequence(int start) {
56 | return subSequence(start, length());
57 | }
58 |
59 | @Override
60 | public StringSequence subSequence(int start, int end) {
61 | int subSequenceStart = this.start + start;
62 | int subSequenceEnd = this.start + end;
63 | if (subSequenceStart > this.end) {
64 | throw new StringIndexOutOfBoundsException(start);
65 | }
66 | if (subSequenceEnd > this.end) {
67 | throw new StringIndexOutOfBoundsException(end);
68 | }
69 | if (start == 0 && subSequenceEnd == this.end) {
70 | return this;
71 | }
72 | return new StringSequence(this.source, subSequenceStart, subSequenceEnd);
73 | }
74 |
75 | /**
76 | * Returns {@code true} if the sequence is empty. Public to be compatible with JDK 15.
77 | * @return {@code true} if {@link #length()} is {@code 0}, otherwise {@code false}
78 | */
79 | public boolean isEmpty() {
80 | return length() == 0;
81 | }
82 |
83 | @Override
84 | public int length() {
85 | return this.end - this.start;
86 | }
87 |
88 | @Override
89 | public char charAt(int index) {
90 | return this.source.charAt(this.start + index);
91 | }
92 |
93 | int indexOf(char ch) {
94 | return this.source.indexOf(ch, this.start) - this.start;
95 | }
96 |
97 | int indexOf(String str) {
98 | return this.source.indexOf(str, this.start) - this.start;
99 | }
100 |
101 | int indexOf(String str, int fromIndex) {
102 | return this.source.indexOf(str, this.start + fromIndex) - this.start;
103 | }
104 |
105 | boolean startsWith(String prefix) {
106 | return startsWith(prefix, 0);
107 | }
108 |
109 | boolean startsWith(String prefix, int offset) {
110 | int prefixLength = prefix.length();
111 | int length = length();
112 | if (length - prefixLength - offset < 0) {
113 | return false;
114 | }
115 | return this.source.startsWith(prefix, this.start + offset);
116 | }
117 |
118 | @Override
119 | public boolean equals(Object obj) {
120 | if (this == obj) {
121 | return true;
122 | }
123 | if (!(obj instanceof CharSequence)) {
124 | return false;
125 | }
126 | CharSequence other = (CharSequence) obj;
127 | int n = length();
128 | if (n != other.length()) {
129 | return false;
130 | }
131 | int i = 0;
132 | while (n-- != 0) {
133 | if (charAt(i) != other.charAt(i)) {
134 | return false;
135 | }
136 | i++;
137 | }
138 | return true;
139 | }
140 |
141 | @Override
142 | public int hashCode() {
143 | int hash = this.hash;
144 | if (hash == 0 && length() > 0) {
145 | for (int i = this.start; i < this.end; i++) {
146 | hash = 31 * hash + this.source.charAt(i);
147 | }
148 | this.hash = hash;
149 | }
150 | return hash;
151 | }
152 |
153 | @Override
154 | public String toString() {
155 | return this.source.substring(this.start, this.end);
156 | }
157 |
158 | }
159 |
--------------------------------------------------------------------------------
/spring-hot-plugin-loader/src/main/java/vip/aliali/spring/plugin/loader/jar/ZipInflaterInputStream.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2019 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package vip.aliali.spring.plugin.loader.jar;
18 |
19 | import java.io.EOFException;
20 | import java.io.IOException;
21 | import java.io.InputStream;
22 | import java.util.zip.Inflater;
23 | import java.util.zip.InflaterInputStream;
24 |
25 | /**
26 | * {@link InflaterInputStream} that supports the writing of an extra "dummy" byte (which
27 | * is required with JDK 6) and returns accurate available() results.
28 | *
29 | * @author Phillip Webb
30 | */
31 | class ZipInflaterInputStream extends InflaterInputStream {
32 |
33 | private int available;
34 |
35 | private boolean extraBytesWritten;
36 |
37 | ZipInflaterInputStream(InputStream inputStream, int size) {
38 | super(inputStream, new Inflater(true), getInflaterBufferSize(size));
39 | this.available = size;
40 | }
41 |
42 | @Override
43 | public int available() throws IOException {
44 | if (this.available < 0) {
45 | return super.available();
46 | }
47 | return this.available;
48 | }
49 |
50 | @Override
51 | public int read(byte[] b, int off, int len) throws IOException {
52 | int result = super.read(b, off, len);
53 | if (result != -1) {
54 | this.available -= result;
55 | }
56 | return result;
57 | }
58 |
59 | @Override
60 | public void close() throws IOException {
61 | super.close();
62 | this.inf.end();
63 | }
64 |
65 | @Override
66 | protected void fill() throws IOException {
67 | try {
68 | super.fill();
69 | }
70 | catch (EOFException ex) {
71 | if (this.extraBytesWritten) {
72 | throw ex;
73 | }
74 | this.len = 1;
75 | this.buf[0] = 0x0;
76 | this.extraBytesWritten = true;
77 | this.inf.setInput(this.buf, 0, this.len);
78 | }
79 | }
80 |
81 | private static int getInflaterBufferSize(long size) {
82 | size += 2; // inflater likes some space
83 | size = (size > 65536) ? 8192 : size;
84 | size = (size <= 0) ? 4096 : size;
85 | return (int) size;
86 | }
87 |
88 | }
89 |
--------------------------------------------------------------------------------
/spring-hot-plugin-loader/src/main/java/vip/aliali/spring/plugin/loader/jar/package-info.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2019 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | /**
18 | * Support for loading and manipulating JAR/WAR files.
19 | */
20 | package vip.aliali.spring.plugin.loader.jar;
21 |
--------------------------------------------------------------------------------
/spring-hot-plugin-loader/src/main/java/vip/aliali/spring/plugin/loader/jarmode/JarMode.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2020 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package vip.aliali.spring.plugin.loader.jarmode;
18 |
19 | /**
20 | * Interface registered in {@code spring.factories} to provides extended 'jarmode'
21 | * support.
22 | *
23 | * @author Phillip Webb
24 | * @since 2.3.0
25 | */
26 | public interface JarMode {
27 |
28 | /**
29 | * Returns if this accepts and can run the given mode.
30 | * @param mode the mode to check
31 | * @return if this instance accepts the mode
32 | */
33 | boolean accepts(String mode);
34 |
35 | /**
36 | * Run the jar in the given mode.
37 | * @param mode the mode to use
38 | * @param args any program arguments
39 | */
40 | void run(String mode, String[] args);
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/spring-hot-plugin-loader/src/main/java/vip/aliali/spring/plugin/loader/jarmode/JarModeLauncher.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2020 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package vip.aliali.spring.plugin.loader.jarmode;
18 |
19 | import org.springframework.core.io.support.SpringFactoriesLoader;
20 | import org.springframework.util.ClassUtils;
21 |
22 | import java.util.List;
23 |
24 | /**
25 | * Delegate class used to launch the fat jar in a specific mode.
26 | *
27 | * @author Phillip Webb
28 | * @since 2.3.0
29 | */
30 | public final class JarModeLauncher {
31 |
32 | static final String DISABLE_SYSTEM_EXIT = JarModeLauncher.class.getName() + ".DISABLE_SYSTEM_EXIT";
33 |
34 | private JarModeLauncher() {
35 | }
36 |
37 | public static void main(String[] args) {
38 | String mode = System.getProperty("jarmode");
39 | List candidates = SpringFactoriesLoader.loadFactories(JarMode.class,
40 | ClassUtils.getDefaultClassLoader());
41 | for (JarMode candidate : candidates) {
42 | if (candidate.accepts(mode)) {
43 | candidate.run(mode, args);
44 | return;
45 | }
46 | }
47 | System.err.println("Unsupported jarmode '" + mode + "'");
48 | if (!Boolean.getBoolean(DISABLE_SYSTEM_EXIT)) {
49 | System.exit(1);
50 | }
51 | }
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/spring-hot-plugin-loader/src/main/java/vip/aliali/spring/plugin/loader/jarmode/TestJarMode.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2020 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package vip.aliali.spring.plugin.loader.jarmode;
18 |
19 | import java.util.Arrays;
20 |
21 | /**
22 | * {@link JarMode} for testing.
23 | *
24 | * @author Phillip Webb
25 | */
26 | class TestJarMode implements JarMode {
27 |
28 | @Override
29 | public boolean accepts(String mode) {
30 | return "test".equals(mode);
31 | }
32 |
33 | @Override
34 | public void run(String mode, String[] args) {
35 | System.out.println("running in " + mode + " jar mode " + Arrays.asList(args));
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/spring-hot-plugin-loader/src/main/java/vip/aliali/spring/plugin/loader/jarmode/package-info.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2020 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | /**
18 | * Support for launching the JAR using jarmode.
19 | *
20 | * @see vip.aliali.spring.plugin.loader.jarmode.JarModeLauncher
21 | */
22 | package vip.aliali.spring.plugin.loader.jarmode;
23 |
--------------------------------------------------------------------------------
/spring-hot-plugin-loader/src/main/java/vip/aliali/spring/plugin/loader/package-info.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2019 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | /**
18 | * System that allows self-contained JAR/WAR archives to be launched using
19 | * {@code java -jar}. Archives can include nested packaged dependency JARs (there is no
20 | * need to create shade style jars) and are executed without unpacking. The only
21 | * constraint is that nested JARs must be stored in the archive uncompressed.
22 | *
23 | * @see vip.aliali.spring.plugin.loader.JarLauncher
24 | * @see vip.aliali.spring.plugin.loader.WarLauncher
25 | */
26 | package vip.aliali.spring.plugin.loader;
27 |
--------------------------------------------------------------------------------
/spring-hot-plugin-loader/src/main/java/vip/aliali/spring/plugin/loader/util/package-info.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2019 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | /**
18 | * Utilities used by Spring Boot's JAR loading.
19 | */
20 | package vip.aliali.spring.plugin.loader.util;
21 |
--------------------------------------------------------------------------------
/spring-hot-plugin-loader/src/test/java/AbstractExecutableArchiveLauncherTests.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2021 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import vip.aliali.spring.plugin.loader.archive.Archive;
18 | import org.junit.jupiter.api.io.TempDir;
19 | import org.springframework.util.FileCopyUtils;
20 |
21 | import java.io.*;
22 | import java.net.MalformedURLException;
23 | import java.net.URL;
24 | import java.nio.charset.StandardCharsets;
25 | import java.util.*;
26 | import java.util.jar.JarEntry;
27 | import java.util.jar.JarFile;
28 | import java.util.jar.JarOutputStream;
29 | import java.util.jar.Manifest;
30 | import java.util.zip.CRC32;
31 | import java.util.zip.ZipEntry;
32 |
33 | /**
34 | * 用来创建临时测试的jar包
35 | */
36 | public abstract class AbstractExecutableArchiveLauncherTests {
37 |
38 | @TempDir
39 | File tempDir;
40 |
41 | protected File createJarArchive(String name, String entryPrefix) throws IOException {
42 | return createJarArchive(name, entryPrefix, false, Collections.emptyList());
43 | }
44 |
45 | @SuppressWarnings("resource")
46 | protected File createJarArchive(String name, String entryPrefix, boolean indexed, List extraLibs)
47 | throws IOException {
48 | return createJarArchive(name, null, entryPrefix, indexed, extraLibs);
49 | }
50 |
51 | @SuppressWarnings("resource")
52 | protected File createJarArchive(String name, Manifest manifest, String entryPrefix, boolean indexed,
53 | List extraLibs) throws IOException {
54 | File archive = new File(this.tempDir, name);
55 | JarOutputStream jarOutputStream = new JarOutputStream(new FileOutputStream(archive));
56 | if (manifest != null) {
57 | jarOutputStream.putNextEntry(new JarEntry("META-INF/"));
58 | jarOutputStream.putNextEntry(new JarEntry("META-INF/MANIFEST.MF"));
59 | manifest.write(jarOutputStream);
60 | jarOutputStream.closeEntry();
61 | }
62 | jarOutputStream.putNextEntry(new JarEntry(entryPrefix + "/"));
63 | jarOutputStream.putNextEntry(new JarEntry(entryPrefix + "/classes/"));
64 | jarOutputStream.putNextEntry(new JarEntry(entryPrefix + "/lib/"));
65 | if (indexed) {
66 | jarOutputStream.putNextEntry(new JarEntry(entryPrefix + "/classpath.idx"));
67 | Writer writer = new OutputStreamWriter(jarOutputStream, StandardCharsets.UTF_8);
68 | writer.write("- \"" + entryPrefix + "/lib/foo.jar\"\n");
69 | writer.write("- \"" + entryPrefix + "/lib/bar.jar\"\n");
70 | writer.write("- \"" + entryPrefix + "/lib/baz.jar\"\n");
71 | writer.flush();
72 | jarOutputStream.closeEntry();
73 | }
74 | addNestedJars(entryPrefix, "/lib/foo.jar", jarOutputStream);
75 | addNestedJars(entryPrefix, "/lib/bar.jar", jarOutputStream);
76 | addNestedJars(entryPrefix, "/lib/baz.jar", jarOutputStream);
77 | for (String lib : extraLibs) {
78 | addNestedJars(entryPrefix, "/lib/" + lib, jarOutputStream);
79 | }
80 | jarOutputStream.close();
81 | return archive;
82 | }
83 |
84 | protected File createJarArchive(String name) throws IOException {
85 | File archive = new File(this.tempDir, name);
86 | JarOutputStream jarOutputStream = new JarOutputStream(new FileOutputStream(archive));
87 |
88 | jarOutputStream.putNextEntry(new JarEntry("classes/"));
89 | jarOutputStream.putNextEntry(new JarEntry("lib/"));
90 |
91 | addNestedJars("", "lib/foo.jar", jarOutputStream);
92 | addNestedJars("", "lib/bar.jar", jarOutputStream);
93 | addNestedJars("", "lib/baz.jar", jarOutputStream);
94 | jarOutputStream.close();
95 | return archive;
96 | }
97 |
98 | private void addNestedJars(String entryPrefix, String lib, JarOutputStream jarOutputStream) throws IOException {
99 | JarEntry libFoo = new JarEntry(entryPrefix + lib);
100 | libFoo.setMethod(ZipEntry.STORED);
101 | ByteArrayOutputStream fooJarStream = new ByteArrayOutputStream();
102 | new JarOutputStream(fooJarStream).close();
103 | libFoo.setSize(fooJarStream.size());
104 | CRC32 crc32 = new CRC32();
105 | crc32.update(fooJarStream.toByteArray());
106 | libFoo.setCrc(crc32.getValue());
107 | jarOutputStream.putNextEntry(libFoo);
108 | jarOutputStream.write(fooJarStream.toByteArray());
109 | }
110 |
111 | protected File explode(File archive) throws IOException {
112 | File exploded = new File(this.tempDir, "exploded");
113 | exploded.mkdirs();
114 | JarFile jarFile = new JarFile(archive);
115 | Enumeration entries = jarFile.entries();
116 | while (entries.hasMoreElements()) {
117 | JarEntry entry = entries.nextElement();
118 | File entryFile = new File(exploded, entry.getName());
119 | if (entry.isDirectory()) {
120 | entryFile.mkdirs();
121 | }
122 | else {
123 | FileCopyUtils.copy(jarFile.getInputStream(entry), new FileOutputStream(entryFile));
124 | }
125 | }
126 | jarFile.close();
127 | return exploded;
128 | }
129 |
130 | protected Set getUrls(List archives) throws MalformedURLException {
131 | Set urls = new LinkedHashSet<>(archives.size());
132 | for (Archive archive : archives) {
133 | urls.add(archive.getUrl());
134 | }
135 | return urls;
136 | }
137 |
138 | protected final URL toUrl(File file) {
139 | try {
140 | return file.toURI().toURL();
141 | }
142 | catch (MalformedURLException ex) {
143 | throw new IllegalStateException(ex);
144 | }
145 | }
146 |
147 | }
148 |
--------------------------------------------------------------------------------
/spring-hot-plugin-loader/src/test/java/JarLauncherTest.java:
--------------------------------------------------------------------------------
1 | import vip.aliali.spring.plugin.loader.JarLauncher;
2 | import vip.aliali.spring.plugin.loader.archive.Archive;
3 | import vip.aliali.spring.plugin.loader.archive.JarFileArchive;
4 | import org.junit.jupiter.api.Test;
5 |
6 | import java.io.File;
7 | import java.net.URL;
8 | import java.util.ArrayList;
9 | import java.util.List;
10 |
11 | import static org.assertj.core.api.Assertions.assertThat;
12 |
13 | /**
14 | * Jar 包读取测试类
15 | * @author 阿提说说
16 | */
17 | public class JarLauncherTest extends AbstractExecutableArchiveLauncherTests {
18 |
19 | @Test
20 | public void test() throws Exception {
21 | File jarRoot = createJarArchive("archive.jar");
22 | try (JarFileArchive archive = new JarFileArchive(jarRoot)) {
23 | JarLauncher launcher = new JarLauncher(archive);
24 | List classPathArchives = new ArrayList<>();
25 | launcher.getClassPathArchivesIterator().forEachRemaining(classPathArchives::add);
26 | assertThat(classPathArchives).hasSize(4);
27 | assertThat(getUrls(classPathArchives)).containsOnly(
28 | new URL("jar:" + jarRoot.toURI().toURL() + "!/classes!/"),
29 | new URL("jar:" + jarRoot.toURI().toURL() + "!/lib/foo.jar!/"),
30 | new URL("jar:" + jarRoot.toURI().toURL() + "!/lib/bar.jar!/"),
31 | new URL("jar:" + jarRoot.toURI().toURL() + "!/lib/baz.jar!/"));
32 | for (Archive classPathArchive : classPathArchives) {
33 | classPathArchive.close();
34 | }
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/spring-hot-plugin-maven/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | !.mvn/wrapper/maven-wrapper.jar
3 | !**/src/main/**/target/
4 | !**/src/test/**/target/
5 |
6 | ### IntelliJ IDEA ###
7 | .idea/modules.xml
8 | .idea/jarRepositories.xml
9 | .idea/compiler.xml
10 | .idea/libraries/
11 | *.iws
12 | *.iml
13 | *.ipr
14 |
15 | ### Eclipse ###
16 | .apt_generated
17 | .classpath
18 | .factorypath
19 | .project
20 | .settings
21 | .springBeans
22 | .sts4-cache
23 |
24 | ### NetBeans ###
25 | /nbproject/private/
26 | /nbbuild/
27 | /dist/
28 | /nbdist/
29 | /.nb-gradle/
30 | build/
31 | !**/src/main/**/build/
32 | !**/src/test/**/build/
33 |
34 | ### VS Code ###
35 | .vscode/
36 |
37 | ### Mac OS ###
38 | .DS_Store
--------------------------------------------------------------------------------
/spring-hot-plugin-maven/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | vip.aliali.spring
8 | spring-hot-plugin-parent
9 | 1.1.1
10 |
11 |
12 | spring-hot-plugin-maven
13 | maven-plugin
14 | 插件Maven打包组件
15 |
16 |
17 | 8
18 | 8
19 | UTF-8
20 | 3.9.8
21 | 3.4.0
22 | 3.6.2
23 | 3.9.8
24 | 3.2
25 |
26 |
27 |
28 |
29 |
30 | org.apache.maven
31 | maven-plugin-api
32 | ${maven-plugin-api.version}
33 |
34 |
35 |
36 | org.apache.maven.shared
37 | maven-common-artifact-filters
38 | ${maven-common-artifact-filters.version}
39 |
40 |
41 |
42 |
43 | org.apache.maven
44 | maven-archiver
45 | ${maven-archiver.version}
46 |
47 |
48 |
49 | org.apache.maven
50 | maven-core
51 | ${maven-core.version}
52 |
53 |
54 |
55 | org.apache.maven.plugin-tools
56 | maven-plugin-annotations
57 | ${maven-plugin-annotations.version}
58 | provided
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | org.apache.maven.plugins
67 | maven-plugin-plugin
68 | 3.13.1
69 |
70 | repackage
71 |
72 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/spring-hot-plugin-maven/src/main/java/vip/aliali/spring/plugin/maven/PackageConstants.java:
--------------------------------------------------------------------------------
1 | package vip.aliali.spring.plugin.maven;
2 |
3 | public class PackageConstants {
4 |
5 | public static final String CLASS_DIR = "classes";
6 |
7 | public static final String LIB_DIR = "lib";
8 |
9 | public static final String PLUGIN_ID = "pluginId";
10 |
11 | public static final String PLUGIN_VERSION = "pluginVersion";
12 |
13 | public static final String PLUGIN_DES = "pluginDescription";
14 |
15 | public static final String DEVELOPER_NAME = "developers";
16 | }
17 |
--------------------------------------------------------------------------------
/spring-hot-plugin-maven/src/main/java/vip/aliali/spring/plugin/maven/PackagePlugin.java:
--------------------------------------------------------------------------------
1 | package vip.aliali.spring.plugin.maven;
2 |
3 | import org.apache.commons.io.FileUtils;
4 | import org.apache.commons.io.filefilter.TrueFileFilter;
5 | import org.apache.commons.lang3.StringUtils;
6 | import org.apache.maven.archiver.MavenArchiveConfiguration;
7 | import org.apache.maven.archiver.MavenArchiver;
8 | import org.apache.maven.artifact.Artifact;
9 | import org.apache.maven.artifact.DependencyResolutionRequiredException;
10 | import org.apache.maven.execution.MavenSession;
11 | import org.apache.maven.plugin.AbstractMojo;
12 | import org.apache.maven.plugin.MojoExecutionException;
13 | import org.apache.maven.plugin.MojoFailureException;
14 | import org.apache.maven.plugins.annotations.Component;
15 | import org.apache.maven.plugins.annotations.Mojo;
16 | import org.apache.maven.plugins.annotations.Parameter;
17 | import org.apache.maven.project.MavenProject;
18 | import org.apache.maven.shared.artifact.filter.ScopeArtifactFilter;
19 | import org.codehaus.plexus.archiver.jar.JarArchiver;
20 | import org.codehaus.plexus.archiver.jar.ManifestException;
21 |
22 | import java.io.File;
23 | import java.io.IOException;
24 | import java.util.Collection;
25 | import java.util.Set;
26 |
27 |
28 | /**
29 | * 插件打包工具
30 | */
31 | @Mojo(name = "repackage")
32 | public class PackagePlugin extends AbstractMojo {
33 |
34 | @Parameter(defaultValue = "${project.build.directory}/classes", readonly = true)
35 | private File classesDir;
36 |
37 | @Parameter(defaultValue = "${project.build.directory}/${project.build.finalName}-repackage.jar", readonly = true)
38 | private File jarFile;
39 |
40 | @Parameter(defaultValue = "${project}", readonly = true)
41 | private MavenProject project;
42 |
43 | @Component
44 | private MavenSession mavenSession;
45 |
46 |
47 |
48 | @Override
49 | public void execute() throws MojoExecutionException, MojoFailureException {
50 | //打包
51 | try {
52 | packageJar();
53 | } catch (Exception ex) {
54 | getLog().error(ex);
55 | }
56 | }
57 |
58 | private void packageJar() throws DependencyResolutionRequiredException, IOException, ManifestException {
59 | MavenArchiver mavenArchiver = createJarPackage();
60 | addClasses(mavenArchiver);
61 | addArtifacts(mavenArchiver);
62 | writeJarPackage(mavenArchiver);
63 | }
64 |
65 | private void writeJarPackage(MavenArchiver mavenArchiver) throws DependencyResolutionRequiredException, IOException, ManifestException {
66 | mavenArchiver.createArchive(mavenSession, project, getJarConfiguration());
67 | }
68 |
69 | private void addClasses(MavenArchiver mavenArchiver) {
70 | getLog().info("Start packing the class files......");
71 |
72 | Collection classesList = FileUtils.listFiles(classesDir, TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE);
73 | for (File file : classesList) {
74 | mavenArchiver.getArchiver().addFile(file, PackageConstants.CLASS_DIR + getRelativeFilePath(file));
75 | }
76 | }
77 |
78 | private String getRelativeFilePath(File file) {
79 | return StringUtils.substringAfter(file.getAbsolutePath(), PackageConstants.CLASS_DIR);
80 | }
81 |
82 | private void addArtifacts(MavenArchiver mavenArchiver) {
83 | getLog().info("Start packing the artifacts files......");
84 |
85 | //依赖
86 | Set artifacts = filterArtifacts();
87 | for (Artifact artifact : artifacts) {
88 | mavenArchiver.getArchiver().addFile(artifact.getFile(), PackageConstants.LIB_DIR + File.separator + artifact.getFile().getName());
89 | }
90 | }
91 |
92 | private MavenArchiveConfiguration getJarConfiguration() {
93 | getLog().info("Start packing the Manifest file......");
94 |
95 | MavenArchiveConfiguration jarConfiguration = new MavenArchiveConfiguration();
96 | jarConfiguration.addManifestEntry(PackageConstants.PLUGIN_ID, project.getModel().getArtifactId());
97 | jarConfiguration.addManifestEntry(PackageConstants.PLUGIN_VERSION, project.getModel().getVersion());
98 | jarConfiguration.addManifestEntry(PackageConstants.PLUGIN_DES, project.getModel().getDescription());
99 | jarConfiguration.addManifestEntry(PackageConstants.DEVELOPER_NAME, String.join(";", project.getModel().getDevelopers().toArray(new String[0])));
100 |
101 | jarConfiguration.setCompress(false);
102 | return jarConfiguration;
103 | }
104 |
105 | private MavenArchiver createJarPackage() {
106 | MavenArchiver mavenArchiver = new MavenArchiver();
107 | mavenArchiver.setOutputFile(jarFile);
108 | mavenArchiver.setArchiver(new JarArchiver());
109 | return mavenArchiver;
110 | }
111 |
112 |
113 | private Set filterArtifacts() {
114 | ScopeArtifactFilter scopeArtifactFilter = new ScopeArtifactFilter();
115 | scopeArtifactFilter.setIncludeRuntimeScopeWithImplications(true);
116 | project.setArtifactFilter(scopeArtifactFilter);
117 | return project.getArtifacts();
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/spring-hot-plugin-mybatis/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | !.mvn/wrapper/maven-wrapper.jar
3 | !**/src/main/**/target/
4 | !**/src/test/**/target/
5 |
6 | ### IntelliJ IDEA ###
7 | .idea/modules.xml
8 | .idea/jarRepositories.xml
9 | .idea/compiler.xml
10 | .idea/libraries/
11 | *.iws
12 | *.iml
13 | *.ipr
14 |
15 | ### Eclipse ###
16 | .apt_generated
17 | .classpath
18 | .factorypath
19 | .project
20 | .settings
21 | .springBeans
22 | .sts4-cache
23 |
24 | ### NetBeans ###
25 | /nbproject/private/
26 | /nbbuild/
27 | /dist/
28 | /nbdist/
29 | /.nb-gradle/
30 | build/
31 | !**/src/main/**/build/
32 | !**/src/test/**/build/
33 |
34 | ### VS Code ###
35 | .vscode/
36 |
37 | ### Mac OS ###
38 | .DS_Store
--------------------------------------------------------------------------------
/spring-hot-plugin-mybatis/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | vip.aliali.spring
8 | spring-hot-plugin-parent
9 | 1.1.1
10 |
11 |
12 | spring-hot-plugin-mybatis
13 | 插件Mybatis、MybatisPlus组件
14 |
15 |
16 | 8
17 | 8
18 | UTF-8
19 | 3.5.7
20 |
21 |
22 |
23 |
24 | org.springframework.boot
25 | spring-boot-starter-web
26 | provided
27 | true
28 |
29 |
30 |
31 | org.mybatis.spring.boot
32 | mybatis-spring-boot-starter
33 | ${mybatis.version}
34 | provided
35 | true
36 |
37 |
38 |
39 | com.baomidou
40 | mybatis-plus-core
41 | ${mp.version}
42 | true
43 |
44 |
45 |
46 | com.baomidou
47 | mybatis-plus-extension
48 | ${mp.version}
49 | true
50 |
51 |
52 |
53 | vip.aliali.spring
54 | spring-hot-plugin-core
55 | ${project.version}
56 | provided
57 |
58 |
59 |
60 | vip.aliali.spring
61 | spring-hot-plugin-common
62 | ${project.version}
63 |
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/spring-hot-plugin-mybatis/src/main/java/vip/aliali/spring/plugin/mybatis/MybatisPluginConfiguration.java:
--------------------------------------------------------------------------------
1 | package vip.aliali.spring.plugin.mybatis;
2 |
3 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
4 | import org.springframework.context.ApplicationContext;
5 | import org.springframework.context.annotation.Bean;
6 | import org.springframework.context.annotation.Configuration;
7 |
8 | @Configuration
9 | public class MybatisPluginConfiguration {
10 |
11 | @Bean("registerMybatis")
12 | @ConditionalOnClass(name = "org.apache.ibatis.session.SqlSession")
13 | public RegisterMybatis createRegisterMybatis(ApplicationContext main) {
14 | return new RegisterMybatis(main);
15 | }
16 |
17 | /**
18 | * 用来处理MybatisPlus
19 | * @return MybatisPlus的处理工具
20 | */
21 | @Bean("mybatisPlusUtil")
22 | @ConditionalOnClass(name = "com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration")
23 | public MybatisPlusUtil createMybatisPluginUtil() {
24 | return new MybatisPlusUtil();
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/spring-hot-plugin-mybatis/src/main/java/vip/aliali/spring/plugin/mybatis/MybatisPlusUtil.java:
--------------------------------------------------------------------------------
1 | package vip.aliali.spring.plugin.mybatis;
2 |
3 | import cn.hutool.core.util.ReflectUtil;
4 | import com.baomidou.mybatisplus.core.MybatisConfiguration;
5 | import com.baomidou.mybatisplus.core.MybatisMapperRegistry;
6 | import com.baomidou.mybatisplus.core.mapper.Mapper;
7 | import com.baomidou.mybatisplus.core.metadata.TableInfo;
8 | import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
9 | import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;
10 | import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
11 | import com.baomidou.mybatisplus.core.toolkit.StringPool;
12 | import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
13 | import org.apache.ibatis.mapping.MappedStatement;
14 | import org.apache.ibatis.mapping.ResultMap;
15 | import org.apache.ibatis.session.Configuration;
16 | import org.mybatis.spring.mapper.MapperFactoryBean;
17 | import org.springframework.context.ApplicationContext;
18 |
19 | import java.util.Map;
20 | import java.util.Set;
21 | import java.util.stream.Collectors;
22 |
23 | /**
24 | * 用来处理MybatisPlus的工具类
25 | * @author 阿提说说
26 | */
27 | public class MybatisPlusUtil {
28 |
29 |
30 | public void setMPConfiguration(Configuration configuration) {
31 | if (configuration instanceof MybatisConfiguration) {
32 | MybatisConfiguration mpConfiguration = (MybatisConfiguration) configuration;
33 | //关闭mappedStatement的短key缓存生成
34 | mpConfiguration.setUseGeneratedShortKey(false);
35 | }
36 | }
37 |
38 | public void clearMPQuote(Configuration configuration, ApplicationContext plugin) {
39 | //处理使用MP时候的引用
40 | if (configuration instanceof MybatisConfiguration) {
41 | MybatisConfiguration mpConfiguration = (MybatisConfiguration) configuration;
42 | //当前插件加载的mapper
43 | Map mapperFactoryBeanMap = plugin.getBeansOfType(MapperFactoryBean.class);
44 | Set mapperRegistryCache = GlobalConfigUtils.getGlobalConfig(mpConfiguration).getMapperRegistryCache();
45 | for (MapperFactoryBean mapperFactoryBean : mapperFactoryBeanMap.values()) {
46 | Class> mapper = mapperFactoryBean.getMapperInterface();
47 |
48 | //清除MP没有清除掉的引用缓存
49 | Class> modelClass = ReflectionKit.getSuperClassGenericType(mapper, Mapper.class, 0);
50 | TableInfo tableInfo = TableInfoHelper.getTableInfo(modelClass);
51 | //null 表示已经清理过
52 | if (tableInfo != null) {
53 | //清除Model 的Class引用缓存
54 | ((Map) ReflectUtil.getFieldValue(mpConfiguration.getReflectorFactory(), "reflectorMap")).remove(modelClass);
55 | ((Map)ReflectUtil.getFieldValue(ReflectionKit.class, "CLASS_FIELD_CACHE")).remove(modelClass);
56 | ((Map)ReflectUtil.getFieldValue(TableInfoHelper.class, "TABLE_NAME_INFO_CACHE")).remove(tableInfo.getTableName());
57 | //删除类型转换处理器中的引用
58 | ((Map)ReflectUtil.getFieldValue(configuration.getTypeHandlerRegistry(), "typeHandlerMap")).remove(modelClass);
59 | // 清空实体表信息映射信息
60 | TableInfoHelper.remove(modelClass);
61 | }
62 |
63 | // 清空 Mapper 缓存信息
64 | final String mapperType = mapper.toString();
65 | MybatisMapperRegistry mapperRegistry = ((MybatisMapperRegistry) ReflectUtil.getFieldValue(mpConfiguration, "mybatisMapperRegistry"));
66 | ReflectUtil.invoke(mapperRegistry, "removeMapper", mapper);
67 | //删除MappedStatement缓存
68 | Map mappedStatements = ((Map) ReflectUtil.getFieldValue(mpConfiguration, "mappedStatements"));
69 | final String typeKey = mapper.getName() + StringPool.DOT;
70 | Set mapperSet = mappedStatements.keySet().stream().filter(ms -> ms.startsWith(typeKey)).collect(Collectors.toSet());
71 | if (!mapperSet.isEmpty()) {
72 | mapperSet.forEach(mappedStatements::remove);
73 | }
74 | //删除ResultMap缓存
75 | Map resultMaps = ((Map)ReflectUtil.getFieldValue(mpConfiguration, "resultMaps"));
76 | Set resultMapSet = resultMaps.keySet().stream().filter(ms -> ms.startsWith(typeKey)).collect(Collectors.toSet());
77 | if (!resultMapSet.isEmpty()) {
78 | resultMapSet.forEach(resultMaps::remove);
79 | }
80 | //删除MP的CRUD的Mapper缓存
81 | mapperRegistryCache.remove(mapperType);
82 |
83 | //重置资源加载标识
84 | Set loadedResources = ((Set)ReflectUtil.getFieldValue(configuration, "loadedResources"));
85 | String xmlResource = getXmlResource(mapper.getName());
86 | loadedResources.remove(xmlResource);
87 | loadedResources.remove(mapper.toString());
88 | }
89 |
90 | SqlHelper.FACTORY = null;
91 | }
92 | }
93 |
94 | private String getXmlResource(String name) {
95 | return name.replace('.', '/') + ".xml";
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/spring-hot-plugin-mybatis/src/main/java/vip/aliali/spring/plugin/mybatis/util/MapperUtils.java:
--------------------------------------------------------------------------------
1 | package vip.aliali.spring.plugin.mybatis.util;
2 |
3 | import cn.hutool.core.io.FileUtil;
4 | import cn.hutool.core.text.CharSequenceUtil;
5 | import vip.aliali.spring.plugin.constants.PluginConstants;
6 | import lombok.extern.slf4j.Slf4j;
7 | import org.apache.ibatis.annotations.Mapper;
8 | import org.springframework.core.annotation.AnnotatedElementUtils;
9 |
10 | import java.io.File;
11 | import java.io.InputStream;
12 | import java.util.Enumeration;
13 | import java.util.HashSet;
14 | import java.util.List;
15 | import java.util.Set;
16 | import java.util.jar.JarEntry;
17 | import java.util.jar.JarFile;
18 |
19 | //暂时无用
20 | @Slf4j
21 | public class MapperUtils {
22 |
23 | /**
24 | * 读取jar包中所有类文件
25 | */
26 | public static Set readJarFile(String jarAddress) {
27 | Set classNameSet = new HashSet<>();
28 |
29 | try(JarFile jarFile = new JarFile(jarAddress)) {
30 | Enumeration entries = jarFile.entries();//遍历整个jar文件
31 | while (entries.hasMoreElements()) {
32 | JarEntry jarEntry = entries.nextElement();
33 | String name = jarEntry.getName();
34 | //读取非依赖包的类文件
35 | if (!name.startsWith(PluginConstants.LIB) && name.endsWith(PluginConstants.CLASS_SUFFIX)) {
36 | String className = name.replaceFirst(PluginConstants.CLASSES + "/", "").replace(PluginConstants.CLASS_SUFFIX, "").replaceAll("/", ".");
37 | // String className = CharSequenceUtil.sub(name, name.lastIndexOf(PluginConstants.JAR_File_DELIMITER), name.indexOf(PluginConstants.CLASS_SUFFIX)).replace(PluginConstants.JAR_File_DELIMITER, "");
38 | classNameSet.add(className);
39 | }
40 | }
41 | } catch (Exception e) {
42 | log.warn("加载jar包失败", e);
43 | }
44 | return classNameSet;
45 | }
46 |
47 | public static InputStream readManifestJarFile(File jarAddress) {
48 | try {
49 | JarFile jarFile = new JarFile(jarAddress);
50 | //遍历整个jar文件
51 | Enumeration entries = jarFile.entries();
52 | while (entries.hasMoreElements()) {
53 | JarEntry jarEntry = entries.nextElement();
54 | String name = jarEntry.getName();
55 | if (name.contains(PluginConstants.MANIFEST)) {
56 | return jarFile.getInputStream(jarEntry);
57 | }
58 | }
59 | } catch (Exception e) {
60 | log.warn("加载jar包失败", e);
61 | }
62 | return null;
63 | }
64 |
65 | public static boolean isMapper(Class> beanType) {
66 | return AnnotatedElementUtils.hasAnnotation(beanType, Mapper.class);
67 | }
68 |
69 | /**
70 | * 类名首字母小写 作为spring容器beanMap的key
71 | */
72 | public static String transformName(String className) {
73 | String tmpstr = className.substring(className.lastIndexOf(".") + 1);
74 | return tmpstr.substring(0, 1).toLowerCase() + tmpstr.substring(1);
75 | }
76 |
77 | /**
78 | * 读取class文件
79 | * @param path
80 | * @return
81 | */
82 | public static Set readClassFile(String path) {
83 | if (path.endsWith(PluginConstants.JAR_SUFFIX)) {
84 | return readJarFile(path);
85 | } else {
86 | List pomFiles = FileUtil.loopFiles(path, file -> file.getName().endsWith(PluginConstants.CLASS_SUFFIX));
87 | Set classNameSet = new HashSet<>();
88 | for (File file : pomFiles) {
89 | String name = file.getPath();
90 | //读取非依赖包的类文件
91 | if (!name.contains(PluginConstants.CLASSES + File.separator + PluginConstants.LIB)) {
92 | String className = CharSequenceUtil.subBetween(name, PluginConstants.CLASSES + File.separator, PluginConstants.CLASS_SUFFIX).replace(File.separator, ".");
93 | classNameSet.add(className);
94 | }
95 | }
96 | return classNameSet;
97 | }
98 | }
99 |
100 |
101 | }
102 |
103 |
--------------------------------------------------------------------------------
/spring-hot-plugin-mybatis/src/main/resources/META-INF/spring.factories:
--------------------------------------------------------------------------------
1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
2 | vip.aliali.spring.plugin.mybatis.MybatisPluginConfiguration
--------------------------------------------------------------------------------