Есть ли способ правильно интегрировать spring -batch-admin и spring -boot?
Согласно документации spring, пакетный администратор очень легко внедрить в существующее приложение. Простое копирование web.xml и index.jsp, а затем добавление необходимых зависимостей достаточно, чтобы заставить его работать.
Но если я хочу использовать его в существующем проекте загрузки spring, это ухудшается. Согласно этому примеру, конфигурация немного взломана, но она работает. UNTIL Я пытаюсь использовать аннотацию @EnableBatchProcessing
в моем configuriton 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$$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
Моя конфигурация довольно проста. У меня две конфигурации beans
@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 для создания заданий, шагов и других beans. Он работает хорошо, но я бы предпочел бы бесплатную конфигурацию xml. Есть ли способ легко интегрировать spring boot, spring batch и spring batch admin?
Ответы
Ответ 1
Короткий ответ заключается в том, что вы не захотите использовать @EnableBatchProcessing
с Spring Batch Admin. SBA предоставляет ряд beans в глобальном масштабе, который также предоставляет @EnableBatchProcessing
. SBA 2.0 (в настоящее время находится в разработке), вероятно, заполнит пробелы между тем, что в настоящее время существует и что @EnableBatchProcessing
обеспечивает (в частности, предоставление JobBuilderFactory
и StepBuilderFactory
).
Чтобы начать работать, вы должны иметь возможность (я сам не устал) настраивать в каталоге META-INF/spring/batch/override/
a JobBuilderFactory
и StepBuilderFactory
для глобального использования. Оттуда вы можете использовать XML файлы в каталоге META-INF/spring/batch/jobs
, которые не более чем сканирование компонентов для ваших классов @Configuration
. Однако оставьте @EnableBatchProcessing
из-за дублирования beans.
Для записи это не проблема с загрузкой Spring, так как @EnableBatchProcessing
является аннотацией Spring, а не загрузочной.
Ответ 2
Spring Batch Admin 2.0-BUILD-SNAPSHOT представляет новую Annoation @EnableBatchAdmin
для легкой интеграции с загрузкой spring.
Существует также образец проекта https://github.com/spring-projects/spring-batch-admin-samples.
Ответ 3
У меня рабочая версия здесь, основанная на том же примере (я разыграл оригинал): https://github.com/vesperaba/spring-batch-admin-spring-boot.
Я последовал совету Майкл Минелл, и я переписал владелец собственности SpringBatch с помощью специального.
Я также добавил работу, чтобы проверить, работает ли она сейчас
Ответ 4
чтобы завершить ответ, вот код для создания двух beans после отключения аннотации @EnableBatchProcessing
@Autowired
JobRepository jobRepository;
@Autowired
PlatformTransactionManager transactionManager;
@Bean
public JobBuilderFactory jobBuilderFactory() {
return new JobBuilderFactory(jobRepository);
}
@Bean
public StepBuilderFactory stepBuilderFactory() {
return new StepBuilderFactory(jobRepository, transactionManager);
}
Ответ 5
Это исключение ClassCastException вызвано
classpath:/org/springframework/batch/admin/web/resources/servlet-config.xml
Загрузка
META-INF/spring/batch/servlet/resources/resource-context.xml
который содержит
<mvc:annotation-driven />
Это конфликтует с конфигурацией mvc в классе конфигурации Spring Java. Следующий класс можно использовать для встраивания Spring Batch Admin в существующее приложение, использующее конфигурацию Java.
@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;
}
}
Для абстрактного базового меню также требуется один XML файл, который упоминается в другом месте проекта Spring Batch Admin. Это необходимо, поскольку абстрактный beans не может быть предоставлен из конфигурации Java Spring.
<?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. Позаботьтесь о том, чтобы только одна версия базового фрейма Spring была втянута 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 также ожидает в конфигурации по умолчанию для следующих файлов, которые существуют в корневом пути пути.
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
бизнес-план-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
) ;