Spring Maven project structure pom.xml - java

I'd like to get help in setting up a multi-module Maven project using Spring Boot.
Correct me if I'm wrong but I've read that Spring-Boot reads the start main Application (Annotated with #SpringBootApplication and ran with SpringApplication.run) and finds the necessary classes through reflection. Which means that it first accesses the start class and then proceeds to find the controllers, models, repositories. If so, how do I set up the dependency in the pom.xml of each module if I had a project structure like this:
app
--src
--pom.xml
core
--pom.xml
--models
----/pom.xml
--controllers
----/pom.xml
--repositories
----/pom.xml
pom.xml

Please have a look complete guide how to create multi module project in spring boot.
https://spring.io/guides/gs/multi-module/

Spring boot will component scan from the package of the class annotated with #SpringBootApplication. Component scannign means that it is looking through the classes under that package recursively, analyzing annotations, and wiring up anything it recognizes. This can include controllers, simple variables with #Value annotations, members with #Autowired, and a host of other things.
You can actually jump into the source for the #SpringBootApplication annotation and see that it expands to numerous other annotations, #ComponentScan being one of them.
If all of your modules are in a sub-hierarchy package wise from there, then they will be scanned properly anyway. Often though, sub-modules will be in a slightly different package hierarchy. In this case, you can explicitly specify #ComponentScan() in your code and inside the () you can list the base packages to component scan from.
Whether or not its a sub-module doesn't matter much at this point; its just like scanning classes in any other library you're including.
General Advice
Also, just FYI - Multi module projects can get a little hard to manage (speaking from numerous separate experiences). They can be very good if used properly though. If you're a beginner to Maven though, it may be wiser to keep separte, well-defined projects with a proper release cycle and just import them as normal dependencies.
So, I'm not for or against them, but just make sure you understand them well going in :).

I have a GitHub project where I configured a multimodule maven project:
https://github.com/cristianprofile/spring-boot-mvc-complete-example
This is Example project maven module structure:
Spring mvc rest maven module ---> service maven module ---> repository maven module
The main module should be configured like this (Spring mvc rest layer):
#SpringBootConfiguration
#EnableAutoConfiguration
//spring mvc module auto scan only its package
#ComponentScan(basePackageClasses = HelloWorldController.class)
//It needs Service bean so it will import ConfigurationService.class from
// Service maven module
#Import({ConfigurationService.class})
Complete class:
https://github.com/cristianprofile/spring-boot-mvc-complete-example/blob/develop/spring-boot-mvc-rest/src/main/java/com/mylab/cromero/controller/Application.java
It will only scan its package :
HelloWorldController.class --> com.mylab.cromero.controller;
This Rest layer use a service maven module so it is necessary to add dependency:
<dependency>
<groupId>com.mylab.cromero.core</groupId>
<artifactId>mylab-core-service-impl</artifactId>
<version>0.0.2-SNAPSHOT</version>
</dependency>
Complete pom file:
https://github.com/cristianprofile/spring-boot-mvc-complete-example/blob/develop/spring-boot-mvc-rest/pom.xml#L16
ConfigurationService.class from service maven module autoscan its packages and it will import ConfigurationRepository.class (Repository maven module)
#Configuration
//It needs repository's bean so it will import ConfigurationRepository.class from
// Repository maven module
#Import(ConfigurationRepository.class)
//service layer module auto scan only its package
#ComponentScan(basePackageClasses = ConfigurationService.class)
public class ConfigurationService {
}
Complete Service maven module code:
https://github.com/cristianprofile/spring-boot-mvc-complete-example/blob/develop/mylab-core/mylab-core-service-impl/src/main/java/com/mylab/cromero/service/ConfigurationService.java#L12
Service maven module layer has a dependency with maven repository module:
https://github.com/cristianprofile/spring-boot-mvc-complete-example/blob/develop/mylab-core/mylab-core-service-impl/pom.xml#L38
Repository module will auto configure jpa and domain classed:
#Configuration
#EnableJpaRepositories(basePackages = "com.mylab.cromero.repository")
#EntityScan(basePackageClasses=Base.class)
#ComponentScan(basePackageClasses = BaseRepository.class)
public class ConfigurationRepository {
}

Related

Spring-Boot Components from other jar are not in context

I have following situation. JDK 17, Spring-Boot: 2.6.2. A gradle multi-project. One project is a library (java-library, no spring boot plugin). Another project is a spring boot application with spring boot plugin. Generally, the spring dependency management plugin is not used, gradle platform concept is used instead. Application project includes dependency to library project per "implementation(project(':.."
Library:
Library project and spring boot application project have different packages.
like:
library root package is a.b.c and application root package is a.b.d
Library project has in root package of the package hierarchy a configuration class (for example a.b.c.LibraryConfig) which is annotated with #Configuration annotation and with #ComponentScan annotation, which has attribute "basePackageClass" pointing to this configuration class:
#Configuration
#ComponentScan(basePackageClass = LibraryConfig.class)
public class LibraryConfig {
Inside of library hierarchy (so in packages a.b.c.*) are services and rest controllers.
Application:
Application has in root package of the package hierarchy (for example in a.b.d) application class and in sub-package "config" it has configuration class (would be for example a.b.d.config.AppConfig), which imports the configuration class from library:
#Configuration
#Import(a.b.c.LibraryConfig.class)
public class AppConfig {
Problem:
Classes from the library are there in runtime - we are able to load them.
No any component of the library is in context (not registered), neither services nor rest controllers.
What are we missing?
We have tried different constellations of imports and scans. Also added component scan to application class and set there particular packages. Nothing helped.
The problem was, that the main application was using spring boot indexing annotation processor and library - not. So after enabling the annotation processor for library problem was solved.

Reuse a Spring-boot submodule in another non Spring project

I'm working on a multi module spring-boot project to build a REST API. Here is my project structure:
Parent project (packaging is pom)
core module (#SpringBootApplication + handle path like / or /status)
restControllerA module (Handle path like /routeA/*)
restControllerB module (Handle path like /routeB/*)
Everything is working in this project :)
In another non Spring project I would like to reuse a service of restControllerB. This service return the result of the request body validation.
First I try to add the restControllerB.jar as a dependency to this new project... But this jar does not contain its depedencies (who are in the fatJAR "core.jar"). When I run the project, I get a lot of ClassNotFoundException.
How can I manage to reuse this service as a dependency ? I thought to create a validator module which implements the validatorService interface, but I'm not sure if it is the best solution.
After few hours googling, It seems that creating an external librairy is the right choice. I create an external module and add it as a dependecy to restControllerB.

Problems with Testing Spring Boot Application with Multi Maven Project Setup

I am currently running into some problems with spring boot and multi maven project structure. I am using Spring Boot 4.3.1.
My project structure looks as follows:
parent
-- pom.xml
-- application
-- pom.xml
-- src
-- main
-- java
-- Application.java (annotated with #SpringBootApplication)
-- test
-- java
-- MyApplicationTest.java (annotated with #SpringBootTest)
-- library
-- pom.xml
-- src
-- main
-- java (...)
-- test
-- java
-- MyLibraryTest.java (annotated with #SpringBootTest)
application module has a dependency on library.
MyApplicationTest works perfectly fine, but running MyLibraryTest instead, I fail with the following error:
java.lang.IllegalStateException: Unable to find a #SpringBootConfiguration, you need to use #ContextConfiguration or #SpringBootTest(classes=...) with your test at org.springframework.util.Assert.state(Assert.java:392)
at org.springframework.boot.test.context.SpringBootTestContextBootstrapper.getOr FindConfigurationClasses(SpringBootTestContextBootstrapper.java:173)
at org.springframework.boot.test.context.SpringBootTestContextBootstrapper.processMergedContextConfiguration(SpringBootTestContextBootstrapper.java:133)
at org.springframework.test.context.support.AbstractTestContextBootstrapper.buildMergedContextConfiguration(AbstractTestContextBootstrapper.java:409)
at org.springframework.test.context.support.AbstractTestContextBootstrapper.buildMergedContextConfiguration(AbstractTestContextBootstrapper.java:305)
at org.springframework.test.context.support.AbstractTestContextBootstrapper.buildTestContext(AbstractTestContextBootstrapper.java:112)
at org.springframework.boot.test.context.SpringBootTestContextBootstrapper.buildTestContext(SpringBootTestContextBootstrapper.java:78)
at org.springframework.test.context.TestContextManager.<init>(TestContextManager.java:120)
at org.springframework.test.context.TestContextManager.<init>(TestContextManager.java:105)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTestContextManager(SpringJUnit4ClassRunner.java:152)
My first guess is that library needs a dependency on application, but this causes a cycle.
Is there any solution to that problem?
How can I structure my application correctly?
thanks a lot for suggestions.
MyLibraryTest looks as follow:
#RunWith(SpringRunner.class)
#SpringBootTest
#Transactional
public class MyLibraryTest {
#Autowired
private MyService service;
#Test
public void testMyService_Save() {...}
}
You need to make sure that the library module's pom.xml includes -
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
<version>1.5.2.RELEASE</version>
</dependency>
which is required for the module to use the #SpringBootTest annotation. You might already be using the same in your app module but is not included in the library module.
Well post the Edit, found the question to be possibly a duplicate of Unable to find a #SpringBootConfiguration when doing a JpaTest
Also quoting from Thomas's answer from the same thread, here
The thing about #DataJpaTest and a few other annotations is that they
look for a #SpringBootConfiguration annotation in the current package,
and if they cannot find it there, they traverse the package hierarchy
until they find it.
For example, if the fully qualified name for your test class was
com.example.test.JpaTest and the one for your application was
com.example.Application, then your test class would be able to find
the #SpringBootApplication (and therein, the
#SpringBootConfiguration).
If the application resided in a different branch of the package
hierarchy, however, like com.example.application.Application, it would
not find it.
which seems to be the case for you, where you are trying to test an application in a different module itself. Hence the error that you see.

Should the service beans defined in the jar library be instantiated by the jar library configuration or by the client war application configuration?

I have a library as a jar packaging Maven project which offers services.
The #Configuration class to instantiate service beans:
#Configuration
public class JpaService {
#Bean
public UserRoleServiceImpl userRoleService() {
return new UserRoleServiceImpl();
}
#Bean
public UserServiceImpl userService() {
return new UserServiceImpl();
}
}
I reckoned I needed to have the beans instantiation outside of the jar archive.
So I had a copy of this class in the test part of the project, and another copy in another war packaging Maven project using the library.
But what if I instantiated the services in the jar library itself. I would need to only do it once, be it for testing or for all client projects using it.
UPDATE: Two questions...
Should all component scanning only be done from the war ? Or should the jar service components be scanned from the jar ?
And what if two components (one in the jar and one in the war) have the same class name in the same package ?
I dont think i fully understand your question, but if you are aiming to add beans to your application context that is outside the jar then what you have to do is use the #ComponentScan annotation, and specify the package you want to scan, the package can be in a different jar, the only thing required is that you anotate the clases you want to include with #Service, #Componenet or even #Configuration
example:
#Configuration
#ComponentScan(basePackages={"com.somepackacge.controller",
...
you can include as much packages as you like.
By the way dont copy your clases from one place to the other, maintining that will be a headache in the futute, if you want to include your configuration in your tests you can always do :
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = MyConfigClass.class)
Where MyConfigClass is the class u used before with the component scan
Hope it helps

How to architect my project by maven

Thanks all of you to read my question!
Months ago , I had build nexus to manage my maven repository and I build an empty web project.
My goal is to create an architecture for my company. When a new project comes, I can only configure my pom.xml to add my frame dependency. Well, my project's frame is springMVC+velocity+mybatis
I separated my Java source code into four models, groupId, artifectId has been named like below:
groupId is __com.myproject.framework__
parent pom's artifactId is com.myproject.framework-root.
sub models's artifactId are framework-core, framework-utils and so on.
This architecture can be used when I create an empty Java web project. I only configured the pom.xml with :
<dependency>
<groupId>com.myproject.framework</groupId>
<artifactId>framework-core</artifactId>
<version>1.0</version>
</dependency>
It's very good, I can reference this jar package very well!
But framework-core's Java code uses the Spring framework. Source code of myproject-framework-core jar package, is as below:
Action:
package com.hc360.buyer.action
#Controller
public class ActiveRecordAction extends BaseAction {
// ...
}
Service:
package com.hc360.buyer.service
#Service
public class BuyerInfoMainServiceImpl{
...
}
This means that I must configure path into my spring config of the new empty project, such as:
<context:component-scan base-package="com.hc360.buyer.service" />
<context:component-scan base-package="com.hc360.buyer.action" />
My question is how can I design my framework models or jar package?
Goal is that when I create a new Java web project and reference my myproject-framework-core jar package, I needn't have to configure my spring config like below:
<context:component-scan base-package="com.hc360.buyer.action" />
In my mind, the best way is I only need to reference my myproject-framework-core jar package in this new project's pom.xml, and then it works well.

Categories