背景
在项目中,经常遇到这种情况:某个功能,需要根据不同条件,搭载不同的插件。
如一个To B的产品,某些客户安装时需要开启a插件,而一些客户安装时需要开启b插件。
本文介绍一种基于spring的插件机制,特点:
- 插件是jar;
- 使用配置文件设置插件的开启或关闭。
示例
下面例子中一共包含三个模块:
- plugin_inte:插件接口协议
- pugin_a:a插件,此文中只演示了一个插件a,当然,可以有b、c...插件,本人懒,只写了一个,嗨嗨~
- demo:业务模块,在该模块中集成插件
1. 首先创建一个接口模块:plugin_inte
- pom.xml
<?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.lk.demo.plugin</groupId>
<artifactId>plugin_inte</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.1.4.RELEASE</version>
</dependency>
</dependencies>
</project>
- 接口类:
package com.lk.demo.plugin;
import java.util.HashMap;
public interface PluginHanler {
public Object invoke(String companyCode, HashMap<String, Object> params);
}
2. 然后编写插件模块:pugin_a
- pom.xml
<?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.lk.demo.plugin</groupId>
<artifactId>pugin-a</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>com.lk.demo.plugin</groupId>
<artifactId>plugin_inte</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
- A插件实现类
package com.lk.demo.plugin;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import java.util.HashMap;
@Component
@ConditionalOnProperty(prefix = "demo.plugin.a", name = "enabled", havingValue = "true")
public class APluginHandler implements PluginHanler {
public Object invoke(String companyCode, HashMap<String, Object> params) {
System.out.println("a plugin");
return null;
}
}
3. 最后,在业务模块(demo)中集成插件
- pom.xml
<?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.lk</groupId>
<artifactId>demo</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<!--插件api-->
<dependency>
<groupId>com.lk.demo.plugin</groupId>
<artifactId>plugin_inte</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--a插件-->
<dependency>
<groupId>com.lk.demo.plugin</groupId>
<artifactId>pugin-a</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
- 插件注册表:PluginRegistry
package com.lk.demo.plugin.register;
import com.lk.demo.plugin.PluginHanler;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
@Component
public class PluginRegistry {
private static final List<PluginHanler> list = new ArrayList<>();
public static void add(PluginHanler plugin){
list.add(plugin);
}
public List<PluginHanler> getPlugins(){
return list;
}
}
- 插件类PostProcessor:PluginBeanPostProcessor
package com.lk.demo.plugin.register;
import com.lk.demo.plugin.PluginHanler;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
@Component
public class PluginBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if(bean instanceof PluginHanler){
//add registry
PluginRegistry.add((PluginHanler)bean);
}
return bean;
}
}
- 配置类:Config
package com.lk.demo.plugin;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.PropertySource;
@ComponentScan("com.lk.demo")
@PropertySource("application.properties")
public class Config {
}
- 属性文件:application.properties
demo.plugin.a.enabled=true
- 测试所有的插件
package com.lk.demo.plugin;
import com.lk.demo.plugin.register.PluginRegistry;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import java.util.List;
public class DemoMain {
public static void main(String[] args) throws InterruptedException {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);
PluginRegistry registry = ac.getBean(PluginRegistry.class);
List<PluginHanler> plugins = registry.getPlugins();
plugins.forEach(p->{
System.out.println("plugin handler="+p);
});
Thread.sleep(10000000);
}
}
打印结果如下,证明插件被加载了:
plugin handler=com.lk.demo.plugin.APluginHandler@12d4bf7e
我们再来测试一下不加载插件:
把属性文件demo.plugin.a.enabled
改为false,重新启动程序,打印结果并没有打印任何插件。