We have a spring boot based application / collection of applications. Already in the first sentence it is complicated to describe it correctly, because from the business view 'the thing' is one application, but from technical view it as a collection of spring boot applications and batch jobs handling use cases of one business software.
For example the software consists of a spring boot application providing a rest-api, multiple spring boot applications providing different web services and multiple batch jobs doing different things based on spring boot applications and other applications.
Now some parts of the code are used only from one of the spring boot applications - so this can be located easily into this particular spring boot application.
But many parts of the code is used from multiple spring boot applications, but usually less then the half of the applications. For this business code we have a central module imported by all spring boot applications. Adding this central module to a spring boot application increases the big number of dependencies (e.g dependency to a CRM system, dependency to an S3 storage, dependency to generate Excel files, dependency to a external webservice, dependency to rabbit, ...). To avoid the hundreds of dependencies we have defined many as optional. So every spring boot application has only the dependencies it needs.
The problem now is in the central module with the implemented services with business code. These services are injected by autowire. For example a spring boot application providing a webservice interface doesn't need the service to generate Excel files and for that don't have a dependency to the excel libraries and doesn't need to start the excel generation service.
Spring has the possibility to annotate the services with #ConditionalOnClass and #ConditionalOnBean but many of the services doesn't depend only on one condition but on two or more.
So what is the best approach to structure the services to start them only if needed and/or the needed dependencies are available?
At the moment we use #ComponentScan with excludeFilters at every spring boot application but that is error prone. Splitting the central module into multiple modules or microservices is no option because then the modules contains only 1 to 5 classes.
Any good practice to structure applications like this?
You have understood the problem well and broken down into modules enough. In your case to solve this problem of using #ComponentScan with excludeFilters, you must try to do what Spring Boot does with its Auto Configuration mechanism.
For example with the data source configured in properties or yaml file and Spring Boot JPA Starter is in the classpath, Spring Boot Auto Configures everything for us. You need write some custom Starters with logic to Auto Configure your services based on the multiple conditions you have. And in your microservices, batch jobs you can include this custom Starters to enable and inject the necessary Services and those alone.
Sometime back I wrote two blog articles on how to write custom Spring Boot Starters. These could be a good start for you. Post 1 and Post 2.
If your module is declaring the common module as a dependency, you can set the scope of that module to 'compile' and then set the dependencies that should be excluded from the common module.
I recently encountered a situation where I required JPA in my common module in order to create generic DAOs for other modules. But I thought it would be wasteful to add a dependency that large my other module that was not utilizing JPA.
This was the solution:
<!-- ... -->
<groupId>com.multimodule</groupId>
<artifactId>server-app</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>${spring.version}</version>
<relativePath />
</parent>
<dependencies>
<dependency>
<groupId>com.multimodule</groupId>
<artifactId>common</artifactId>
<version>1.0.0-SNAPSHOT</version>
<scope>compile</scope>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</exclusion>
<exclusion>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- ... -->
</dependencies>
<!-- ... -->
By doing this, in addition to excluding the JPA-related packages in the common module using #ComponentScan, the common dependency is included when the server-app module compiles, but does not include the JPA jars with it.
If #ComponentScan is not added, the module would still compile, but there would probably be exceptions as soon as a bean is wired from common that attempts to wire a bean from any of the excluded jars.
This is immediately true in my case, since my common module would have attempted to autoconfigure Jpa during startup, if it had not been excluded, because of the the #EnableAutoConfiguration in #SpringBootApplication.
Related
I have a Spring Boot 2.1 web application. It works great. I can package it as either a WAR or a JAR.
In my pom.xml file I use:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
</parent>
This application has many entity classes and quite a few Spring Data repositories. Almost always we deploy it as a WAR file.
Here's the question: sometimes we need to do a command line or batch process on our database. For example, we might want to run a process to resize all the images which are stored in the database, and that should be run by an administrator from the command line.
It would be great if mvn install would install a JAR file in the local Maven repository and I could use that artifact in another project to access my entity and repository definitions. I've tried many things, but whenever I build my project as a JAR file, and I look at the JAR, all my classes are within BOOT-INF/classes, which doens't allow them to be referenced from another project.
I was able to fix that by using a repackage goal in the spring-boot-maven-plugin. However, when I did that, it did generate a jar file but my CLI application couldn't start correctly with the repository beans created.
I read in the Spring Boot documentation:
Like a war file, a Spring Boot application is not intended to be used
as a dependency. If your application contains classes that you want to
share with other projects, the recommended approach is to move that
code into a separate module. The separate module can then be depended
upon by your application and other projects.
Is there any simpler way to do this, such that I don't have to create yet another project and manage that? Or is it a good practice to have a separate project for entities and Spring Data repositories?
So, in short words you just want to have a library with your entity and repositories? Then it should be enough to configure a simple maven project, a standard one, not inheriting from Spring Boot.
As spring Boot uses Spring Data JPA under the covers, you just need Spring Data JPA declarations, so add the dependency marking it as provided.
<dependencies>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>Some reference version<version>
<scope>provided<scope>
</dependency>
<dependencies>
This allows not to make the project include the Spring Data JPA dependency itself, as it will be included by the end project, which uses Spring Boot. However, you'll need to declare a reference version to use, you could take the one your current Spring Boot project uses (you can have a look in the maven dependency tree).
https://spring.io/projects/spring-data-jpa
Difference between maven scope compile and provided for JAR packaging
I am working on building an oAuth2 application using spring boot. However, there are various sample projects in Github using spring-security-oauth2 and spring-cloud-starter-oauth2.
Do we have specific scenarios where we can use a specific jar among both for an application?
Though Spring cloud is mainly used for distributed systems. There are a lot of implementations on Github using spring-cloud-starter-oauth2 for even non-distributed applications. Thanks.
To resolve complex dependency management, Spring Boot starters were introduced. Starter POMs are a set of dependency descriptors (combines multiple commonly used dependencies into one POM) which otherwise you could also manually include in your application individually. Starters are available for web, test, data jpa, security, mailing and more. If it is not starter, it is a module: a simple artifact.
If you want to work with web, you could include tomcat, mvc and jackson all by yourself (manually) - a lot of dependencies for a single simple application. Instead, you just introduce one starter dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Coming to your question:
spring-security-oauth2 is an artifact of group org.springframework.security.oauth which supports oAuth2 (only) for Spring Security (not cloud), whereas spring-cloud-starter-oauth2 is a set of multiple dependencies like a starter web dependency above. This is OAuth2 starter for Spring Cloud that is only if you are working with Spring cloud. This starter comes with bundle of out-of-the-box dependencies underneath the OAuth2 framework for Spring Cloud like SSO, OAuth2 client.
Spring initially moved oauth2 to spring cloud started but as of version 2.4.0.M1 it was moved to spring security. You will be able to verify on start.spring.io that oauth2 cloud dependency is only in version >=2.0.0.RELEASE and <2.4.0.M1
Which's the difference between
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
</dependency>
and,
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
I'm developing an spring boot service.
spring-boot-starter-data-mongodb contains configuration classes for Spring Boot. It also includes the spring-data-mongodb library so you would only need to include the start in your boot app:
https://search.maven.org/artifact/org.springframework.boot/spring-boot-starter-data-mongodb/2.0.5.RELEASE/jar
spring-boot-starter-data-mongodb is a spring boot starter pom. For more information on starters:
spring-boot-starters
Dependency management is a critical aspects of any complex project. And doing this manually is less than ideal; the more time you spent on it the less time you have on the other important aspects of the project.
Spring Boot starters were built to address exactly this problem. Starter POMs are a set of convenient dependency descriptors that you can include in your application. You get a one-stop-shop for all the Spring and related technology that you need, without having to hunt through sample code and copy paste loads of dependency descriptors.
fair warning: I am new to Spring and maven.
The project:
The client has provided us with a web project that uses jsp to render front ends and uses spring-context only to manage some of their classes as Spring Beans. This project uses xml configuration. Here is the maven section that includes the spring-context:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.2.8.RELEASE</version>
</dependency>
Our job is to create an api layer for this project to open up access to the project's resources. The plan is to create a modern Spring Boot api using the spring-boot-web starter.
The problem:
The jsp project is split up into sub-modules. We are trying to take their business logic submodule as a maven dependency in our api project (it defines it's own spring context). The first problem that we have faced occurs when we simply add the module from the jsp project as a dependency in our api project. The spring boot initialization process provides no log feedback and hangs but does not exit (It runs fine when the dependency is not included). When crawling through with a debugger I have found that it is loading configuration files that are in the business module. My thinking is that one of the api's configuration files are getting overwritten which is breaking spring's logging and other behaviors.
The other more general problem is that I don't know if I can use the beans provided in the business logic module in my modern version of spring.
So the question is:
Is there a proper way to use beans defined in a dependent spring project?
I have the following maven dependecy in my project.
<dependency>
<groupId>org.jvnet.jax-ws-commons.spring</groupId>
<artifactId>jaxws-spring</artifactId>
<version>1.8</version>
</dependency>
Question:
Is this Spring Webservices project?
If not what this dependency is for?
Thanks for your help.
It's a project combining JAX-WS and Spring. Basically it gives you the wss namespace that you might be using in your application context to expose JAX-WS providers as web services. It isn't mandatory but it can be a convenience as it allows you to easily have dependency injection in your servlets although there are other ways to get this. Unfortunately, the last time I was using it I noticed that it was depending on some pretty old spring libraries (pre 3.x) and didn't seem to be updated in some time.