В ресурсе пути к классу определено неверное определение bean-компонента, поскольку универсальный bean-компонент уже существует

Я пытаюсь преобразовать приложение Spring в приложение SpringBoot. Приложение работает нормально, когда я запускаю его с помощью графического интерфейса Intellij, но если я использую терминал и пытаюсь запустить его с помощью команды java -cp , оно выдает исключение. Полный поток приложения вместе с сообщением об ошибке прилагается ниже:

Основной класс -

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 выглядит примерно так

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

свойства-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>

Теперь, как уже упоминалось выше, если я запускаю DataServer из графического интерфейса, он работает нормально. Но если я попытаюсь запустить его через терминал, скомпилировав jar, я получу сообщение об ошибке ниже:

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

Я добавил spring.main.allow-bean-definition-overriding=true к application.properties

Ваш основной метод не имеет смысла. Вы воссоздаете контекст и дважды запускаете приложение. SpringApplication.run уже возвращает контекст приложения, используйте его вместо этого.

M. Deinum 23.07.2024 13:48

@M.Deinum, не могли бы вы показать это в коде?

Moneet 23.07.2024 13:50

Я попробовал ApplicationContext ctx = SpringApplication.run(DataServer.class,args); и удалил ApplicationContext ctx = new ClassPathXmlApplicationContext("spring/data-server-context.x‌​ml");. Но у меня возникла та же ошибка, хотя на графическом интерфейсе все работало.

Moneet 23.07.2024 13:58

Вы прочитали ошибку? Удалите bean-компонент из вашего XML, вы уже автоматически создаете его, потому что это @SpringBootApplication.

M. Deinum 23.07.2024 14:02

Но мне нужен компонент, поскольку я использую его в data-server-context.xml в планировщике кварца. И как это работает нормально, когда я запускаю его в IDE, откуда @SpringBootApplication получает параметры конструктора?

Moneet 23.07.2024 14:06

Как уже говорилось, он уже существует из-за @SpringBootApplication, который в конце является @Component и, таким образом, зарегистрирован как bean-компонент. Если у вас есть параметры конструктора (они здесь не отображаются). Самый простой — переместить имеющийся у вас метод main в другой класс вместо того, чтобы добавлять его в DataServer.

M. Deinum 23.07.2024 14:20
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
6
66
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Как уже отмечал М. Дейнум в комментариях вы фактически создаете второй (ненужный) контекст, ведущий к описанной вами ошибке.

Рекомендация по настройке приложения весенней загрузки относительно вашего вопроса:

  • пусть ваш «класс сервера», о котором идет речь, будет простым, как в вашем примере, но без каких-либо аннотаций:
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;
        }
    }
}

Ваше повторно созданное исключение будет «заполнено полностью» и остановит запуск приложения с помощью exit code из 1. Если вам нужен другой код выхода, посмотрите здесь

Подсказка об одинаковых вариантах решения:

Если @PostConstruct нежелательно, вы также можете просто поместить свою «логику запуска» внутри конструктора класса, если у вас есть «инъекция конструктора».

Вместо этого вы также можете реализовать интерфейс InitializingBean или ApplicationListener<ContextRefreshedEvent>, каждый из которых имеет немного другой момент выполнения. Порядок будет таким: «Конструктор > PostConstruct > InitializingBean».


Дальнейшие подсказки:

  • В вашем случае установка spring.main.allow-bean-definition-overriding не требуется, и ее следует избегать.

  • Из Spring Boot уже имеется достаточное количество журналов о «запуске», таких как Starting DataServerApp using Java [...] и Started DataServerApp in X seconds [...], поэтому нет необходимости в таком простом ведении журнала вручную.

  • Если возможно, попробуйте перейти на объявление bean-компонента на основе Java-кода (как предлагает весна)

Спасибо. Я постараюсь внедрить ваши предложения

Moneet 24.07.2024 06:26

Другие вопросы по теме