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?
Related
When I try to run a Spring Boot JavaFX project with
mvn spring-boot:run
I get an error "JavaFX runtime components are missing". The JDK version is 17.
I did an online search on the error message. Two solutions I find. One is to make the application modular. I don't see that is a valid solution for the Spring Boot application. And the other one is to add command line arguments for module path and modules names. The Maven command doesn't take arguments "--module-path" nor "--add-modules".
I also try to run the project with JDK only
java -jar app.jar
Although there aren't any errors, the GUI doesn't show up.
How to resolve this issue? A good solution would be that it is easy to run without many downloading, local setup etc.
Since Java 11, JavaFX was removed from the SDK. It is now in its own separate module, and if you want to use it in your application, you will need to specifically include it.
Regarding, "to make the application modular":
Spring 5 and Spring Boot are not friendly with Java Platform Module System (JPMS). It won't really be built for modules until Spring 6/Springboot 3 is released.
I do try the modular approach with Spring Boot 2. However, compilation
failed due to a known Lombok
error.
Regarding, "to add command line arguments for module path and modules names. The Maven command doesn't take arguments "--module-path" nor "--add-modules":
You can use non-modular approach without --module-path and --add-modules.
Maven configuration is pretty straightforward.
Firstly, Add the JavaFX modules you need as maven dependencies. For instance:
<properties>
<java.version>17</java.version>
<javafx.version>17.0.2</javafx.version>
</properties>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>${javafx.version}</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
<version>${javafx.version}</version>
</dependency>
The project can be built and run using Spring Boot Maven Plugin
NOTE: There is a maven plugin available for running JavaFX 11+ applications. So far, it is not required since the application will be packed and run using Spring Boot.
You may find a working example here, showcasing how to bootstrap JavaFX applications within Spring Boot application.
How to bootstrap JavaFX application within Spring Boot application?
The bootstrap process is heavily inspired by Mr. Awesome Josh Long’s
Spring Tips:
JavaFX
installment.
Instead of calling SpringBootApplication.run() use a custom bootstrap class inheriting from JavaFX Application. This is needed to initialize JavaFX correctly.
JavaFxApplication class does the heavy lifting for creating a proper JavaFX application with initialized Spring Context. It's responsible for:
Set Spring Boot web server type to NONE.
Programmatically create a Spring Boot context in the Application#init() method.
Kick off application logic by sending a StageReadyEvent containing the primary Stage as payload.
Support graceful shutdown for both Spring context and JavaFX platform.
Im literally searching for the same...
But I found there are some mods to do in your run-config to get this problem solved
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.
Spring boot starter project provides extensive set of functionalities auto configured. But for our application we want to have only a subset of functionality. May be only one feature out of the spring boot starter project. So is it advised to have custom starter project on top of spring boot provided starter project to mask some of the features or write new starter project directly from lower level libraries?
Spring boot starter project provides extensive set of functionalities
auto configured
There are two separate concerns you are talking about.
I think the auto configured part is something which is Spring boot's opinionated way of configuring. As an example if in classpath it finds a in-memory database library ( like H2) it automatically creates a datasource (pointing to embedded in-memory database) which is available for autowiring without you writing the configuration in a Java config class. Of course you can create you own datasource of choice by including appropriate driver jar for that database. Similarly lots of other configurations are done by default by classpath scanning and availability of certain jars and classes.
The second part - is more of a dependency issue in general. Say you want to use web mvc part in Spring boot. This will require a consistent set of dependencies and transitive dependencies as well. Rather than finding and declaring the dependency in your build tool of choice ( maven, gradle etc) it has created the concept of starter projects where you simply mention the parent and all the right dependencies would be pulled on. This gives a great way to correctly lock your dependencies. However if you want to include any different version than what is provided by boot starter ( provided there is no compatibility issues with those different versions) you can still add explicitly in your build tool. For e.g., maven will by default include the highest version of a particular dependency among all available via transitive dependencies when it resolves dependency for an artifact.
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