GithubHelp home page GithubHelp logo

tomdw / java-modules-context-boot Goto Github PK

View Code? Open in Web Editor NEW
10.0 3.0 1.0 13.34 MB

Simple framework to boot a spring context within java platform modules

License: Apache License 2.0

Java 100.00%
spring java9 jpms modularity modules java jdk11 springboot spring-boot jigsaw

java-modules-context-boot's Introduction

java-modules-context-boot

Simple framework to boot a spring context within each java module

  1. Requirements
  2. Maven Dependency
  3. Define a spring context within a module
  4. Starting the application
  5. Integrating modules using services
  6. Samples

The Boot Spring Context within each Java 11 Module blog post describes the approach in detail.

Requirements

Maven Dependency

The dependency to use is

<dependency>
	<groupId>io.github.tomdw.java.modules.spring</groupId>
	<artifactId>java-modules-context-boot</artifactId>
	<version>0.0.6</version>
</dependency>

which provides you with a java module named

io.github.tomdw.java.modules.context.boot

transitively providing read access to spring modules and jakarta's java.inject (still in javax.inject package).

Define a spring context within a module

To define a spring context within a java module, you need to:

  • use the ModuleContext annotation on the module pointing to a java-based spring configuration
  • require io.github.tomdw.java.modules.context.boot
  • open the package of the module to spring

For example:

@ModuleContext(
	mainConfigurationClass = SpeakerConfiguration.class
)
module io.github.tomdw.java.modules.spring.samples.basicapplication.speaker {
	requires io.github.tomdw.java.modules.context.boot;
	opens io.github.tomdw.java.modules.spring.samples.basicapplication.speaker.internal to spring.beans, spring.core, spring.context;
}

java-modules-context-boot will make sure to start a separate spring context for every module annotated with this annotation.

If you would like to use another ApplicationContext than the default AnnotationConfigApplicationContext you can specify the class using the applicationContextClass parameter of the @ModuleContext annotation. This class should extend GenericApplicationContext and implement the AnnotationConfigRegistry interface.

This could be useful when you want to use a Spring Boot specific ApplicationContext that contains starters. For example an application context supporting web servlets such as org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext.

Starting the application

Using the built-in main class

You can use the ModuleContextBooter.main as main method to start the application.

Make sure to add the application modules to your module path. For every module on the module path annotated with @ModuleContext we boot a spring context.

Using your own (main) classes

When you want to control when the modules get booted with spring contexts you can call

ModuleContextBooter.boot();

from anywhere in your project's code.

Using the Spring Boot Starter

The dependency to use is

<dependency>
	<groupId>io.github.tomdw.java.modules.spring</groupId>
	<artifactId>java-modules-context-boot-starter</artifactId>
	<version>0.0.6</version>
</dependency>

which provides you with a java module named

be.aca.platform.java.modules.context.boot.starter

and which is a standard Spring Boot starter that automatically triggers ModuleContextBooter.boot(springBootApplicationContext). The framework uses the given springBootApplicationContext as default application context when retrieving beans for a Module.

This alternative enables applications that are standard spring boot applications running on the modulepath to easily integrate other modules which define their own ModuleContext for better isolation.

Integrating modules using services

Using services through the ServiceLoader API allows to integrate modules in a loosely coupled way. The following sections describe how to do this between multiple spring contexts in different modules.

Provide a module service from the spring context

To provide a service through the ServiceLoader API you need to add the 'provides' in your module-info:

@ModuleContext(...)
module io.github.tomdw.java.modules.spring.samples.basicapplication.speaker {
	...
	provides SpeakerService with DefaultSpeakerService;
	...
}

Instead of using a default contructor, leverage the 'provider' factory method alternative as follows:

@Named
public class DefaultSpeakerService implements SpeakerService {
	...
	public static SpeakerService provider() {
		return ModuleServiceProvider.provide(SpeakerService.class);
	}
	...
}

Best practice is to provide the service with the interface class. This way a dynamic proxy is created for consumers. It prevents exceptions while providing instances of beans while certain springContexts are still inactive. The actual bean lookup will be performed when this service is invoked.

If you have 2 services of the same type (for instance a datasource), you can provide the proper service using the bean name as follows:

public class SpeakerServiceProvider {

	public static SpeakerService provider() {
		return ModuleServiceProvider.provide(SpeakerService.class, "speaker-service-bean-name");
	}

	private SpeakerServiceProvider() {
		//must not be constructed
	}
}

The ModuleServiceProvider.provide helper method retrieves a bean of the given type from the spring application context associated with the module calling the provide method.

Reference a module service from another spring context

Using a service in another module can simply be done by annotating with @ModuleServiceReference:

@Named
public class MessageGenerator {

	@ModuleServiceReference
	private SpeakerService speakerService;

}

Don't forget to add a 'uses' entry in the module-info:

@ModuleContext(...)
module io.github.tomdw.java.modules.spring.samples.basicapplication.application {
	...
	uses SpeakerService;
}

Every spring context is enriched with a processor to retrieve the necessary services through the ServiceLoader API and make them available for injection in the spring context of that module.

Reference a list of module services from another spring context

Similar to using a single service, the annotation @ModuleServiceReference can be used to inject a list of all services that implement a certain interface.

@Named
public class MessageGenerator {

	@ModuleServiceReference
	@Named("speakerServiceList")
	private List<SpeakerService> speakerService;

}

The difference is that we cannot inject by type, because of generics limitations a list of speakerServices are not known by spring to inject. To support this, we are registring the list of provided services with a bean name. By convention this name will be the type of the services suffixed by "List". When injecting the service list you should use the @Named or @Qualifier annotation to specify the name following this naming convention.

Sharing Spring Environment

When a default application context is provided, it's Environment will be shared with all other contexts. This allows all contexts to share configuration, including configuration set in tests.

Using in combination with @DirtiesContext

When using this framework in combination wtih DirtiesContext, the registry should be reset whenever the spring context is refreshed. Call ModuleContextBooter.reset() whenever the context is refreshed (e.g. in @AfterAll when DirtiesContext runs after each class).

Samples

  • Under 'samples' the 'basicapplication' sample shows this in a working hello world application.
    • follow the instructions regarding the java toolchains from the java-modules-parent project
    • start the application using 'mvn toolchains:toolchain exec:exec' from within the 'basicapplication/application' module.

java-modules-context-boot's People

Contributors

dependabot[bot] avatar tomdw avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

Forkers

jabrena

java-modules-context-boot's Issues

Remove workaround after JDK bug fix is available in JDK

Problem when using xxxAnnotation methods on Module: java.lang.NoClassDefFoundError: module-info is not a class because access_flag ACC_MODULE is set

Waiting for fix in https://bugs.openjdk.java.net/browse/JDK-8241770 to be released in jdk.

Currently a workaround is implemented by forking some code from the jdk and applying the patch given in the JDK-8241770 issue.

Also logged: https://issues.apache.org/jira/browse/SUREFIRE-1765 as it is maven surefire that puts explicit modules with a module-info.class on the classpath and thus triggers this problem.

After JDK bug has been fixed removing this class and replace its usage with module.isAnnotationPresent and module.getAnnotation should be possible.

javax.inject should at least be a named automatic module

For safe distribution of the library the javax.inject or jakarta.inject dependency should at least be an automatic module with a reserved name in the manifest.

Reached out to the community through http://mail.openjdk.java.net/pipermail/jigsaw-dev/2017-October/013233.html

Contact with the original spec owners will be taken by someone at oracle, see mail thread.

Code at https://github.com/javax-inject/javax-inject, issue logged at javax-inject/javax-inject#33

For Jakarta inject, code at https://github.com/eclipse-ee4j/injection-api/, issue logged at jakartaee/inject#14

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.