Search code examples
javaspringspring-bootjar

Invalid bean definition is defined in class path resource as there is already a Generic bean


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


Solution

  • As already pointed out by M. Deinum on the comments you are effectively creating a second (unnecessary) context leading to your described error.

    A recommendation to setup a spring boot app, regarding your question:

    • let your 'server class in question' be plain as in your example but without any annotation:
    package com.company.data;
    
    public class DataServer {
        // your fields
        public DataServer(X arg1, Y arg2) {
            // [...]
        }
        public void func1() {
           // [...]
        }
        // other stuff
    }
    
    • have a separate class defining your application and the bean discovery
    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);
        }
    }
    
    • then add a component that does some extra startup logic as desired
    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

    Hint about same solution variants:

    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".


    Further hints:

    • 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)