How to create a multi module spring mvc application - java

I have 2 maven packages, both of them with spring boot dependencies.
CoreApplication and CustomerApplication. Both apps have Spring MVC controllers, views and static resources.
In CoreApplication I dont have any runner class annotated with #SpringBootApplication.
In CustomerApplication pom.xml I use CoreApplication as a dependency.
If I run the CustomerApplication #SpringBootApplication annotated runner class, it finds the controllers in CoreApplication, but not the views. It can serve requests like http://localhost:8080/core/index, but I get an error from thymeleaf. (org.thymeleaf.exceptions.TemplateInputException: Error resolving template "index")
Is it possible what I want to do? How can I have a Core module with all common app specific stuff and a Customer app for every customer with their own business logic?

Maybe you can try:
Annotate your CoreApplication module with #SpringBootApplication to let Spring manage and initialize your app as usual:
#SpringBootApplication
public class CoreApplication {
public static void main(String[] args) {
SpringApplication.run(CoreApplication.class, args);
}
}
And in your CustomerApplication's runner, you can put:
#SpringBootApplication
public class CustomerApplication {
public static void main(String[] args) {
new SpringApplicationBuilder()
.sources(CoreApplication.class, CustomerApplication.class)
.run(args);
}
}
This way Spring will initialize your two modules properly.

Related

What is a default Spring Boot Application Context? [duplicate]

This question already has answers here:
Which ApplicationContext implementation used in simple spring boot application?
(3 answers)
Closed 2 years ago.
In Spring Framework we can choose the type of application context from the image below:
But which one is implemented by default by spring boot?
Does it depend on which starter dependencies we choose when create project?
It depends on the starter projects you use. For regular projects Spring Boot uses the AnnotationConfigApplicationContext and for web projects
the AnnotationConfigServletWebServerApplicationContext.
See also the output of
#SpringBootApplication
public class DummyApplication implements ApplicationContextAware {
public static void main(String[] args) {
SpringApplication.run(DummyApplication.class, args);
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) {
System.out.println(applicationContext.getClass().getName());
}
}
Technically it does not directly depends on the starter , but depends on the value of WebApplicationType you configure to run the application :
public static void main(String[] args) throws Exception {
SpringApplication app = new SpringApplication(FooApplication.class);
app.setWebApplicationType(WebApplicationType.SERVLET);
app.run(args);
}
If you do not configure it , the default value will be deduced by checking if certain classes exist in the classpath.
There are 3 types of WebApplicationType will are REACTIVE , SERVLET and NONE.
And based on its value , it will choose which type of application context to be created for. See this for the logic.
For REACTIVE , it will create AnnotationConfigReactiveWebServerApplicationContext
For SERVLET , it will create AnnotationConfigServletWebServerApplicationContext
For NONE, it will create AnnotationConfigApplicationContext
So it is possible that even you use certain starter , but changing the WebApplicationType value will cause different context type to be used.
We can see the output using the following code as well
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
#SpringBootApplication
public class Application implements CommandLineRunner {
#Autowired
private ApplicationContext applicationContext;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
public void run(String... args) throws Exception {
System.out.println(applicationContext.getDisplayName());
}
}
I used the spring-boot-starter-web dependency , so it printed org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext
On including spring-boot-starter it prints org.springframework.context.annotation.AnnotationConfigApplicationContext
So depending one the spring-boot starter dependency , spring is choosing the implementation of ApplicationContext

Is it possible to exclude nested configuration from autoconfiguration in Spring Boot application?

Suppose we have a Spring Boot application and autoconfiguration with several configurations defined inside it
#Configuration
#AutoConfigureBefore(MainAutoConfiguration.class)
public class TestAutoConfiguration {
....
#Configuration
public static class FirstNestedConfiguration {
...
}
#Configuration
public static class SecondNestedConfiguration {
...
}
}
this class is providing via external library dependency and all conditions are satisfied, so all beans in these configurations are loading.
Nevertheless, I need to exclude beans provided in FirstNestedConfiguration
Is it possible to do it?
UPD: as it's simple Spring Boot application, it runs as
#SpringCloudApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
without any explicit #ComponentScan configuration
You might use excludeFilters in #ComponentScan like this:
#ComponentScan(value = {'your.package'},
excludeFilters = #Filter(TestAutoConfiguration.class))
Also, if you want to exclude specific autoconfiguration globally, use properties:
spring:
autoconfigure.exclude: your.package.TestAutoConfiguration
However, please, note that this way you exclude outer configuration. According to this issue it's not possible to exclude inner configuration.
Does the profile approach works for you?
Look for With the #Profile annotation section
#Profile("ConfigOne")
#Configuration
Configuration spring documentation

EnableWebMvc throws ServletException: Could not resolve view with name

Playing around with Spring Boot + MVC with static HTML pages, while noticed this thing:
Firstly, what I have:
Index controller:
#Controller
public class IndexController {
#RequestMapping("/")
public String index() {
return "index.html";
}
#RequestMapping("/{path:[^\\.]+}/**")
public String forward() {
return "forward:/";
}
}
The Html file is:...\src\main\resources\static\index.html
So when my main application class is:
#SpringBootApplication
public class MyApplication extends WebMvcConfigurerAdapter {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
Everything works well and in default path: localhost:8080\ I get index.html page content
But if I annotate Application class with #EnableWebMvc
#SpringBootApplication
#EnableWebMvc
public class MyApplication extends WebMvcConfigurerAdapter {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
I get exception: javax.servlet.ServletException: Could not resolve view with name 'index.html' in servlet with name 'dispatcherServlet'
But according to this spring doc it is a valid configuration.
Maybe someone can explain me why? Do I understand something wrong?
According to spring-boot's docs
The auto-configuration adds the following features on top of Spring’s defaults:
Static index.html support.
...
If you want to keep Spring Boot MVC features, and you just want to add
additional MVC configuration (interceptors, formatters, view
controllers etc.) you can add your own #Configuration class of type
WebMvcConfigurerAdapter, but without #EnableWebMvc. If you wish to
provide custom instances of RequestMappingHandlerMapping,
RequestMappingHandlerAdapter or ExceptionHandlerExceptionResolver you
can declare a WebMvcRegistrationsAdapter instance providing such
components.
So by adding #EnableWebMvc you just disable what spring-boot autoconfiguring for you. Namely static index.html support.
Actually I think when you choose to use spring boot you should use the default config of spring Boot. It means you just have to edit the file application.properties. Now if you use spring mvc, you have to provide your own servlet. So I think mixing up the to is not a good idea. Either you use spring Boot wiht no much config to do or you use spring mvc and you make all the necessary config.
According to Spring Boot MVC structure, you should locate your html file in the templates folder. Then will be visible for Spring Boot
src\main\resources\templates\index.html

Scan components of different maven modules/JARs in a Spring Boot application

I have two Maven modules.
The first one, called "application", contains the spring boot Application class that just contains these lines:
package org.example.application;
#SpringBootApplication
#ComponentScan({"org.example.model", "org.example"})
public class Application {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Application.class, args);
}
}
In the same Maven module and package, org.example.application, I have a RestController that uses a Component that in turn uses the components of the other Maven module described below.
The other Maven module, called "model", contains the spring boot components (crud-repositories, entities etc). All those classes are under the same package structure as the first Maven module (org.example) but in subpackages of that, like org.example.model.entities, org.example.model.repositories etc.
So, the flow is like this:
Maven module application in package org.example:
SpringBootApplication -> RestController -> MyComponent
And the components that should be autowired in MyComponent are the ones in the model Maven module under the package org.example.model.
But when I start the application I just get the error:
***************************
APPLICATION FAILED TO START
***************************
Description:
Field myRepository in org.example.MyComponent required a bean of type 'org.example.model.repositories.MyRepository' that could not be found.
Action:
Consider defining a bean of type 'org.example.model.repositories.MyRepository' in your configuration.
org.example.model.repositories.MyRepository does exist in Maven module "model" but cannot be found by the SpringBootApplication class!
As you can see, I have tried to explicitly define the scan components to:
#ComponentScan({"org.example.model", "org.example"}) but that does not seem to help.
So what have I done wrong?
The first thing that you should wonder is : why do you declare #ComponentScan while one of the goal of #SpringBootApplication is (among other things) to enable the component scan ?
From Spring Boot documentation :
The #SpringBootApplication annotation is equivalent to using
#Configuration, #EnableAutoConfiguration and #ComponentScan with their
default attributes
Note that when on the class of your Spring Boot Application, you declare #ComponentScan to specify a value as basePackages, it overrides the basePackages used by default by #SpringBootApplication that is the current package where the class resides. So to have as base package both the package of the Spring Boot Application class and the additional packages that were missing, you have to explicitly set them.
Besides basePackages is recursive. So to enable the scan both for classes locating in the "org.example" and "org.example.model" packages, specifying "org.example" is enough as "org.example.model" is a sub-package of it.
Try that :
#SpringBootApplication(scanBasePackages={"org.example"})
Or alternatively :
#SpringBootApplication
#ComponentScan("org.example")
When specify #EnableJpaRepositories/#ComponentScan/scanBasePackages in a Spring Boot Application ?
As you design your Spring Boot application layout, your have two cases :
1) case (to favor) where you use a package layout that provides the auto configuration of Spring Boot with zero configuration.
To summarize : if your classes annotated with Spring Bean stereotypes : #Component, #Repositories, #Repositories,... are located in the same package or a sub-package of the Spring Boot Application class, declaring only
#SpringBootApplication is all you need.
2) case (to avoid) where you don't use a package layout that provides the auto configuration of Spring Boot with zero configuration.
It generally means that you have candidate classes to scan that are not in the package (or sub-package) of your class annotated with #SpringBootApplication.
In this case, you add the scanBasePackages attribute or add #ComponentScan to specify packages to scan.
But additionally, if your repositories are not located in a package or sub-package of your class annotated with #SpringBootApplication, something else has to be declared such as : #EnableJpaRepositories(="packageWhereMyRepoAreLocated")
Here is the documentation about this part (emphasis is mine) :
80.3 Use Spring Data Repositories
Spring Data can create implementations of #Repository interfaces of
various flavors. Spring Boot handles all of that for you, as long as
those #Repositories are included in the same package (or a
sub-package) of your #EnableAutoConfiguration class.
For many applications, all you need is to put the right Spring Data
dependencies on your classpath (there is a
spring-boot-starter-data-jpa for JPA and a
spring-boot-starter-data-mongodb for Mongodb) and create some
repository interfaces to handle your #Entity objects. Examples are in
the JPA sample and the Mongodb sample.
Spring Boot tries to guess the location of your #Repository
definitions, based on the #EnableAutoConfiguration it finds. To get
more control, use the #EnableJpaRepositories annotation (from Spring
Data JPA).
Examples
1) case (to favor) where you use a package layout that provides the auto configuration of Spring Boot with zero configuration.
With a Spring Boot application declared in the org.example package, and all bean classes (Repositories included) declared in the same package or a sub-package of org.example, the following declaration is enough for the Spring Boot application :
package org.example;
#SpringBootApplication
public class Application {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Application.class, args);
}
}
The repositories could be located in the org.example.repository package such as :
package org.example.repository;
#Repository
public interface FooRepository extends JpaRepository<Foo, Long>, { }
and
package org.example.repository;
#Repository
public interface BarRepository extends JpaRepository<Bar, Long>, { }
The controllers could be located in the org.example.controller package :
package org.example.controller;
#RestController
#RequestMapping("/api/foos")
public class FooController {...}
and so for...
2) case (to avoid) where you don't use a package layout that provides the auto configuration of Spring Boot with zero configuration.
With a Spring Boot application declared in the org.example.application package, and not all bean classes (Repositories included) declared in the same package or a sub-package of org.example.application, the following declaration will be required for the Spring Boot application :
package org.example.application;
#SpringBootApplication(scanBasePackages= {
"org.example",
"org.thirdparty.repository"})
#EnableJpaRepositories("org.thirdparty.repository")
public class Application {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Application.class, args);
}
}
And the bean classes could be as below.
The repositories that may come from an external a JAR could be located in the org.thirdparty.repository package such as :
package org.thirdparty.repository;
#Repository
public interface FooRepository extends JpaRepository<Foo, Long>, { }
and
package org.thirdparty.repository;
#Repository
public interface BarRepository extends JpaRepository<Bar, Long>, { }
The controllers could be located in the org.example.controller package :
package org.example.controller
#RestController
#RequestMapping("/api/foos")
public class FooController {...}
and so for...
Conclusion : defining the Spring Boot application in the base package of your namespace is really encouraged to make the Spring Boot configuration as simple as possible.

How to make sure Flyway loads before my bean?

I have a Spring Boot application in which a Bean loads configuration-data from the database.
Right now I set up this Bean in the Configuration class. But it seems it loads before Flyway.
How to make sure Flyway has finished it's job before my beans get loaded?
You can initialize it before you start Spring Boot application:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
// Init Flyway here
SpringApplication.run(Application.class, args);
}
}
Second option is to use #DependsOn annotation for your beans depending on Flyway.

Categories