I am developing a spring boot custom starter which pom contains some dependencies (other starters, libraries) and this starter does some config about jwt filtering to allow the filter at security level. The issue is when I add my custom starter as a pom dependency in another project (starter-consumer), it seems it detects the class I want to import but IntelliJ does nothing.
Maybe I didn't packaged the starter correctly, or at least the classes that I coded inside. The other dependencies that the starter contains in the pom are successfully added as a dependencies of the starter-consumer. For example, I use some jwt utils which dependency is in the parent starter. So that thing works ok.
import io.jsonwebtoken.impl.DefaultClaims;
The problem is when I try to import a custom class which I coded in the starter. This is my package structure:
I want to use the JwtConfig class in my starter-consumer. It appears but I can't import. It does nothing.
And then If I manually check package availability I see this:
Pepito is missing :( and theinit is the package name of the starter-consumer. The jar is installed in the local m2 so I get the dependency like this:
<dependency>
<groupId>com.pepito</groupId>
<artifactId>starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
Any insights on this?
Edit 1:
I removed the boot maven plugin as a you said, it seems now it is not packaged as boot app and my starter-consumer can import the clases I coded . One additional thing, what happens with beans? Does the autoconfigure starts the bean by itself or the starter-consumer needs to declare it?
Edit 2:
So as part of the solution of this post, I am trying to inject a bean from the starter into the starter-consumer.
Apart from another beans, here we have the jwtTokenAuthenticationFilter which I want to inject into my starter-consumer security config.
#Configuration
#ConditionalOnProperty(name = "security.jwt-enabled", havingValue = "true")
public class JwtAutoConfiguration extends AbstractHttpConfigurer<JwtAutoConfiguration, HttpSecurity> {
#Bean
public JwtConfig jwtConfig() {
return new JwtConfig();
}
#Bean
public FilterRegistrationBean registration(#Qualifier("jwtFilter") JwtTokenAuthenticationFilter filter) {
FilterRegistrationBean registration = new FilterRegistrationBean(filter);
registration.setEnabled(false);
return registration;
}
#Bean
public JwtTokenAuthenticationFilter jwtFilter() {
return new JwtTokenAuthenticationFilter(jwtConfig());
}
#Override
public void init(HttpSecurity http) throws Exception {
// initialization code
}
}
This is my spring.factories
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.pepito.starter.configuration.security.jwt.JwtAutoConfiguration
And in the starter-consumer I have the following
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private JwtTokenAuthenticationFilter jwtFilter;
And here is where I see the error in intellij that can't autowire that bean because it does not exist. I suppose is because of something about scan but in my starter-consumer I have #SpringBootApplication which is suppose it contains componentscan annotation.
I believe A couple of issues that I see here and some clarifications will help you to find an answer:
Module with starter is a regular jar. The only thing that differs is that it has META-INF/spring.factories which I see exist.
Having said that - I see in the Pepito starter module class SpringBootMicroserviceStarterApplication. This is wrong. If the starter is a spring boot application, then the chances are that you're using spring boot maven plugin to prepare it as an application.
But the jar created by this plugin is not really a Jar, more specifically it stores other jars in BOOT-INF lib and in general can't be considered a Jar build-tool-wise and IDE-wise.
To put it simple you cannot declare a dependency on something that gets packaged as a spring boot application!
Update 1
To address your OP's question in comments:
The starter is not a spring boot application. It should not have a method annotated with #SpringBootApplication, it should not be packaged as spring boot application and so forth.
The best way to view the starter is as an self-contained "feature" or "facility" (trying to find the most appropriate word in English) that can be used by Spring boot applications by importing the starter module.
In terms of testing, #SpringBootTest should not be used in starter's module because it mimics the startup of spring boot application which obviously does not exist here.
Its possible to test it with Unit Test or with Spring Testing Framework:
#RunWith(SpringRunner.class)
#ContextConfiguration(<here comes the class of AutoConfiguration in starter)
One last thing to clarify here:
In spring Factories you can specify the autoconfiguration that in turn declares a series of beans required by the starter.
These beans are resolved by spring boot application just like other beans, the difference is that the Configuration is identified out of spring.factories file and not by package structure or explicit configuration.
So to answer your question:
Would you know if I can declare a bean in the starter and then autowire it in the consumer?
Yes, as long as the starter gets loaded by spring boot application, you can autowire (or inject in any other way) beans from starter into the beans of the spring boot application itself.
I have a Spring Webflux application where I am trying to load a dependency from an old module (old module is on Spring WebMVC framework).
When the application is launched, this error is thrown -
***************************
APPLICATION FAILED TO START
***************************
Description:
The bean 'requestMappingHandlerAdapter', defined in class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class], could not be registered. A bean with that name has already been defined in class path resource [org/springframework/web/reactive/config/DelegatingWebFluxConfiguration.class] and overriding is disabled.
Action:
Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true
I want all the beans from webflux package to be initiated, so I can't set spring.main.allow-bean-definition-overriding=true.
Also tried excluding all classes within org.springframework.boot at the time of component scan - #ComponentScan(excludeFilters = #Filter(type = FilterType.REGEX, pattern = "org.springframework.boot*").
Also tried excluding all spring packages in pom.xml of my webflux project like this -
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</exclusion>
Since I cannot modify the older dependency project to webflux, are there any options I could use the make the code work ?
In your springboot startup class , the #EnableAutoConfiguration annotation will auto configure the mvc part (WebMvcAutoConfiguration will fail due to same bean name in DelegatingWebFluxConfiguration)
So try ti exclude this from auto config and try as below
#SpringBootApplication
#EnableAutoConfiguration(exclude = {WebMvcAutoConfiguration.class })
public static void main(String[] args) {
...
SpringApplication.run(MyApp.class, args);
}
If I understand correctly you have some Web-related dependencies on the classpath but aren't building a web application, you can explicitly tell SpringApplication that you don't want a web application:
app.setWebEnvironment(false);
This is the way to disabling Web-related auto-configuration as it means you don't need to know what those auto-configuration classes are and lets Spring Boot take care of it for you.
As mentioned in your problem description, Using dependencies of Spring MVC in Spring Webflux can cause this issue. I have solved this issue by excluding the group "org.springframework.boot" while including the old dependency.
In gradle.build i have did something like below:
implementation("dependency-using-spring-mvc") {
exclude(group= "org.springframework.boot")
}
I have created my first spring boot project and I have copied the example verbatim from springs offical website.
However, when I try to start using mvn spring:run, it fails with the following exception:
org.springframework.context.ApplicationContextException: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean.
You didn't say if your project is web project or not. Also since I don't see your application.properties or application.yaml. There could be so many reason and with limited amount of information on your question, I am trying to guess the answer.
If this doesn't work. update your question with more information.
If your project is not a web application project, try setting following on your application.properties
spring.main.web-application-type=none
And if you have application.yaml, set thefollowing
spring:
main:
web-application-type: none
With limited information, its hard to say whats wrong in your application. I had a similar issue and I believe it was due to corrupted jar files downloaded. I resolved referring this github issue.
There might be below reasons for ServletWebServerApplicationContext due to missing ServletWebServerFactory bean
You might have missing web dependency in you pom
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
You have not annoted #SpringBootApplication to your main class:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Spring Boot application runs on embedded tomcat server when run it from Eclipse or intellij idea. But when deployed on external tomcat server it gives 404 error.
Make sure you have done below steps:
Extends SpringBootServletInitializer
#SpringBootApplication
public class SpringBootWebApplication extends SpringBootServletInitializer
{
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder
application) {
return application.sources(SpringBootWebApplication.class);
}
public static void main(String[] args) throws Exception {
SpringApplication.run(SpringBootWebApplication.class, args);
}
}
Marked the embedded servlet container as provided in you pom.xml
<!-- marked the embedded servlet container as provided -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
Update packaging to war
<packaging>war</packaging>
Copy the generated war into Tomcat`s webapp folder and re-start tomcat.
Go to admin page of tomcat and see if you can find your your app and its status is running/started.While accessing URL make sure you are appending right context path,if defined using "server.context" property in application.properties file.
Paste any specific error in case otherwise,if you still face the issue.
I have a simple Spring Boot application that gets messages from a JMS queue and saves some data to a log file, but does not need a web server. Is there any way of starting Spring Boot without the web server?
if you want to run Spring Boot 1.x without a servlet container, but with one on the classpath (e.g. for tests), use the following, as described in the spring boot documentation:
#Configuration
#EnableAutoConfiguration
public class MyClass {
public static void main(String[] args) throws JAXBException {
SpringApplication app = new SpringApplication(MyClass.class);
app.setWebEnvironment(false); //<<<<<<<<<
ConfigurableApplicationContext ctx = app.run(args);
}
}
also, I just stumbled across this property:
spring.main.web-environment=false
Spring Boot 2.x, 3.x
Application Properties
spring.main.web-application-type=NONE
# REACTIVE, SERVLET
or SpringApplicationBuilder
#SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(MyApplication.class)
.web(WebApplicationType.NONE) // .REACTIVE, .SERVLET
.run(args);
}
}
Where WebApplicationType:
NONE - The application should not run as a web application and should not start an embedded web server.
REACTIVE - The application should run as a reactive web application and should start an embedded reactive web server.
SERVLET - The application should run as a servlet-based web application and should start an embedded servlet web server.
You can create something like this:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
new SpringApplicationBuilder(Application.class).web(false).run(args);
}
}
And
#Component
public class CommandLiner implements CommandLineRunner {
#Override
public void run(String... args) throws Exception {
// Put your logic here
}
}
The dependency is still there though but not used.
Spring boot will not include embedded tomcat if you don't have Tomcat dependencies on the classpath.
You can view this fact yourself at the class EmbeddedServletContainerAutoConfiguration whose source you can find here.
The meat of the code is the use of the #ConditionalOnClass annotation on the class EmbeddedTomcat
Also, for more information check out this and this guide and this part of the documentation
The simplest solution. in your application.properties file. add the following property as mentioned by a previous answer:
spring.main.web-environment=false
For version 2.0.0 of Spring boot starter, use the following property :
spring.main.web-application-type=none
For documentation on all properties use this link : https://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html
Use this code.
SpringApplication application = new SpringApplication(DemoApplication.class);
application.setWebApplicationType(WebApplicationType.NONE);
application.run(args);
For Spring boot v2.1.3.RELEASE, just add the follow properties into application.propertes:
spring.main.web-application-type=none
If you need web functionality in your application (like org.springframework.web.client.RestTemplate for REST calls) but you don't want to start a TOMCAT server, just exclude it in the POM:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
Through program :
ConfigurableApplicationContext ctx = new SpringApplicationBuilder(YourApplicationMain.class)
.web(WebApplicationType.NONE)
.run(args);
Through application.properties file :
spring.main.web-environment=false
Through application.yml file :
spring:
main:
web-environment:false
Spring boot has many starters, some starters have an embedded web server, some don't. The following have the embedded web server:
spring-boot-starter-web
spring-boot-starter-data-jpa
spring-boot-starter-jetty
spring-boot-starter-tomcat
spring-boot-starter-jdbc
spring-boot-starter-data-rest
...
Pick the one that meets your requirements and that does not have server support.
I only need to make restful json api request in my spring application, so the starter I need is
spring-boot-starter-json
which provide RestTemplate and jackson for me to use.
You can use the spring-boot-starter dependency. This will not have the web stuff.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
If you want to use one of the "Getting Started" templates from spring.io site, but you don't need any of the servlet-related stuff that comes with the "default" ("gs/spring-boot") template, you can try the scheduling-tasks template (whose pom* contains spring-boot-starter etc) instead:
https://spring.io/guides/gs/scheduling-tasks/
That gives you Spring Boot, and the app runs as a standalone (no servlets or spring-webmvc etc are included in the pom). Which is what you wanted (though you may need to add some JMS-specific stuff, as someone else points out already).
[* I'm using Maven, but assume that a Gradle build will work similarly].
Remove folowing dependancy on your pom file will work for me
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
For Kotling here is what I used lately:
// src/main/com.blabla/ShellApplication.kt
/**
* Main entry point for the shell application.
*/
#SpringBootApplication
public class ShellApplication : CommandLineRunner {
companion object {
#JvmStatic
fun main(args: Array<String>) {
val application = SpringApplication(ShellApplication::class.java)
application.webApplicationType = WebApplicationType.NONE
application.run(*args);
}
}
override fun run(vararg args: String?) {}
}
// src/main/com.blabla/command/CustomCommand.kt
#ShellComponent
public class CustomCommand {
private val logger = KotlinLogging.logger {}
#ShellMethod("Import, create and update data from CSV")
public fun importCsv(#ShellOption() file: String) {
logger.info("Hi")
}
}
And everything boot normally ending up with a shell with my custom command available.
In Spring boot, Spring Web dependency provides an embedded Apache Tomcat web server. If you remove spring-boot-starter-web dependency in the pom.xml then it doesn't provide an embedded web server.
remove the following dependency
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
From my experience on spring boot > 2.5 ,
if you plan to build the application as a jar file, in my opinion the solution of spring.main.web-application-type=NONE should not be widely accepted and used, since it has only a limited scope of benefits.
For asking to have spring boot without the web server, it means that you have from Spring either the dependency spring-boot-starter-web to build a spring web application or the dependency spring-boot-starter-jersey to build a jax-rs web application. Those dependencies pack inside the spring-boot-starter-tomcat which will then bring the dependency of tomcat-embed-core which is the actual tomcat server. This library is packed automatically inside and is of size ~3.3 MB. Even if you disable the server with the aforementioned property, you will still deliver your application jar file, containing the tomcat server inside.
So the con of just using the aforementioned property is that the deliverable jar file will be some MB larger in size without any actual need.
So if you want to have spring boot without the web server just don't use the dependencies spring-boot-starter-jersey or spring-boot-starter-web since if you build your application as a jar file there is no reason to have those dependencies and not have an embedded server delivered.
if you plan to build the application as a war file, you should also not use the above property.
In this case you will just need in your .pom the following configurations
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web or (spring-boot-starter-jersey)</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>pick the version that the server that already runs in production supports</version>
<scope>provided</scope>
</dependency>
</dependencies>
Exceptional cases
The property spring.main.web-application-type=NONE should be in my opinion used for some exceptional cases like if we build some web library that needs the above dependencies but is not to be used like a web application, or we have some complex type of testing that needs those libraries although the application does not need any server to run. This type of usages are however rare.
Similar to #nayun oh answer above, but for older versions of Spring, use this code:
SpringApplication application = new SpringApplication(DemoApplication.class);
application.setApplicationContextClass(AnnotationConfigApplicationContext.class);
application.run(args);