복붙노트

[SPRING] 스프링 - 배치 관리자와 스프링 부트를 적절하게 통합 할 수있는 방법이 있습니까?

SPRING

스프링 - 배치 관리자와 스프링 부트를 적절하게 통합 할 수있는 방법이 있습니까?

문서 스프링 배치 관리에 따르면 기존 응용 프로그램에 포함하기가 매우 쉽습니다. web.xml과 index.jsp를 복사하고 필요한 종속성을 추가하기 만하면됩니다.

하지만 기존의 스프링 부트 프로젝트에서 사용하고 싶다면 점점 더 악화되고 있습니다. 이 예제에 따르면 구성은 약간 해킹되지만 작동합니다. 내 configuriton bean에서 @EnableBatchProcessing 어노테이션을 사용하려고 시도 할 때까지. 그렇다면 다음과 같은 예외가 있습니다.

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$$49fa0273 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$1.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$$49fa0273 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$$49fa0273 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$8(<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

내 구성은 매우 간단합니다. 두 개의 구성 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 {
}

@Configuration
@EnableBatchProcessing
public class BatchImporterConfiguration { 
}

@EnableBatchProcessing을 제거하고 JobBuilderFactory로 작업을 만들고 @StepScope 주석을 사용하려고하면 다른 ClassCastExceptions이 표시됩니다.

이제 xml 기반 구성을 사용하여 작업, 단계 및 기타 bean을 작성합니다. 그것은 잘 작동하지만 실제로 xml 무료 구성을 preffer 것입니다. 스프링 부트, 스프링 배치 및 스프링 배치 관리를 쉽게 통합 할 수있는 방법이 있습니까?

해결법

  1. ==============================

    1.간단히 말해 Spring Batch Admin에서 @EnableBatchProcessing을 사용하고 싶지 않을 것입니다. SBA는 @EnableBatchProcessing도 제공하는 전역 규모의 여러 가지 빈을 제공합니다. SBA 2.0 (현재 개발 중)은 현재 존재하는 것과 @EnableBatchProcessing이 제공하는 것 (특히 JobBuilderFactory 및 StepBuilderFactory 제공) 사이의 차이를 채울 것입니다.

    간단히 말해 Spring Batch Admin에서 @EnableBatchProcessing을 사용하고 싶지 않을 것입니다. SBA는 @EnableBatchProcessing도 제공하는 전역 규모의 여러 가지 빈을 제공합니다. SBA 2.0 (현재 개발 중)은 현재 존재하는 것과 @EnableBatchProcessing이 제공하는 것 (특히 JobBuilderFactory 및 StepBuilderFactory 제공) 사이의 차이를 채울 것입니다.

    스스로 실행하려면 META-INF / spring / batch / override / 디렉토리에 JobBuilderFactory와 StepBuilderFactory를 전역 적으로 사용할 수 있도록 구성해야합니다. 거기에서 @Configuration 클래스에 대한 구성 요소 검사 이상의 작업을 수행하는 META-INF / spring / batch / jobs 디렉토리의 XML 파일을 사용할 수 있습니다. 그러나 Bean의 중복 때문에 @EnableBatchProcessing을 사용하지 마십시오.

    기록을 위해, @EnableBatchProcessing은 Spring 배치 주석이 아니고 Boot 태그가 아니기 때문에 이것은 Spring Boot 문제가 아닙니다.

  2. ==============================

    2.대답을 완료하려면 @EnableBatchProcessing 주석을 사용하지 않도록 설정 한 후 두 개의 빈을 만드는 코드를 작성하십시오.

    대답을 완료하려면 @EnableBatchProcessing 주석을 사용하지 않도록 설정 한 후 두 개의 빈을 만드는 코드를 작성하십시오.

    @Autowired
    JobRepository jobRepository;
    @Autowired
    PlatformTransactionManager transactionManager;
    
    @Bean
    public JobBuilderFactory jobBuilderFactory() {
        return new JobBuilderFactory(jobRepository);
    }
    
    @Bean
    public StepBuilderFactory stepBuilderFactory() {
        return new StepBuilderFactory(jobRepository, transactionManager);
    }
    
  3. ==============================

    3.Spring Batch Admin 2.0 - BUILD-SNAPSHOT은 스프링 부트와 쉽게 통합 할 수있는 새로운 Annoation @EnableBatchAdmin을 소개합니다. 샘플 프로젝트 https://github.com/spring-projects/spring-batch-admin-samples도 있습니다.

    Spring Batch Admin 2.0 - BUILD-SNAPSHOT은 스프링 부트와 쉽게 통합 할 수있는 새로운 Annoation @EnableBatchAdmin을 소개합니다. 샘플 프로젝트 https://github.com/spring-projects/spring-batch-admin-samples도 있습니다.

  4. ==============================

    4.나는 여기에 같은 예제 (나는 원래 하나를 포크)를 기반으로 작업 버전을했습니다 : https://github.com/vesperaba/spring-batch-admin-spring-boot.

    나는 여기에 같은 예제 (나는 원래 하나를 포크)를 기반으로 작업 버전을했습니다 : https://github.com/vesperaba/spring-batch-admin-spring-boot.

    나는 Michael Minella의 조언을 따랐고 SpringBatch 속성 홀더를 커스텀 테마 홀더로 덮어 썼다.

    나는 또한 지금 일하고 있는지 확인하기 위해 일자리를 추가했다.

  5. ==============================

    5.이 ClassCastException는 다음에 의해 발생합니다.

    이 ClassCastException는 다음에 의해 발생합니다.

    로딩

    포함하는

    이는 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;
        }
    
    
    }
    

    Spring Batch Admin 프로젝트의 다른 곳에서 참조되는 추상 기본 메뉴에는 하나의 XML 파일도 필요합니다. Spring 자바 설정에서 추상 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 의존성. Maven이 기본 스프링 프레임 워크의 단일 버전 만 가져 왔는지 확인하십시오.

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

    # 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

    DROP TABLE  ERROR_LOG IF EXISTS;
    CREATE TABLE ERROR_LOG  (
            JOB_NAME CHAR(20) ,
            STEP_NAME CHAR(20) ,
            MESSAGE VARCHAR(300) NOT NULL
    ) ;
    
  6. from https://stackoverflow.com/questions/27139100/is-there-a-way-to-integrate-spring-batch-admin-and-spring-boot-properly by cc-by-sa and MIT license