Overview
jshERP includes a powerful plugin system based on springboot-plugin-framework 2.2.1, allowing you to extend functionality without modifying core code.The plugin system supports hot deployment, meaning plugins can be installed, started, stopped, and uninstalled at runtime without restarting the application.
Plugin Framework
Core Dependencies
pom.xml
Plugin Configuration
PluginConfiguration.java
Application Properties
application.properties
- runMode:
dev(development) orprod(production) - pluginPath: Directory where plugin JAR files are stored
- pluginConfigFilePath: Directory for plugin configuration files
Plugin Bean Configuration
PluginBeanConfig.java
Plugin Management API
The system provides REST APIs for plugin management:List Plugins
PluginController.java:51
GET /plugin/list?name=¤tPage=1&pageSize=10
Start Plugin
PluginController.java:147
POST /plugin/start/{pluginId}
Stop Plugin
PluginController.java:113
POST /plugin/stop/{pluginId}
Install Plugin
PluginController.java:243
POST /plugin/uploadInstallPluginJar
Uninstall Plugin
PluginController.java:182
POST /plugin/uninstall/{pluginId}
Check Plugin Existence
PluginController.java:341
GET /plugin/checkByPluginId?pluginIds=plugin1,plugin2
All plugin management operations require admin privileges (user with login name matching
DEFAULT_MANAGER).Creating a Plugin
Plugin Project Structure
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.jsh.erp.plugin</groupId>
<artifactId>demo-plugin</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<dependencies>
<!-- Plugin framework -->
<dependency>
<groupId>com.gitee.starblues</groupId>
<artifactId>springboot-plugin-framework</artifactId>
<version>2.2.1-RELEASE</version>
<scope>provided</scope>
</dependency>
<!-- Spring Boot (provided by host) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.0.0.RELEASE</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<archive>
<manifestEntries>
<Plugin-Class>com.jsh.erp.plugin.DemoPlugin</Plugin-Class>
<Plugin-Id>demo-plugin</Plugin-Id>
<Plugin-Version>1.0.0</Plugin-Version>
<Plugin-Provider>Your Company</Plugin-Provider>
<Plugin-Description>Demo Plugin for jshERP</Plugin-Description>
</manifestEntries>
</archive>
</configuration>
</plugin>
</plugins>
</build>
</project>
package com.jsh.erp.plugin;
import com.gitee.starblues.annotation.AutowiredType;
import com.gitee.starblues.realize.BasePlugin;
import org.pf4j.PluginWrapper;
import org.springframework.stereotype.Component;
/**
* Plugin main class
*/
public class DemoPlugin extends BasePlugin {
public DemoPlugin(PluginWrapper wrapper) {
super(wrapper);
}
@Override
protected void startEvent() {
System.out.println("Demo Plugin started!");
}
@Override
protected void stopEvent() {
System.out.println("Demo Plugin stopped!");
}
@Override
protected void deleteEvent() {
System.out.println("Demo Plugin deleted!");
}
}
package com.jsh.erp.plugin.controller;
import com.gitee.starblues.annotation.AutowiredType;
import com.gitee.starblues.annotation.ConfigDefinition;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/demo")
@ConfigDefinition
public class DemoController {
@GetMapping("/hello")
public String hello() {
return "Hello from Demo Plugin!";
}
@GetMapping("/version")
public String version() {
return "Demo Plugin v1.0.0";
}
}
Plugin REST controllers are automatically prefixed with
/api/plugin/{pluginId}/For example: http://localhost:9999/jshERP-boot/api/plugin/demo-plugin/demo/hellopackage com.jsh.erp.plugin.service;
import com.gitee.starblues.annotation.ConfigDefinition;
import org.springframework.stereotype.Service;
@Service
@ConfigDefinition
public class DemoService {
public String processData(String input) {
return "Processed: " + input;
}
}
Plugin with Database Access
If your plugin needs to access the database:<dependency>
<groupId>com.gitee.starblues</groupId>
<artifactId>springboot-plugin-framework-extension-mybatis</artifactId>
<version>2.2.1-RELEASE</version>
<scope>provided</scope>
</dependency>
package com.jsh.erp.plugin.entity;
public class DemoEntity {
private Long id;
private String name;
private String data;
// getters and setters
}
package com.jsh.erp.plugin.mapper;
import com.gitee.starblues.annotation.ConfigDefinition;
import com.jsh.erp.plugin.entity.DemoEntity;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
@ConfigDefinition
public interface DemoMapper {
@Select("SELECT * FROM demo_table WHERE tenant_id = #{tenantId}")
List<DemoEntity> selectByTenantId(Long tenantId);
}
package com.jsh.erp.plugin.service;
import com.gitee.starblues.annotation.ConfigDefinition;
import com.gitee.starblues.annotation.AutowiredType;
import com.jsh.erp.plugin.mapper.DemoMapper;
import com.jsh.erp.plugin.entity.DemoEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
@ConfigDefinition
public class DemoService {
@Autowired(autowiredType = AutowiredType.PLUGIN_MAIN)
private DemoMapper demoMapper;
public List<DemoEntity> getData(Long tenantId) {
return demoMapper.selectByTenantId(tenantId);
}
}
Plugin Configuration Files
Plugins can have their own configuration files:Create plugin.properties
src/main/resources/plugin.properties
Read Configuration
Deploying Plugins
Plugin Lifecycle
Lifecycle Hooks
Best Practices
1. Use @ConfigDefinition Annotation
Always annotate plugin beans:2. Tenant Awareness
Respect multi-tenancy in queries:3. Soft Delete Pattern
Follow the soft delete pattern:4. Exception Handling
Handle exceptions gracefully:5. Version Compatibility
Specify compatible host versions:Debugging Plugins
Development Mode
Use development mode for easier debugging:application.properties
Logging
Add logging to track plugin behavior:View Plugin Status
Check plugin status via API:Security Considerations
MAC Address Licensing
The system provides MAC address access:PluginController.java:320
Plugin Examples
Common plugin use cases:- Payment Integration: Alipay, WeChat Pay, PayPal
- Logistics Integration: Express tracking APIs
- Reports: Custom report generators
- Notifications: SMS, Email, WeChat notifications
- Import/Export: Excel, PDF converters
- Authentication: LDAP, OAuth2, SAML
Troubleshooting
Plugin Not Loading
- Check plugin manifest in JAR
- Verify
Plugin-Classpoints to correct class - Check logs for errors
- Ensure plugin path is correct
Database Access Fails
- Verify
SpringBootMybatisExtensionis added - Check
@ConfigDefinitionon mappers - Use correct
AutowiredType.PLUGIN_MAIN
API Endpoints Not Working
- Verify controller path prefix
- Check
@ConfigDefinitionannotation - Ensure plugin is started
Related Resources
- System Architecture - Core system design
- Customization Guide - Other extension methods
- API Reference - Using plugin APIs