整合Spring Batch与Spring Integration时遇Bean创建异常求助
Let's break down your issue and fix it step by step, plus share a complete working example.
Root Cause of Your Error
The exception Target object of type [JobLaunchingGateway] has no eligible methods for handling Messages happens because you're trying to call a non-existent method toRequest on JobLaunchingGateway:
.handle(jobLaunchingGateway(),"toRequest")
The toRequest method belongs to your FileMessageToJobRequest transformer, which already converts the file message to a JobLaunchRequest. The JobLaunchingGateway only needs to handle this pre-converted request—no method name is required here.
Additional Issues to Fix
There are a few other critical problems in your code that would have caused failures even after fixing the above:
- Missing
JobRepositoryinJobLauncher: Spring Batch'sSimpleJobLauncherrequires aJobRepositoryto function, but you had it commented out. - Uninjected Batch Job: Your
FileMessageToJobRequesthad no reference to your actual Batch Job (you commented outsetJob(personJob())). - Unnecessary Payload Transformation: The
Transformers.fileToString()call converts theFilepayload to a String, which breaks theFileMessageToJobRequesttransformer that expects aMessage<File>. - Non-static Inner Class: Your
FileMessageToJobRequestis a non-static inner class, which Spring can't instantiate without an instance of the outerFilePollingIntegrationFlowclass.
Complete Fixed Code
Here's the fully corrected configuration with best practices applied:
@Configuration public class FilePollingIntegrationFlow { private final JobRepository jobRepository; private final Job personBatchJob; // Inject your actual Spring Batch Job here // Constructor injection (preferred over @Autowired for better testability) public FilePollingIntegrationFlow(JobRepository jobRepository, Job personBatchJob) { this.jobRepository = jobRepository; this.personBatchJob = personBatchJob; } @Bean public IntegrationFlow inboundFileIntegration( @Value("${inbound.file.poller.fixed.delay}") long pollDelay, @Value("${inbound.file.poller.max.messages.per.poll}") int maxMessagesPerPoll, TaskExecutor taskExecutor, MessageSource<File> fileReadingMessageSource) { return IntegrationFlows.from(fileReadingMessageSource, config -> config.poller(Pollers.fixedDelay(pollDelay) .taskExecutor(taskExecutor) .maxMessagesPerPoll(maxMessagesPerPoll))) .channel(ApplicationConfiguration.INBOUND_CHANNEL) .handle(fileMessageToJobRequest()) .handle(jobLaunchingGateway()) // No method name needed—handles JobLaunchRequest automatically .channel(MessageChannels.queue()) .get(); } @Bean public FileMessageToJobRequest fileMessageToJobRequest() { FileMessageToJobRequest transformer = new FileMessageToJobRequest(); transformer.setFileParameterName("input.file.name"); transformer.setJob(personBatchJob); // Assign your Batch Job return transformer; } @Bean public JobLaunchingGateway jobLaunchingGateway() { SimpleJobLauncher jobLauncher = new SimpleJobLauncher(); jobLauncher.setJobRepository(jobRepository); // Critical dependency for Batch jobLauncher.setTaskExecutor(new SyncTaskExecutor()); // Initialize the launcher (required for Spring Batch) try { jobLauncher.afterPropertiesSet(); } catch (Exception e) { throw new RuntimeException("Failed to initialize Job Launcher", e); } return new JobLaunchingGateway(jobLauncher); } // Static inner class so Spring can instantiate it independently public static class FileMessageToJobRequest { private Job job; private String fileParameterName; public void setFileParameterName(String fileParameterName) { this.fileParameterName = fileParameterName; } public void setJob(Job job) { this.job = job; } @Transformer public JobLaunchRequest toRequest(Message<File> fileMessage) { JobParametersBuilder paramsBuilder = new JobParametersBuilder(); // Add unique timestamp to ensure each file run is a distinct Job instance paramsBuilder.addString(fileParameterName, fileMessage.getPayload().getAbsolutePath()) .addLong("run.timestamp", System.currentTimeMillis()); return new JobLaunchRequest(job, paramsBuilder.toJobParameters()); } } }
Required Supporting Bean (File Reader)
You'll also need a MessageSource<File> bean to define the directory to poll:
@Bean public MessageSource<File> fileReadingMessageSource(@Value("${inbound.file.directory}") String inputDir) { FileReadingMessageSource fileSource = new FileReadingMessageSource(); fileSource.setDirectory(new File(inputDir)); // Filter to only process CSV files (adjust as needed) fileSource.setFilter(new SimpleFileListFilter<>(file -> file.getName().endsWith(".csv"))); // Prevent reprocessing the same file fileSource.setFilter(new AcceptOnceFileListFilter<>()); return fileSource; }
Key Fixes Explained
- Removed
fileToString()Transformation: Keeps the payload as aFileso the transformer can access its absolute path. - Fixed
JobLaunchingGatewayCall: Let the gateway handle the pre-convertedJobLaunchRequestdirectly. - Added
JobRepository: Critical for Spring Batch to manage job state. - Static Inner Class: Allows Spring to create the transformer bean without an outer class instance.
- Unique Job Parameters: Added a timestamp to avoid Spring Batch rejecting duplicate job runs with identical parameters.
内容的提问来源于stack exchange,提问作者Prerna

