I am trying to convert a spring application to springboot application. The application runs fine when I run it using the intellij GUI, but if i use the terminal and try to run it by java -cp
command it throws exception. The complete flow of the application along with error message is attached below-
The main class -
package com.company.data;
@SpringBootApplication
@ImportResource("classpath:spring/data-server-context.xml")
public class DataServer {
// other fields
private static DataServer dataServer;
// other fields
//Constructor
public DataServer( args ){
//assignments
}
public static void main(String[] args) throws Exception {
SpringApplication.run(DataServer.class,args);
LOG.info("Starting Data Server...");
ApplicationContext ctx = new ClassPathXmlApplicationContext("spring/data-server-context.xml");
try {
dataServer = ctx.getBean(DataServer.class)
//perform tasks
dataServer.func1()
}
catch (Exception e) {
//LOG error
throw e;
}
LOG.info("Data server started successfully!");
}
public void func1(){
//func def
}
}
data-server-context.xml looks something like this
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<import resource="classpath:spring/properties-context.xml"/>
<!-- other bean definitions-->
<bean id="dataServer" class="com.company.data.DataServer">
<constructor-arg name="arg1" ref="arg1_ref"/>
<constructor-arg name="arg2" ref="arg2_ref"/>
</bean>
<!-- other bean definitions -->
<beans profile="!test"> <!-- Don't create scheduler beans if spring test profile passed -->
<bean id="dataReloadJob" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="dataServer"/>
<property name="targetMethod" value="func1"/>
<property name="concurrent" value="false"/>
</bean>
<bean id="dataReloadTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="dataReloadJob"/>
<property name="cronExpression" value="some_expression"/>
</bean>
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="dataReloadTrigger"/>
</list>
</property>
</bean>
</beans>
</beans>
properties-context.xml -
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="propertyConfigurer" class="com.company.config.PropertyPlaceholderConfigurerAdapter">
<constructor-arg name="name" value="application"/>
</bean>
</beans>
Now as mentioned aboved if I run DataServer
from GUI it runs fine. But if I try to run it through terminal by compiling the jar , I get below error message -
06:38:36.909 [main] WARN org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Failed to register bean definition with name 'dataServer'
Offending resource: class path resource [spring/data-server-context.xml]; nested exception is org.springframework.beans.factory.support.BeanDefinitionOverrideException: Invalid bean definition with name 'dataServer' defined in class path resource [spring/data-server-context.xml]: Cannot register bean definition [Generic bean: class [com.company.data.DataServer]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [spring/data-server-context.xml]] for bean 'dataServer': There is already [Generic bean: class [com.company.data.DataServer]; scope=singleton; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null] bound.
06:38:36.916 [main] ERROR org.springframework.boot.SpringApplication - Application run failed
org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Failed to register bean definition with name 'dataServer'
Offending resource: class path resource [spring/data-server-context.xml]; nested exception is org.springframework.beans.factory.support.BeanDefinitionOverrideException: Invalid bean definition with name 'dataServer' defined in class path resource [spring/data-server-context.xml]: Cannot register bean definition [Generic bean: class [com.company.data.DataServer]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [spring/data-server-context.xml]] for bean 'dataServer': There is already [Generic bean: class [com.company.data.DataServer]; scope=singleton; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null] bound.
at org.springframework.beans.factory.parsing.FailFastProblemReporter.error(FailFastProblemReporter.java:72)
at org.springframework.beans.factory.parsing.ReaderContext.error(ReaderContext.java:119)
at org.springframework.beans.factory.parsing.ReaderContext.error(ReaderContext.java:104)
at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.processBeanDefinition(DefaultBeanDefinitionDocumentReader.java:314)
at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.parseDefaultElement(DefaultBeanDefinitionDocumentReader.java:197)
at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.parseBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:176)
at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.doRegisterBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:149)
at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.registerBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:96)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.registerBeanDefinitions(XmlBeanDefinitionReader.java:511)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:391)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:338)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:310)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:188)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:224)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:195)
at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.lambda$loadBeanDefinitionsFromImportedResources$0(ConfigurationClassBeanDefinitionReader.java:378)
at java.util.LinkedHashMap.forEach(LinkedHashMap.java:684)
at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsFromImportedResources(ConfigurationClassBeanDefinitionReader.java:345)
at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForConfigurationClass(ConfigurationClassBeanDefinitionReader.java:147)
at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitions(ConfigurationClassBeanDefinitionReader.java:120)
at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:332)
at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:237)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:280)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:96)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:707)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:533)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:143)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:758)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:750)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:405)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:315)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1237)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226)
at com.company.data.DataServer.main(DataServer.java:88)
Caused by: org.springframework.beans.factory.support.BeanDefinitionOverrideException: Invalid bean definition with name 'dataServer' defined in class path resource [spring/data-server-context.xml]: Cannot register bean definition [Generic bean: class [com.company.data.DataServer]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [spring/data-server-context.xml]] for bean 'dataServer': There is already [Generic bean: class [com.company.data.DataServer]; scope=singleton; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null] bound.
at org.springframework.beans.factory.support.DefaultListableBeanFactory.registerBeanDefinition(DefaultListableBeanFactory.java:945)
at org.springframework.beans.factory.support.BeanDefinitionReaderUtils.registerBeanDefinition(BeanDefinitionReaderUtils.java:164)
at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.processBeanDefinition(DefaultBeanDefinitionDocumentReader.java:311)
... 30 common frames omitted
I have added spring.main.allow-bean-definition-overriding=true
to the application.properties
As already pointed out by M. Deinum on the comments you are effectively creating a second (unnecessary) context leading to your described error.
package com.company.data;
public class DataServer {
// your fields
public DataServer(X arg1, Y arg2) {
// [...]
}
public void func1() {
// [...]
}
// other stuff
}
package com.company.data;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ImportResource;
// this combination will tell spring to parse your XML file and create the defined beans
@SpringBootApplication
@ImportResource("classpath:spring/data-server-context.xml")
public class DataServerApp {
public static void main(String[] args) {
SpringApplication.run(DataServerApp.class, args);
}
}
package com.company.data;
import org.springframework.stereotype.Component;
import jakarta.annotation.PostConstruct;
import com.company.data.DataServer;
@Component
final class AppStartupComponent {
private final DataServer dataServer;
// inject your bean using constructor arg (the recommended way)
public AppStartupComponent(DataServer dataServer) {
this.dataServer = dataServer;
}
// gets called (precisely) 'on this beans creation' which is effectivly 'on app startup'
@PostConstruct
public void onAppSartup() {
// do your startup logic, from your exmaple:
try {
dataServer.func1()
} catch (Exception e) {
// logging and re-throw
throw e;
}
}
}
Your re-thrown exception will be 'populated all the way up' and stops the application startup with an exit code
of 1
.
If you want another exit code please have a look here
If @PostConstruct
is undesired you could also just put your 'startup logic' inside the class constructor, as long as you have an 'constructor injection' going.
You could also implement the InitializingBean
or ApplicationListener<ContextRefreshedEvent>
interface instead, each have a slightly other timepoint of execution. The order would be "Constructor > PostConstruct > InitializingBean".
In your case: setting spring.main.allow-bean-definition-overriding
is not required and should be avoided.
There is already sufficient logging from spring boot about the 'startup' like Starting DataServerApp using Java [...]
and Started DataServerApp in X seconds [...]
, so no need for such manual simple logging.
If possible try to migrate to java code based bean declaration (as spring suggests)