{"id":14972933,"url":"https://github.com/spring-projects/spring-plugin","last_synced_at":"2025-05-13T20:21:45.559Z","repository":{"id":2520307,"uuid":"3496264","full_name":"spring-projects/spring-plugin","owner":"spring-projects","description":null,"archived":false,"fork":false,"pushed_at":"2025-04-17T15:06:36.000Z","size":800,"stargazers_count":461,"open_issues_count":10,"forks_count":121,"subscribers_count":66,"default_branch":"main","last_synced_at":"2025-04-28T11:57:08.663Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/spring-projects.png","metadata":{"files":{"readme":"README.markdown","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2012-02-20T18:06:37.000Z","updated_at":"2025-04-27T03:02:39.000Z","dependencies_parsed_at":"2024-06-19T06:17:28.437Z","dependency_job_id":"4c31cf3d-6f27-408c-9295-cc58b0c796d8","html_url":"https://github.com/spring-projects/spring-plugin","commit_stats":{"total_commits":281,"total_committers":13,"mean_commits":"21.615384615384617","dds":0.3345195729537367,"last_synced_commit":"e39fd453e102e0dabcba483bbb6391e4066e0057"},"previous_names":[],"tags_count":20,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spring-projects%2Fspring-plugin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spring-projects%2Fspring-plugin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spring-projects%2Fspring-plugin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spring-projects%2Fspring-plugin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/spring-projects","download_url":"https://codeload.github.com/spring-projects/spring-plugin/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254020685,"owners_count":22000762,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-09-24T13:47:46.985Z","updated_at":"2025-05-13T20:21:45.504Z","avatar_url":"https://github.com/spring-projects.png","language":"Java","readme":"# The smallest plugin system ever\n\n## Preface\n\n### Introduction\n\nBuilding extensible architectures nowadays is a core principle to create maintainable applications. This is why fully fledged plugin environments like *OSGi* are so popular these days. Unfortunately the introduction of *OSGi* introduces a lot of complexity to projects.\n\nSpring Plugin provides a more pragmatic approach to plugin development by providing the core flexibility of having plugin implementations extending a core system's functionality but of course not delivering core OSGi features like dynamic class loading or runtime installation and deployment of plugins. Although Spring Plugin thus is not nearly as powerful as OSGi, it serves a poor man's requirements to build a modular\nextensible application.\n\n### Context\n\n-   You want to build an extensible architecture minimizing overhead as much as possible\n-   You cannot use OSGi as fully fledged plugin architecture for whatever reasons\n-   You want to express extensibility by providing dedicated plugin interfaces\n-   You want to extend the core system by simply providing an implementation of the plugin interface bundled in a JAR file and available in the classpath\n-   (You use Spring in your application)\n\nThe last point actually is not essential although Spring Plugin gains a\nlot of momentum in collaborative use with Spring.\n\n### Technologies\n\n#### Spring\n\nSpring is the de-facto standard application framework for Java applications. Its consistent programming model, easy configuration and wide support for all kinds of third party libraries makes it the first class citizen of application frameworks. Spring Plugin tightly integrates into Spring's component model and extends the core container\nwith some custom functionality.\n\n## Core\n\n### Introduction\n\nHost system provides a plugin interface providers have to implement.\nCore system is build to hold a container of instances of this interface\nand works with them.\n\n**Example 1.1. Basic example of plugin interface and host**\n\n```java\n/**\n * Interface contract for the providers to be implemented.\n */\npublic interface MyPluginInterface {\n  public void bar();\n}\n\n\n/**\n * A host application class working with instances of the plugin\n * interface.\n */\npublic class HostImpl implements Host {\n\n  private final List\u003cMyPluginInterface\u003e plugins;\n\n  public HostImpl(List\u003cMyPluginInterface\u003e plugins) {\n    Assert.notNull(plugins);\n    this.plugins = plugins;\n  }\n\n  /**\n   * Some business method actually working with the given plugins.\n   */\n  public void someBusinessMethod() {\n    for (MyPluginInterface plugin : plugins) {\n      plugin.bar();\n    }\n  }\n}\n```\n\nThis is the way you would typically construct a host component in general. Leveraging dependency injection via setters allows flexible usage in a variety of environments. Thus you could easily provide a factory class that is able to lookup `MyPluginInterface`\nimplementations from the classpath, instantiate them and inject them into `HostImpl`.\n\nUsing Spring as component container you could configure something like this:\n\n**Example 1.2. Configuring HostImpl with Spring**\n\n```xml\n\u003cbean id=\"host\" class=\"com.acme.HostImpl\"\u003e\n  \u003cproperty name=\"plugins\"\u003e\n    \u003clist\u003e\n      \u003cbean class=\"MyPluginImplementation\" /\u003e\n    \u003c/list\u003e\n  \u003c/property\u003e\n\u003c/bean\u003e\n```\n\nThis is pretty much well known to Spring developers and let's us face the wall that this is rather static. Everytime you want to add a new plugin implementation instance you have to modify configuration of the core. Let's see how we can get this dance a little more.\n\n### Collecting Spring beans dynamically\n\nWith the `BeanListBeanFactory` Spring Plugin provides a Spring container extension, that allows to lookup beans of a given type in the current `ApplicationContext` and register them as list under a given name. Take a look at the configuration now:\n\n**Example 1.3. Host and plugin configuration with Spring Plugin\nsupport**\n\n```xml\n\u003cimport resource=\"classpath*:com/acme/**/plugins.xml\" /\u003e\n\n\u003cbean id=\"host\" class=\"com.acme.HostImpl\"\u003e\n  \u003cproperty name=\"plugins\" ref=\"plugins\" /\u003e\n\u003c/bean\u003e\n\n\u003cbean class=\"org.springframework.plugin.support.BeanListBeanFactory\"\u003e\n  \u003cproperty name=\"lists\"\u003e\n    \u003cmap\u003e\n      \u003centry key=\"plugins\" value=\"org.acme.MyPluginInterface\" /\u003e\n    \u003c/map\u003e\n  \u003c/property\u003e\n\u003c/bean\u003e\n```\n\n```xml\n\u003c!-- In a file called plugins.xml in the plugin project --\u003e\n\u003cbean class=\"MyPluginImplementation\" /\u003e\n```\n\nYou can see that we include a wildcarded configuration file that allows plugin projects to easily contribute plugin implementations by declaring them as beans in configuration files matching the wildcarded path. If you use Spring 2.5 component scanning you don't have to use the import trick at all as Spring would detect the implementation automatically as long as it is annotated with `@Component`, `@Service` a.s.o.\n\nThe `BeanListBeanFactory` in turn allows registering a map of lists to be created, where the maps entry key is the id under which the list will be registered and the entry's value is the type to be looked up.\n\n\u003e #### Note\n\u003e\n\u003e The design of the `BeanListBeanFactory` might seem a little confusing at first\n\u003e (especially to set a map on a property named lists). This is due to the possibility to\n\u003e register more than one list to be looked up. We think about dropping this\n\u003e functionality for the sake of simplicity in future versions.\n\n### A whole lotta XML - namespace to help!\n\nActually this already serves a lot of requirements we listed in [Section “Context”](#context). Nevertheless the amount of XML to be written is quite large. Furthermore it's rather not intuitive to configure a bean id as key, and a type as value. We can heavily shrink the XML required to a single line by providing a Spring namespace boiling configuration down to this:\n\n**Example 1.4. Host configuration using the plugin namespace**\n\n```xml\n\u003cbeans xmlns=\"http://www.springframework.org/schema/beans\"\n  xmlns:plugin=\"http://www.springframework.org/schema/plugin\"\n  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n  xsi:schemaLocation=\"http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\n    http://www.springframework.org/schema/plugin https://www.springframework.org/schema/plugin/spring-plugin.xsd\"\u003e\n\n  \u003cimport resource=\"classpath*:com/acme/**/plugins.xml\" /\u003e\n\n  \u003cbean id=\"host\" class=\"com.acme.HostImpl\"\u003e\n    \u003cproperty name=\"plugins\" ref=\"plugins\" /\u003e\n  \u003c/bean\u003e\n\n  \u003cplugin:list id=\"plugins\" class=\"org.acme.MyPluginInterface\" /\u003e\n\u003c/beans\u003e\n```\n\nAssuming you have added the namespace XSD into Eclipse and installed Spring IDE, you should get code completion on filling the class attribute.\n\n### Using inner beans\n\nThe listing above features an indirection for the `plugin` bean definition. Defining the plugin list as top level bean can have advantages: you easily could place all plugin lists in a dedicated configuration file, presenting all application extension points in one single place. Nevertheless you also might choose to define the list directly in the property declaration:\n\n**Example 1.5. Using inner bean definition**\n\n```xml\n\u003cimport resource=\"classpath*:com/acme/**/plugins.xml\" /\u003e\n\n\u003cbean id=\"host\" class=\"com.acme.HostImpl\"\u003e\n  \u003cproperty name=\"plugins\"\u003e\n    \u003cplugin:list class=\"org.acme.MyPluginInterface\" /\u003e\n  \u003c/property\u003e\n\u003c/bean\u003e\n```\n\nThis way you have a more compact configuration, paying the prica of tangling all extention points though possibly various config files.\n\n#### Plugin beans\n\nUsing plain interfaces and `BeanListBeanFactory` offers an easy way to dynamically lookup beans in Spring environments. Nevertheless, very often you face the situation that you want to have dedicated access to a subset of all plugins, choose plugins by a given criteria or use a decent default plugin or the like. Thus we need a basic infrastructure interface for plugin interfaces to extend and a more sophisticated plugin container.\n\n#### Plugin\n\nHera's central infrastructure interfacte is `Plugin\u003cS\u003e`, where `S` defines the delimiter type you want to let implementations decide on, whether they shall be invoked or not. Thus the plugin implementation have to implement `supports(S delimiter)` to come to the decision. Consider the following example:\n\n**Example 1.6. Usage of Plugin interface**\n\n```java\npublic enum ProductType {\n  SOFTWARE, HARDWARE;\n}\n\npublic interface ProductProcessor extends Plugin\u003cProductType\u003e {\n  public void process(Product product);\n}\n```\n\nThis design would allow plugin providers to implement `supports(ProductType productType)` to decide which product types they want to process and provide actual processing logic in `process(Product product)`.\n\n#### PluginRegistry\n\nUsing a `List` as plugin container as well as the `Plugin` interface you can now select plugins supporting the given delimiter. To not reimplement the lookup logic for common\ncases Spring Plugin provides a `PluginRegistry\u003cT extends Plugin\u003cS\u003e, S\u003e` interface that provides sophisticated methods to access certain plugins:\n\n**Example 1.7. Usage of the PluginRegistry**\n\n```java\nPluginRegistry\u003cProductProcessor, ProductType\u003e registry = SimplePluginRegistry.of(new FooImplementation());\n\n// Returns the first plugin supporting SOFTWARE if available\nOptional\u003cProductProcessor\u003e plugin = registry.getPluginFor(ProductType.SOFTWARE);\n// Returns the first plugin supporting SOFTWARE, or DefaultPlugin if none found\nProductProcessor plugin = registry.getPluginOrDefaultFor(ProductType.SOFTWARE, () -\u003e new DefaultPlugin());\n// Returns all plugins supporting HARDWARE, throwing the given exception if none found\nList\u003cProductProcessor\u003e plugin = registry.getPluginsFor(ProductType.HARDWARE, () -\u003e new MyException(\"Damn!\");\n```\n\n#### Configuration, XML namespace and @EnablePluginRegistries\n\nSimilar to the `BeanListBeanFactory` described in [Collecting Spring beans\ndynamically](#core.beans-dynamically) Spring Plugin provides a `PluginRegistryBeanFactory` to automatically lookup beans of a dedicated type to be aggregated in a `PluginRegistry`. Note that the type has to be assignable to `Plugin` to let the registry work as expected.\n\nFurthermore there is also an element in the namespace to shrink down configuration XML:\n\n**Example 1.8. Using the XML namespace to configure a registry**\n\n```xml\n\u003cplugin:registry id=\"plugins\" class=\"com.acme.MyPluginInterface\" /\u003e\n```\n\nAs of version 0.8 creating a `PluginRegistry` can also be achieved using the `@EnablePluginRegistries` annotation:\n\n```java\n@Configuration\n@EnablePluginRegistries(MyPluginInterface.class)\nclass ApplicationConfiguration { … }\n```\n\nThis configuration snippet will register a `OrderAwarePluginRegistry` for `MyPluginInterface` within the `ApplicationContext` and thus make it available for injection into client beans. The registered bean will be named `myPluginInterfaceRegistry` so that it can be explicitly referenced on the client side using the `@Qualifier` annotation if necessary. The bean name can be customized using `@Qualifier` on the plugin interface definition.\n\n### Ordering plugins\n\nDeclaring plugin beans sometimes it is necessary to preserve a certain order of plugins. Suppose you have a plugin host that already defines one plugin that shall always be executed after all plugins declared by extensions. Actually the Spring container typically returns beans in the order they were declared, so that you could import you wildcarded config files right before declaring the default plugin. Unfortunately the order of the beans is not contracted to be preserved for the Spring container. Thus we need a different solution.\n\nSpring provides two ways to order beans. First, you can implement `Ordered` interface and implement `getOrder` to place a plugin at a certain point in the list. Secondly you can user the `@Order` annotation. For more information on ordering capabilities of Spring see the [section on this topic in the Spring reference documentation](https://docs.spring.io/spring/docs/3.1.x/javadoc-api/org/springframework/core/Ordered.html).\n\nUsing the Spring Plugin namespace you will get a `PluginRegistry` instance that is capable of preserving the order defined by the mentioned means. Using Spring Plugin\nprogrammatically use `OrderAwarePluginRegistry`.\n\n## Metadata\n\nFor plugin architectures it is essential to capture metadata information about plugin instances. A very core set of metadata (name, version) also serves as identifier of a plugin and thus can be used. The Spring Plugin metadata module provides support to capture metadata.\n\n### Core concepts\n\nThe metadata module actually builds around two core interfaces, `PluginMetadata` and `MetadataProvider`:\n\n**Example 2.1. Core concepts**\n\n```java\npublic interface PluginMetadata {\n  String getName();\n  String getVersion();\n}\n\npublic interface MetadataProvider {\n  PluginMetadata getMetadata();\n}\n```\n\nThe `PluginMetadata` interface captures the required properties to define an identifiable plugin. This means, that implementations should ensure uniqueness through these two properties. With `SimplePluginMetadata` Spring Plugin provides a Java bean style class to capture metadata. Of course applications can and should provide extended metadata information according to their needs. The very narrow interface is only targeted at integrating the metadata concept with the `PluginRegistry` (see [the section called “PluginRegistry”](#core.plugin-registry)) without bothering developers with too much information required.\n\nThe `MetadataProvider` interface is to be used in application plugin interfaces to indicate that they can provide metadata. To ease plugin implementation we provide\n`AbstractMetadataBasedPlugin` that uses the internal metadata to implement `supports(…)` method of `Plugin`. Extending this base class plugins with metadata as selection criteria can easily be build. This way you could store the metadata in user specific configuration files and use this to select a distinct plugin specific to a given user.\n\n## Glossary\n\n\n### O\n\nOSGi\n\n  * Open Services Gateway Initiative - a fully fledged plugin runtime environment on top of the Java VM - [https://en.wikipedia.org/wiki/OSGi](https://en.wikipedia.org/wiki/OSGi).\n\n### X\n\nXML\n\n  * eXtensible Markup Language\n\nXSD\n\n  * Xml Schema Definition\n","funding_links":[],"categories":["开发框架"],"sub_categories":["Spring Cloud框架"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fspring-projects%2Fspring-plugin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fspring-projects%2Fspring-plugin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fspring-projects%2Fspring-plugin/lists"}