java 有没有办法正确集成 spring-batch-admin 和 spring-boot?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/27139100/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 11:18:41  来源:igfitidea点击:

Is there a way to integrate spring-batch-admin and spring-boot properly?

javaspringspring-mvcspring-batchspring-batch-admin

提问by Selim Ok

According to the documentationspring batch admin is very easy to embed into the existing application. Simply copying web.xml and index.jsp then adding needed dependencies is enough getting it to work.

根据文档,spring batch admin 很容易嵌入到现有的应用程序中。只需复制 web.xml 和 index.jsp 然后添加所需的依赖项就足以让它工作。

But if I want to use it in an existing spring boot project it getting worse. According to this examplethe configuration is a bit hacky but it works. UNTIL I try to use @EnableBatchProcessingannotation in my configuriton bean. Then I get the following exception.

但是如果我想在现有的 Spring Boot 项目中使用它,它会变得更糟。根据这个例子,配置有点hacky,但它可以工作。直到我尝试@EnableBatchProcessing在我的配置 bean 中使用注释。然后我得到以下异常。

Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jobBuilders' defined in class path resource [org/springframework/batch/core/configuration/annotation/SimpleBatchConfiguration.class]: Instantiation of bean failed; nested exception is org.springframework.beans.factory.BeanDefinitionStoreException: Factory method [public org.springframework.batch.core.configuration.annotation.JobBuilderFactory org.springframework.batch.core.configuration.annotation.AbstractBatchConfiguration.jobBuilders() throws java.lang.Exception] threw exception; nested exception is java.lang.ClassCastException: org.springframework.batch.core.repository.support.JobRepositoryFactoryBean$$EnhancerBySpringCGLIB$fa0273 cannot be cast to org.springframework.batch.core.repository.JobRepository
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:597)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1095)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:990)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:504)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getObject(AbstractBeanFactory.java:302)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:706)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:762)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:482)
    at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:109)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:691)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:320)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:952)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:941)
    at demo.Application.main(Application.java:35)
Caused by: org.springframework.beans.factory.BeanDefinitionStoreException: Factory method [public org.springframework.batch.core.configuration.annotation.JobBuilderFactory org.springframework.batch.core.configuration.annotation.AbstractBatchConfiguration.jobBuilders() throws java.lang.Exception] threw exception; nested exception is java.lang.ClassCastException: org.springframework.batch.core.repository.support.JobRepositoryFactoryBean$$EnhancerBySpringCGLIB$fa0273 cannot be cast to org.springframework.batch.core.repository.JobRepository
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:188)
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:586)
    ... 17 more
Caused by: java.lang.ClassCastException: org.springframework.batch.core.repository.support.JobRepositoryFactoryBean$$EnhancerBySpringCGLIB$fa0273 cannot be cast to org.springframework.batch.core.repository.JobRepository
    at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$$EnhancerBySpringCGLIB$$b5c6eb04.jobRepository(<generated>)
    at org.springframework.batch.core.configuration.annotation.AbstractBatchConfiguration.jobBuilders(AbstractBatchConfiguration.java:58)
    at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$$EnhancerBySpringCGLIB$$b5c6eb04.CGLIB$jobBuilders(<generated>)
    at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$$EnhancerBySpringCGLIB$$b5c6eb04$$FastClassBySpringCGLIB$$d88bd05f.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228)
    at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:312)
    at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$$EnhancerBySpringCGLIB$$b5c6eb04.jobBuilders(<generated>)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:166)
    ... 18 more

My configuration is quite simple I have two configuration beans

我的配置很简单我有两个配置bean

@Configuration
@ImportResource({"classpath:/org/springframework/batch/admin/web/resources/servlet-config.xml", 
        "classpath:/org/springframework/batch/admin/web/resources/webapp-config.xml"})
public class BatchAdminConfiguration {
}

and

@Configuration
@EnableBatchProcessing
public class BatchImporterConfiguration { 
}

When I remove @EnableBatchProcessing and try to create jobs with JobBuilderFactory and use @StepScope annotation I'm getting other ClassCastExceptions.

当我删除 @EnableBatchProcessing 并尝试使用 JobBuilderFactory 创建作业并使用 @StepScope 注释时,我收到了其他 ClassCastExceptions。

Now I'm using xml based configuration to create jobs, steps and other beans. It work well but I would actually preffer xml free configuration. Is there a way to easily integrate spring boot, spring batch and spring batch admin ?

现在我使用基于 xml 的配置来创建作业、步骤和其他 bean。它运行良好,但我实际上更喜欢 xml 自由配置。有没有办法轻松集成 spring boot、spring batch 和 spring batch admin?

采纳答案by Michael Minella

The short answer is that you won't want to use @EnableBatchProcessingwith Spring Batch Admin. SBA provides a number of beans on a global scale that the @EnableBatchProcessingalso provides. SBA 2.0 (currently in development) will probably fill the gaps between what is currently there and what @EnableBatchProcessingprovides (specifically providing the JobBuilderFactoryand StepBuilderFactory).

简短的回答是您不想@EnableBatchProcessing与 Spring Batch Admin一起使用。 SBA 在全球范围内提供了许多 bean,@EnableBatchProcessing也提供了这些 bean 。 SBA 2.0(目前正在开发中)可能会填补当前存在的内容与@EnableBatchProcessing提供的内容(特别是提供JobBuilderFactoryStepBuilderFactory)之间的差距。

To get yourself running, you should be able to (I haven't tired this myself) configure in the META-INF/spring/batch/override/directory a JobBuilderFactoryand a StepBuilderFactoryfor global use. From there, you can use XML files in the META-INF/spring/batch/jobsdirectory that do nothing more than component scan for your @Configurationclasses. However, leave off the @EnableBatchProcessingbecause of the duplication of beans.

为了让自己运行起来,您应该能够(我自己还没有厌倦)在META-INF/spring/batch/override/目录 aJobBuilderFactory和 a 中StepBuilderFactory进行配置以供全局使用。从那里,您可以使用目录中的 XML 文件,这些文件META-INF/spring/batch/jobs仅对您的@Configuration类进行组件扫描。但是,@EnableBatchProcessing由于 bean 的重复,请省略 。

For the record, this isn't an Spring Boot issue since @EnableBatchProcessingis a Spring Batch annotation, not a Boot one.

作为记录,这不是 Spring Boot 问题,因为@EnableBatchProcessing是 Spring Batch 注释,而不是 Boot 注释。

回答by smartwjw

Spring Batch Admin 2.0-BUILD-SNAPSHOT introduce a new Annoation @EnableBatchAdminfor easy to integrate with spring boot. There is also a samples project https://github.com/spring-projects/spring-batch-admin-samples.

Spring Batch Admin 2.0-BUILD-SNAPSHOT 引入了一个新的 Annoation @EnableBatchAdmin,以便于与 Spring Boot 集成。还有一个示例项目https://github.com/spring-projects/spring-batch-admin-samples

回答by abe

to complete the answer, here is the code to create the two beans once you disable the @EnableBatchProcessing annotation

要完成答案,这是禁用@EnableBatchProcessing 注释后创建两个bean 的代码

@Autowired
JobRepository jobRepository;
@Autowired
PlatformTransactionManager transactionManager;

@Bean
public JobBuilderFactory jobBuilderFactory() {
    return new JobBuilderFactory(jobRepository);
}

@Bean
public StepBuilderFactory stepBuilderFactory() {
    return new StepBuilderFactory(jobRepository, transactionManager);
}

回答by Abel ANEIROS

I've a working version here based on the same example (I forked the original one): https://github.com/vesperaba/spring-batch-admin-spring-boot.

我在这里有一个基于相同示例的工作版本(我分叉了原始版本):https: //github.com/vesperaba/spring-batch-admin-spring-boot

I followed Michael Minella advice and I overwrote the SpringBatch property holder with a custom one.

我遵循了 Michael Minella 的建议,并用自定义的属性覆盖了 SpringBatch 属性持有者。

I also added a job to check it's working now

我还添加了一份工作来检查它现在是否有效

回答by Tobias M

This ClassCastException is caused by

这个 ClassCastException 是由

classpath:/org/springframework/batch/admin/web/resources/servlet-config.xml

classpath:/org/springframework/batch/admin/web/resources/servlet-config.xml

loading

加载

META-INF/spring/batch/servlet/resources/resource-context.xml

META-INF/spring/batch/servlet/resources/resource-context.xml

which contains

其中包含

<mvc:annotation-driven />

<mvc:annotation-driven />

This conflicts with the mvc configuration in the Spring Java configuration class. The following class can be used to embed Spring Batch Admin within an existing application that uses Java configuration.

这与 Spring Java 配置类中的 mvc 配置冲突。以下类可用于在使用 Java 配置的现有应用程序中嵌入 Spring Batch Admin。

@Configuration
@EnableWebMvc
@ImportResource({"classpath*:/META-INF/spring/batch/bootstrap/**/*.xml"
    , "classpath*:/META-INF/spring/batch/override/**/*.xml"
    , "classpath*:/org/springframework/batch/admin/web/resources/webapp-config.xml" 
    , "classpath*:/META-INF/spring/batch/servlet/manager/**/*.xml"
    , "classpath:base-menu-config.xml"
    })
public class SpringBatchAdminConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addResourceHandlers(final ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/resources/**").addResourceLocations("classpath:/META-INF/");      
    }

    @Bean
    public SimpleControllerHandlerAdapter simpleControllerHandlerAdapter() {
        return new SimpleControllerHandlerAdapter();
    }

    @Bean
    public BeanNameUrlHandlerMapping beanNameUrlHandlerMapping() {
        return new BeanNameUrlHandlerMapping();
    }

    @Bean
    public BeanNameViewResolver beanNameViewResolver() {
        return new BeanNameViewResolver();
    }

    @Bean(name = "defaultResources")
    public PropertiesFactoryBean defaultResources() {
        return new PropertiesFactoryBean();
    }

    @Bean(name = "jsonResources")
    public PropertiesFactoryBean jsonResources() {
        return new PropertiesFactoryBean();
    }

    @Bean
    public HomeController homeController() throws IOException {
        HomeController homeController = new HomeController();
        homeController.setDefaultResources(defaultResources().getObject());
        homeController.setJsonResources(jsonResources().getObject());
        return homeController;
    }

    @Bean
    public MenuManager menuManager() {
        return new MenuManager();
    }

    @Bean(name = "freemarkerConfig")
    public HippyFreeMarkerConfigurer hippyFreeMarkerConfigurer() {
        HippyFreeMarkerConfigurer hippyFreeMarkerConfigurer = new HippyFreeMarkerConfigurer();
        hippyFreeMarkerConfigurer.setTemplateLoaderPaths("/WEB-INF/web", "classpath:/org/springframework/batch/admin/web");
        hippyFreeMarkerConfigurer.setPreferFileSystemAccess(false);
        hippyFreeMarkerConfigurer.setFreemarkerVariables(Collections.singletonMap("menuManager", (Object) menuManager()));
        Properties freemarkerSettings = new Properties();
        freemarkerSettings.put("default_encoding", "UTF-8");
        freemarkerSettings.put("output_encoding", "UTF-8");
        hippyFreeMarkerConfigurer.setFreemarkerSettings(freemarkerSettings);
        return hippyFreeMarkerConfigurer;
    }

    public AjaxFreeMarkerView parentLayout() {
        AjaxFreeMarkerView ajaxFreeMarkerView = new AjaxFreeMarkerView();
        FreeMarkerViewResolver freeMarkerViewResolver = new FreeMarkerViewResolver();
        freeMarkerViewResolver.setExposeSpringMacroHelpers(false);
        freeMarkerViewResolver.setAllowRequestOverride(true);
        ajaxFreeMarkerView.setViewResolver(freeMarkerViewResolver);
        Properties attributes = new Properties();
        attributes.put("titleCode", "home.title");
        attributes.put("titleText", "Spring Batch Admin");
        ajaxFreeMarkerView.setAttributes(attributes);
        return ajaxFreeMarkerView;
    }

    @Value("#{resourceService.servletPath}")
    private String servletPath;

    @Bean(name="standard")
    public AjaxFreeMarkerView standard() {
        AjaxFreeMarkerView standard = parentLayout();
        standard.setUrl("/layouts/html/standard.ftl");
        standard.setContentType("text/html;charset=UTF-8");
        standard.getAttributesMap().put("body", "/layouts/html/home.ftl");
        standard.getAttributesMap().put("servletPath", servletPath);
        return standard;        
    }

    @Bean(name="standard.rss")
    public AjaxFreeMarkerView standardRss() {
        AjaxFreeMarkerView standardRss = parentLayout();
        standardRss.setUrl("/layouts/html/standard.ftl");
        standardRss.setContentType("text/xml");
        standardRss.getAttributesMap().put("body", "/layouts/rss/home.ftl");
        standardRss.getAttributesMap().put("servletPath", servletPath);
        return standardRss;     
    }

    @Bean(name="standard.json")
    public AjaxFreeMarkerView standardJson() {
        AjaxFreeMarkerView standardJson = parentLayout();
        standardJson.setUrl("/layouts/json/standard.ftl");
        standardJson.setContentType("application/json");
        standardJson.getAttributesMap().put("body", "/layouts/json/home.ftl");
        standardJson.getAttributesMap().put("servletPath", servletPath);
        return standardJson;        
    }

    @Bean(name="home")
    public AjaxFreeMarkerView home() {
        return standard();
    }

    @Bean(name="home.json")
    public AjaxFreeMarkerView homeJson() {
        AjaxFreeMarkerView homeJson = standardJson();
        homeJson.getAttributesMap().put("body", "/layouts/json/home.ftl");
        return homeJson;
    }


}

A single XML file is also required for the abstract base menu which is referenced elsewhere in the Spring Batch Admin project. This is required as abstract beans can not be provided from a Spring Java configuration.

Spring Batch Admin 项目中其他地方引用的抽象基本菜单也需要一个 XML 文件。这是必需的,因为不能从 Spring Java 配置中提供抽象 bean。

<?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-2.5.xsd">

    <bean id="baseMenu" abstract="true">
        <property name="prefix" value="#{resourceService.servletPath}" />
    </bean>

</beans>

Maven dependencies. Take care to ensure only a single version of the base Spring framework is pulled in by Maven.

Maven 依赖项。请注意确保 Maven 仅拉入基本 Spring 框架的单个版本。

<dependency>
    <groupId>org.springframework.batch</groupId>
    <artifactId>spring-batch-admin-manager</artifactId>
    <version>1.3.1.RELEASE</version>
</dependency>
<dependency>
        <groupId>hsqldb</groupId>
        <artifactId>hsqldb</artifactId>
        <scope>runtime</scope>
        <version>1.8.0.10</version>
</dependency>

Spring batch also expects in a default configuration for the following files to exist at the root of the classpath.

Spring 批处理还期望在类路径的根目录中存在以下文件的默认配置。

batch-default.properties

批处理default.properties

# Default placeholders for database platform independent features 
batch.remote.base.url=http://localhost:8080/spring-batch-admin-sample
# Non-platform dependent settings that you might like to change
batch.job.configuration.file.dir=/tmp/config

build.artifactId=1
build.version=1
build.buildNumber=1
build.timestamp=1
log.enableConsole=true

batch-hsql.properties

批处理-hsql.properties

# Placeholders batch.*
#    for HSQLDB:
batch.jdbc.driver=org.hsqldb.jdbcDriver
batch.jdbc.url=jdbc:hsqldb:mem:testdb;sql.enforce_strict_size=true
# Override and use this one in for a separate server process so you can inspect 
# the results (or add it to system properties with -D to override at run time).
# batch.jdbc.url=jdbc:hsqldb:hsql://localhost:9005/samples
batch.jdbc.user=sa
batch.jdbc.password=
batch.database.incrementer.class=org.springframework.jdbc.support.incrementer.HsqlMaxValueIncrementer
batch.schema.script=classpath*:/org/springframework/batch/core/schema-hsqldb.sql
batch.drop.script=classpath*:/org/springframework/batch/core/schema-drop-hsqldb.sql
batch.business.schema.script=classpath:/business-schema-hsqldb.sql

# Non-platform dependent settings that you might like to change
# batch.data.source.init=true

business-schedule-hsqldb.sql

业务计划-hsqldb.sql

DROP TABLE  ERROR_LOG IF EXISTS;
CREATE TABLE ERROR_LOG  (
        JOB_NAME CHAR(20) ,
        STEP_NAME CHAR(20) ,
        MESSAGE VARCHAR(300) NOT NULL
) ;