DEV Community

loading...

Modular Spring Boot Development

frameworkz profile image frameworkz ・7 min read

On a basic level, Spring Boot provides a fast way to build applications by looking at your classpath and at the beans you have configured, makes reasonable assumptions about what you are missing, and adds those items. With Spring Boot, you can focus more on business features and less on infrastructure.
Alt Text
I’ll cover some of the main concepts of Spring before we dig into the Modular aspects a little below.

Dependency Injection (DI) (Inversion of Control) – To give the application more flexibility, control is given to an external controller through events instead of the application taking control sequentially.

Beans & ApplicationContext – In Spring these are the two kinds of objects containers. Beans are managed and configured by a BeanFactory. This BeanFactory is a root interface for accessing the Spring container that configures and manages the beans. Implementations use lazy loading, which means that Beans are only instantiating when directly called.

ApplicationContext is the central interface within a Spring application for providing configuration information to the application. It's actually a superset of BeanFactory used for more complex applications that need event propagation, declarative mechanisms, and integration with aspect-oriented features of Spring.

Modularizing a Spring Boot Application

Every software project comes to a point where the code should be broken up into modules. This is because modular systems architecture allows for shorter development times, along with improved code management, and the deployment of different solutions for different requirements. These modules may reside within a single code base or could be modules that each reside within their own particular code base.

Alt Text

Modules in Spring Boot

In relation to Spring Boot, a module is a set of Spring components loaded into the application context.

A module can be a business module that provides some business services for the application, or it can be a technical module that provides cross-cutting concerns for several other modules or the entire application. Additionally, these modules could be part of a monolithic codebase or split up into multiple build modules using Maven or Gradle.

Creating Modules - Some Options

The base for a Spring Module is a @Configuration-annotated class along the lines of Spring’s Java configuration feature. There are several ways to define what beans should be loaded by such a configuration class.

@ComponentScan
The simplest method to create a module is by using the @ComponentScan statement on a configuration class:

@ComponentScan
public class BlogPostsFilter {
 {
Enter fullscreen mode Exit fullscreen mode

As the configuration class is picked up by an importing mechanism, it will search in all classes in the package and load an instance of each class that is annotated with one of Spring’s annotations into the application context.

@Bean Definitions
Beans are objects that form the backbone of your application, and they are defined, assembled, and managed by the Spring IoC container. These beans are created by the configuration metadata supplied to the container.

public JavaConfigBean javaConfigBean(){
       return new JavaConfigBean();
   }
Enter fullscreen mode Exit fullscreen mode

This above configuration class is imported creating a TransferService instance that will be inserted into the application context.

The method of creating a module demonstrates what beans are actually loaded by having a single location to look at. The flipside of using @ComponentScan, which can be scattered through annotations of all classes in the package.

@ConditionalOn... Annotations
To achieve more control over which components should be loaded into the application context, then @ConditionalOn... annotations is the answer. This is how Spring loads the exact beans required.

@ConditionalOnClass(SpringTemplateRank.class)
Enter fullscreen mode Exit fullscreen mode

Importing Modules - Some Options

The next step in using modular Spring Boot development is once the module is created then we will need to import it into the application. There are several methods of doing this:

@Import...Annotations
will import the class and all beans that come with it. Any new bean added to the package will be automatically found in our context. And we still have explicit control over the configurations we are using.

@Import(JavaConfig.class)
Enter fullscreen mode Exit fullscreen mode

@Enable... Annotations
Spring Boot already comes with a set of annotations that help developers to configure applications. The @Enable annotation allows for features to be enabled and configured. Some examples:

  • @EnableScheduling
  • @EnableAsync
  • @EnableWebSocket
  • @EnableJpaRepositories
  • @EnableTransactionManagement

Auto-Configuration
Spring also comes with an auto-configuration feature. Although super useful there are issues that may arise too. For example, auto-configuration is especially useful when building a cross-cutting solution that will be used across several Spring Boot applications. However, the limitations because all beans registered due to auto-configuration will be available to all contexts, which might not be what we want.

To enable a module for auto configuration, put the file META-INF/spring.factories into the classpath:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.xyz.libx.autoconfigure.LibXAutoConfiguration

Configuring a Module

@ConfigurationProperties works best with hierarchical properties giving Spring Boot quality support for binding external configuration parameters to a Spring bean. The Spring framework uses standard Java bean setters, so we must declare setters for each of the properties.

Modular development at run time through plugins

For deployment flexibility, run time modularization provides additional benefits over strictly modular development.

With plugins, development and deployment can be separated so new plugins can be developed and deployed without changing the main application.

Thus different teams can work on different aspects of a system and expand it later without changing the Spring Boot application, it provides significant time-saving in quality assurance (QA) and deployment. Different customers can use a different set of plugins matching their special requirements.
This is possible with on-premise deployment as well as on the cloud.

The main difference between modular Spring development and Plugins based development is related to the packaging of the application.

With modular Spring development, the entire application is packaged together, libraries that use the same third-party components may cause “dependency hell” that must be resolved at the packaging time, ultimately only a specific version of the third-party library may be selected. All logical components are known at this point in time, this means that implementations must be also provided at this point.

With plugins-based development the host application and each of the plugins is separately packaged, a properly developed plugin system allows overcoming dependency hell as each plugin is separately loaded.

Logical components are unknown at this point, this allows deciding on the relevant implementation at the deployment time. Additional features, perhaps from a completely different application domain can be added through plugins.

A properly designed and implemented plugins-system should allow any part of a system to be implemented through plugins such as API interfaces, business logic, and persistence.

Alt Text

Injectable plugins using FlexiCore Boot and Spring Boot

FlexiCore Boot is an open-source library available on GitHub and documented at support.wizzdi.com and Wizzdi. FlexiCore artifacts are available on Maven Central.

Support for inter-injectable plugins can be added to any existing Spring Boot application or used on a newly created application. FlexiCore encourages and supports building solutions as sets of interconnected plug-ins, each developed using Spring Boot APIs, services, and paradigms.

A plug-in may depend on other plug-ins, these plugins are injected (@AutoWire) into dependent plugins. All the components of a system may be defined as plug-ins.

In FlexiCore Plug-ins have no limitations to what they define or provide, including the interfaces to front-end devices (APIs), the domain model (Database structure), business services, and Spring event bus.

Unlike Spring modules, the main application has no ‘knowledge’ of what plugins will be available to it at run-time. Plugins are deployed by placing them in a predefined location on the server.

Thus, the above mentioned requirement for supporting different deployments for different customers is addressed at deploy time and not at build time. Simply dispatch a different set of plugins to create a different ‘flavor’ of a system.

Moreover, different versions of the same plugin can be concurrently deployed. The exact version may be selected by the caller, for instance, the client mobile or browser application accessing the API a plugin exposes.

Examples

Adding plugins support
@EnableFlexiCorePlugins
This should be added near the @SpringBootApplication annotation. Once added, the application will load and use plugins stored in a configurable folder on the server.

Adding REST API support (in plugins)
@EnableFlexiCoreRESTPlugins
Once added (on the same location), plugins can now create REST API endpoints. Clients (mobile, browsers, and other servers).

Adding support for JPA in plugins
@EnableFlexiCoreJPAPlugins
Although not strictly considered as plugins, FlexiCore allows model definition jars built to conform to JPA standard to be placed in a predefined entities folder. The domain model defined in these ‘plugins’ is available to plugins and also to other entities definition plugins that may be dependent on them.

Adding support for health messages in plugins
@EnableFlexiCoreHealthPlugins
Allows plugins use the standard annotations Spring provides for actuators and health.

Summary

Spring’s great value is multifaceted - firstly as an integration framework. Then from having stable and familiar libraries to implement all layers of applications. And with all the benefits as a dependency injection container and MVC framework.

Modular architectures encourage cohesive program units, which should be easier for developers to read and understand. This Modularisation of a codebase enables parallelisation in its build process, making it easy for modules to develop into stand-alone services if their boundaries are clear.

On large projects i think modularisation could be essential to offering structure to the code base while also reducing crossover and duplication of developers resources.

With FlexiCore plugins, Spring Modularity can be implemented at run time with requiring new builds of the complete monolithic application.

Discussion (0)

pic
Editor guide