Controlling complexity is the essence of computer programming. – Brian Kernighan

In the process of daily use of some software, some plug-ins are installed to meet some personalized requirements. For example, we install “Maven Helper” plug-in in IDEA to analyze dependency conflicts. Install “vue-devTool” on your Chrome browser to debug your application. As you can see, almost all mature software has good plug-in support. This article will mainly introduce the basic implementation principle of “plug-in mode”, and related actual combat operation.

The plug-in model

Those of you familiar with GoF’s 23 design patterns should know that the plug-in pattern is not one of them, but is widely used. Its main core purpose is also to enhance the system scalability, but different from ordinary object-oriented extension: ordinary extension often happens inside the system software, while plug-in extension is often reflected outside the software. For example, we used a policy pattern in a project, and when we needed to add a new policy, we had to recompile the code, package it, and release it for the new policy to take effect. Plug-in extensions are mostly added to the software in the form of a new component (such as JAR package), and the system software itself does not need to be repackaged and deployed. Some plug-in modes can even achieve hot deployment, that is, dynamic loading of plug-ins in the process of running the program, so as to achieve real plug and play.

Plug-in architecture

Basic functions of plug-in architecture

  • Definition of plug-ins
  • Plug-in loading
  • Plug-in lifecycle management (install, uninstall, start, stop, update)
  • The interaction mechanism between plug-ins
  • Extensions to plug-ins

Two typical plug-in architecture products

As shown in the figure above, plug-ins in the “flat extension” architecture product are only used to extend local functions, while the core of the “microcore + Extension” architecture itself is just a plug-in loader, and all functions are realized by plug-ins.

Basic implementation principles of plug-in pattern

Within a plug-in framework, the following concepts are usually involved:

  • ExtensionPoint: an ExtensionPoint that indicates a function point that can be extended.
  • Extension is an Extension implementation of ExtensionPoint.
  • PluginDescriptor: Refers to the metadata used to describe the plug-in, which defines the basic description, version, and dependency information needed to run the plug-in. Usually a PluginDescriptor may correspond to a Plugin configuration file.
  • PluginRegistry: plug-in registry, mainly used to register and store plug-ins.
  • PluginManager: Plug-in management used to install and activate plug-in instances.
  • Plugin: Plugin instance. After the PluginManager activates the plug-in, a Plugin instance is generated.

Basic use of the plug-in framework PF4J

PF4J profile

PF4J is a Java lightweight plug-in framework that can dynamically load, execute, and uninstall external plug-ins (jar and ZIP support).

Based on the sample

This DEMO project mainly involves three modules:

  • Plugin-api: Defines extensible interfaces
  • Plugins: Plug-in projects that can contain multiple plug-ins and need to be implementedplugin-apiThe interface defined in
  • Plugin-app: main program, dependentplugin-api, load and executeplugins

Introduce PF4J dependencies

Pf4j </groupId> <artifactId>pf4j</artifactId> <version>3.0.1</version> </dependency>Copy the code

Defining an extensible interface (plugin-API)

First, define an extensible interface that inherits ExtensionPoint:

public interface Greeting extends ExtensionPoint {

    String getGreeting();
}
Copy the code

Extension Interface Implementation (Plugins)

Plug-ins need to implement the interface defined by plugin-API and use the @extension tag:

@Extension
public class WelcomeGreeting implements Greeting {

    public String getGreeting() {
        return "Welcome,xs!!!";
    }
}
Copy the code

If you need to pay attention to some of the plug-in’s lifecycle events, you can do so by inheriting the Plugin class:

Public Class WelcomePlugin extends Plugin {// From the Wrapper object we can get context information such as Plugin Manager and Descriptor public WelcomePlugin(PluginWrapper wrapper) { super(wrapper); } @Override public void start() { System.out.println("WelcomePlugin.start()"); } @Override public void stop() { System.out.println("WelcomePlugin.stop()"); } @Override public void delete() { System.out.println("WelcomePlugin.delete()"); }}Copy the code

Plugins

To package the plugin, write the plugin information to the manifest.mf using maven plugin (package command: package) :

< plugin > < groupId > org. Apache. Maven. Plugins < / groupId > < artifactId > maven - jar - plugin < / artifactId > < version > 2.3.1 < / version > <configuration> <archive> <manifestEntries> < plugin-id >welcome Plugin </ plugin-id > < plugin-version >0.0.1</ plugin-version > <! WelcomePlugin</ plugins.WelcomePlugin</ plugin-class > </manifestEntries> </archive> </configuration> </plugin>Copy the code

Note: plugin-id and plugin-version in manifest.mf are required information.

Load execution plug-in (plugin-app)

Public class Main {public static void Main (String[] args) {// PluginManager = new JarPluginManager(); // Load pluginmanager.loadPlugin (paths.get ("plugins-0.0.1- snapshot.jar ")); / / start specified plug-in (also can load all plug-in) pluginManager. StartPlugin (" welcome - plugin "); / / execution plugin List < the Greeting >'t. = pluginManager getExtensions (the Greeting. Class); for (Greeting greeting : greetings) { System.out.println(">>> " + greeting.getGreeting()); } // stop and uninstall the pluginmanager.stopplugin ("welcome-plugin"); pluginManager.unloadPlugin("welcome-plugin"); }}Copy the code

PF4J framework is integrated with Spring

Introduction of depend on

Introducing dependencies that integrate the Spring framework

<dependency>
    <groupId>org.pf4j</groupId>
    <artifactId>pf4j-spring</artifactId>
    <version>${pf4j-spring.version}</version>
</dependency>    
Copy the code

Create the core configuration Bean

@Configuration public class SpringConfiguration { @Bean public SpringPluginManager pluginManager() { return new SpringPluginManager(); } @Bean @DependsOn("pluginManager") public Greetings greetings() { return new Greetings(); }}Copy the code

Use the PF4J extension as a Spring Bean:

public class Greetings { @Autowired private List<Greeting> greetings; public void printGreetings() { System.out.println(String.format("Found %d extensions for extension point '%s'", greetings.size(), Greeting.class.getName())); for (Greeting greeting : greetings) { System.out.println(">>> " + greeting.getGreeting()); }}}Copy the code

Application startup class

Public class Boot {public static void main(String[] args) {// Annotate the container context ApplicationContext ApplicationContext = new AnnotationConfigApplicationContext(SpringConfiguration.class); / / from the container to get extension point't't = applicationContext. GetBean ('t. Class); greetings.printGreetings(); / / from the container to get the plug-in manager PluginManager PluginManager = applicationContext. GetBean (. PluginManager class); / / offline plugin pluginManager stopPlugins (); }}Copy the code

PF4J framework is integrated with Spring Boot

The integration of PF4J and Spring Boot framework can be extended on the basis of the official PF4J-Spring. The implementation idea is generally the same. Here also provides a good quality three-party extension framework Springboot-plugin-framework. Can be used as a reference for how implementations integrate the Spring Boot framework.

conclusion

In the process of actual project development, if some systems need to be dynamically extended at run time, and can not directly use the policy mode to solve the problem, it is advisable to adopt plug-in architecture for system design. It is also worth noting that plug-in versioning should be treated the same as the main application, and a proper versioning mechanism should be in place, otherwise it is no fun to produce the wrong version of the plug-in.

reference

From Code Farmer to Craftsman