Can we implement a Java library by using Spring Boot? - java

As guided by the thread's name, I would like to create a JAVA library by using Spring Boot. I have found this thread: Creating a library jar using Spring boot. However, that thread's objectives seem to be solved by implementing it as a REST API.
Currently, I am developing a Spring-based JAVA library by using Spring Boot. And, I have tried to package as a jar file and let another JAVA application to use it in term of a JAVA library. Unfortunately, I found that when the caller application invokes some methods of the added library, configurations which are defined inside the library do not work at all.
It also shows an error like "CommandLineRunner does not exist".
For more information, a snippet of the pom.xml file is shown below. According to the configuration, I do not include dependencies for web applications.
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.3.3.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>2.3.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

When designed in the correct way this should not be a problem at all. But in detail it depends which features you're using. Since Spring supports external library like JPA, Websocket,..
There are two important annotations to develop a library and use it in another project.
The first one is simply #Configuration and the other one is #Import.
Library Project
Put a class in the root package which looks something like this.
#Configuration // allows to import this class
#ComponentScan // Scan for beans and other configuration classes
public class SomeLibrary {
// no main needed here
}
Other Project using the library
As usually put a class in the root package of your project.
#SpringBootApplication
#Import(SomeLibrary.class) // import the library
public class OtherApplication {
// just put your standard main in this class
}
It is important to keep in mind, that other things might be necessary depending on what your using in terms of other frameworks.
For example if your using spring-data the #EntityScan annotation extends the hibernate scan.

Related

Creating and importing a custom Spring library whilst separating shared dependencies

Im looking to create a Spring library project to share across an internal team.
At a very basic concept level The library will send message events to a queue and my plan is to standardise this within a team across several Spring Boot Microservices send messages the same way.
My pom in the library project looks something like this
<artifactId>my-library</artifactId>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
etc...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.16.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.2.Final</version>
</dependency>
<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>javax.el</artifactId>
<version>2.2.6</version>
</dependency>
I have a service in the library project that looks like this
public class EventService {
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
public void sendAuditEvent(AuditMessage auditMessage){
Set<ConstraintViolation<AuditMessage>> violations = validator.validate(auditMessage);
if(!isEmpty(violations)){
log.error("Unable to send audit message");
violations.stream().forEach( v-> log.error(v.getMessage()));
}
log.info("Found {} violations", violations.size());
// etc blah blah
return;
}
}
When I import the library into another project my thinking is that I can Autowire the EventService. By adding it in the pom and then
#ComponentScan({"my.library.package.eventlibrary.service"})
How do I prevent spring version locking? If the library is using spring 2.1.5.RELEASE today and the project that imports the library uses a different version would I not end up with potentially maven conflicts?
Also lets say the project that imports the library uses a lower version of hibernate api and the library has 6.0.16.Final. How would I prevent the project from using the newer one found one in the library classpath?
To clarify my question further is there a way I can separate the dependencies in the library from the project that uses it.
Pre Java 9. You can exclude the spring dependencies using maven when you declare the dependency to your module, same goes on for Hibernate. But you can't tell to your module to use a different hibernate version in a WAR.
If you want to work around this you can develop your library as independent micro service expose interface in the form of REST or Websocket if you want full duplex communication or something else JMS whatever....
Post Java 9 you can use java modularity to define the exact dependencies for your jar module. Check Project Jigsaw https://www.baeldung.com/project-jigsaw-java-modularity.
In your case in order to have different versions of the same library (hibernate). You would need two separate class loaders. To achieve this you would need to use layering read here http://openjdk.java.net/projects/jigsaw/spec/sotms/#layers
And here is the source code of many examples including ones that use layers. Focus on them : https://github.com/accso/java9-jigsaw-examples/tree/master/jigsaw-examples
You can try to exclude all transitive dependencies that your library can bring to projects that will use it.
To do this you should replace spring-boot-starter-parent with spring-boot-dependencies in dependencyManagement section and use provided scope for all dependencies which the library needs to work with and which will be exactly used by the projects, that will work with the library.
For example, a pom.xml of your library can be looks like this:
<!-- ... -->
<groupId>com.example</groupId>
<artifactId>library</artifactId>
<version>0.1.0</version>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<spring-boot.version>2.1.5.RELEASE</spring-boot.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<!-- ... -->
Then you will be able to use your library in the different projects, that use for example the old Spring Boot:
<!-- ... -->
<groupId>com.example</groupId>
<artifactId>old-project</artifactId>
<version>0.13.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.19.RELEASE</version>
<relativePath/>
</parent>
<!-- ... -->
<dependencies>
<dependency>
<groupId>com.example</groupId>
<artifactId>library</artifactId>
<version>0.1.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<!-- ... -->
So this project will use hibernate-validator:5.3.6.Final from its spring-boot-starter-web.
Important notes - the code of your library should be 'compatible' with this version of Spring Boot. In other words, you should test your library with different versions of Spring Boot in which you are interested.
See my project as an example.
Might be not what you are looking for, but you can distribute your library as a spring-boot-starter auto configuration module (of course, if the clients are spring boot applications).
This way you can control your dependencies in an agile way and you give your clients more freedom in using the library.
In your particular case, if you need to send a message to a queue you for sure need to have a corresponding classes in classpath. With auto configuration you can have Class Conditions or Been Conditions based on which you can track if your clients have correct configurations in runtime. You can also fail the context loading if something is wrong (providing a meaningful error message).
Spring also provides tracking mechanisms of what could happen if a particular class/library is missing.

Migrate java web app to spring boot?

I am considering modifying an existing java web app (with web.xml and applicationContext.xml files and Spring 2.5) to use Spring Boot. What are the implications of this process? I know I will need to create an #Configuration file to declare all of the beans (to eliminate the applicationContext.xml files), but would love some additional insight. Any suggestions are appreciated!
Migration would take several steps. Some of them are:-
Add Spring Boot dependencies to your pom.xml or gradle file.
Define all the XML beans using Java Configuration by #Bean
Define Spring Security if used through Java Configuration
Add application.properties to resources directory and defines all the configuration values like database host,username,password etc
Define your views inside resources/templates
You've to include all the dependencies required. Some of the dependencies are:-
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
You could have some other dependencies too like Spring Security. If your application uses Spring Security you have to provide java configuration for it.
http://www.baeldung.com/spring-boot-security-autoconfiguration
https://spring.io/blog/2014/01/30/migrating-from-spring-framework-3-2-to-4-0-1

Can I easily replace spring-boot-starter by spring-boot to avoid use of spring-boot-starter-parent?

I want to use spring-boot-starter, but that means I have to use the "spring-boot-starter-parent" pom and cannot use the company wide used root pom.
Spring-boot-starter is a huge advantage when it comes to manage dependecy and plugin that go together. But that contradict the advantage of our company wide used root pom.
Are there further advantages of using spring-boot-starter?
Is there an easy way to only use spring-boot without spring-boot-starter?
If you do not want to use the spring-boot-starter-parent, you can still keep the benefit of the dependency management (but not the plugin management) by using a scope=import dependency, as follows:
<dependencyManagement>
<dependencies>
<dependency>
<!-- Import dependency management from Spring Boot -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.0.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
And here you can find an appropriate section in Spring Documentation with more details.
Add needed version of spring-boot-starter-parent as the parent to your company's root pom.xml file.
This way it is spring-boot that manages all the dependencies for all of your projects.
Your Company's parent-pom can have its own <dependencyManagement> to override and manage any internal or third-party dependencies.
The plugin management is cleaner this way. You can define your own stages on project and parent level.

Spring auto load changes in jsp without compiling again

I come today with a simple question. Is there any plugin I can use in spring so everytime I make changes in one of my .jsp files, it makes a fast re-compiling instead of having to stop the project and compile it all over again?
For references I'm working on Spring Tool Suite and my project uses Maven, I've read about Spring Loaded and used the following code to implement it, but I just can't make it work.
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>springloaded</artifactId>
<version>1.2.6.RELEASE</version>
</dependency>
</dependencies>
</plugin>
Can anyone give me any advice about it, or if there's really a way to do this?

Cannot find org.springframework.integration.Message when using spring-boot-starter-integration

I'm using Spring boot and want to use the starter pom for Spring Integration.
In my POM I have:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.3.RELEASE</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-integration</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
This pulls in 4.3.6 versions of Spring Integration jars and 4.3.5 of Spring Framework jars. In one of my own classes I'm trying to use Message:
import org.springframework.integration.Message;
public Object doThings(Message<?> message) {
}
but I can't seem to locate Message. In an older version of Spring integration it was in spring-integration-core.jar but it's not there in this version. Has it moved or has something changed? I've checked the docs and it's still referenced so I assume I'm looking in the wrong place - but core sounds like the place it should be in to me! What am I doing wrong?
A few core concepts of Spring Integration have been merged inside Spring Core between versions 3.0 and 4.0, and org.springframework.integration.Message is one of them.
In your code sample, replacing
import org.springframework.integration.Message;
public Object doThings(Message<?> message) {
}
by
import org.springframework.messaging.Message;
public Object doThings(Message<?> message) {
}
will do the trick.
For a more exhaustive list of affected classes and interfaces, have a look at the 3.0 to 4.0 Spring Integration migration guide
It ok for me with using this version
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-messaging</artifactId>
<version>4.1.9.RELEASE</version>
</dependency>
and
import org.springframework.messaging.Message;

Categories

Resources