Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

maven - separate modules for interfaces and implementation with Spring

Tags:

java

spring

maven

We are working on Mavenizing our java project and we would like to setup a clean separation between interfaces and implementations for each module. In order to do so, we want to split each module into two sub-modules one for interfaces and data objects used by them and another for implementations. For example:

 +commons 
  +commons-api 
  +commons-impl 

The POMs of the modules will be configured such that no module depends on the impl sub-modules. This way no code from one module will be able to "see" implementation details of another module.

What we are having trouble with, is where to put our spring XMLs. In our project we automatically import spring XML files using wildcard import like

<import resource="classpath*:**/*-beans.xml"/>

This way the location of Spring XMLs doesn't really matter at runtime, as all the modules get loaded into the same class loader and, the strict one way dependency rules in the POMs don't apply.

However, during development we want the IDE - we use Intellij IDEA - to recognize implementation classes referenced from the spring XMLs. We also want IDEA to recognize beans defined in other modules.

If we put the spring XMLs in API sub-modules - they won't "see" the implementation classes in the impl sub-modules. If we put them in the impl sub-modules, their beans won't be "seen" from other modules. It is probably possible to configure the IDEA project to recognize spring XMLs from modules on which there is no dependency, but we prefer for our POMs to hold all the project structure information and not rely on IDEA project files.

We considered creating a third sub-module just to hold Spring XMLs (and perhaps hibernate xmls as well). For example:

 +commons 
  +commons-api 
  +commons-impl 
  +commons-config

The external modules will depend on both commons-api and commons-config and commons-config will depend on both commons-api and commons-impl, with the dependency on commons-impl marked as "provided" (to prevent transitive resolution).

This however seems like a complex and awkward solution and we feel that there must be a better - simpler way to achieve interface/impl separation with Maven and Spring.

like image 796
dkarlinsky Avatar asked Feb 13 '13 14:02

dkarlinsky


People also ask

How do I run a multi-module project in Maven?

A multi-module project is built from an aggregator POM that manages a group of submodules. In most cases, the aggregator is located in the project's root directory and must have packaging of type pom. The submodules are regular Maven projects, and they can be built separately or through the aggregator POM.

How do I create a multi-module Maven project in Spring Initializr?

To skip the basics, do the following: Download and unzip the source repository for this guide, or clone it using Git: git clone https://github.com/spring-guides/gs-multi-module.git. cd into gs-multi-module/initial. Jump ahead to Create the Library Project.


5 Answers

What you need is a runtime dependency scope:

runtime - This scope indicates that the dependency is not required for compilation, but is for execution. It is in the runtime and test classpaths, but not the compile classpath.

(https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html)

Define a runtime dependency from one impl module to another impl module where you use the impl classes in the *-beans.xml config. Intellij will correctly recognize this in spring configuration files, but won't auto complete them in code (but it will do that in test code).

Also if anyone used the classes in the code, compilation through maven would fail, because the runtime dependency is not on a compile class path.

like image 70
František Hartman Avatar answered Oct 19 '22 12:10

František Hartman


You can achieve decoupling of api and impl like this:

+ commons (pom)
  + pom.xml         <--- serves as a parent aggregator (see below)
  + commons-api (jar)  <--- contains models, interfaces and abstract classes only
  + commons-impl (jar)  <--- depends on commons-api
  + commons-config (jar) <--- depends on commons-impl only (no need to depend on commons-api as it is brought in transitively)

 + external-project (war or jar) <--- has commons-config as a dependency

Parent aggregator pom (specify build order):

<modules>
  <module>commons-api</module>
  <module>commons-impl</module>
  <module>commons-config</module>
</modules>

The config module can be omitted if it only contains spring application context configuration. The app configuration xml should be in the classpath and folder structure of the module that contains the artifact that you are deploying. So if you are building a war artifact, the app context should be in there.

The only configuration that should be in your commons module would be in a test package of your impl module.

like image 24
cosbor11 Avatar answered Oct 19 '22 11:10

cosbor11


In short you want Idea to override maven dependency graph but avoid keeping this configuration in idea project files?

One option is to group implementation dependencies in a maven profile. This profile would not be enabled by default but you should be able to mark it as active under idea.

like image 26
mrembisz Avatar answered Oct 19 '22 13:10

mrembisz


Two ideas come to mind:

  1. You will have one (or more) modules where all the modules (api+impl) are dependencies, you could place your spring configuration files there.
  2. Place the spring configuration files in the api modules and declare a dependency on the impl module with scope provided this way the implementations will be known, while there is no dependency of the api for the deployment.
like image 1
hotzst Avatar answered Oct 19 '22 12:10

hotzst


  • commons-impl at runtime scope in external modules

    1. commons (pom dependencyManagement) =>

      +commons-api (compile)

      +commons-impl (compile)

      +commons-config (compile)

    2. commons-impl (pom dependencies) =>

      +commons-api (compile)

      +commons-config (compile)

    3. external modules (pom dependencies) =>

      +commons-impl (runtime)

      +commons-api (compile)

      +commons-config (compile)

like image 1
question_maven_com Avatar answered Oct 19 '22 11:10

question_maven_com