运行时加载resources目录文本文件遇路径解析及ResourceLoader注入问题
Got it, let's tackle this problem step by step. I've run into similar issues before when trying to load classpath resources in Spring event listeners, so here's what you need to do:
First, Ensure Your Listener Class is a Spring Bean
The most common reason ResourceLoader injection fails is that the class containing your @EventListener method isn't managed by Spring. Add a stereotype annotation like @Component, @Service, or @Configuration to the class:
@Component // Tells Spring to manage this class as a bean public class GeolocationLoader { // Your code will go here }
Option 1: Use Constructor Injection for ResourceLoader
Constructor injection is the recommended approach (Spring 4.3+ even auto-wires single-constructor beans without needing @Autowired). Here's how to implement it:
@Component public class GeolocationLoader { private final ResourceLoader resourceLoader; // Spring will automatically inject ResourceLoader here public GeolocationLoader(ResourceLoader resourceLoader) { this.resourceLoader = resourceLoader; } @EventListener public void loadGeolocation(ServerStartupEvent event) { try { // Use the "classpath:" prefix to target your CSV file in the classpath Resource resource = resourceLoader.getResource("classpath:locker_locations.csv"); // Critical note: If your app is packaged as a JAR, resource.getFile() will fail (JAR resources aren't physical files) // Use getInputStream() instead for cross-environment compatibility try (InputStream inputStream = resource.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) { // Process your CSV content line by line String line; while ((line = reader.readLine()) != null) { // Add your CSV parsing logic here } } } catch (IOException e) { // Properly handle exceptions (log them, throw a startup exception, etc.) e.printStackTrace(); } } }
Option 2: Inject the Resource Directly with @Value
If you don't need the full ResourceLoader, you can inject the Resource directly using @Value for a simpler setup:
@Component public class GeolocationLoader { // Inject the CSV resource directly using the classpath prefix @Value("classpath:locker_locations.csv") private Resource lockerLocationsResource; @EventListener public void loadGeolocation(ServerStartupEvent event) { try (InputStream inputStream = lockerLocationsResource.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) { // Process your CSV content here String line; while ((line = reader.readLine()) != null) { // Add your CSV handling logic here } } catch (IOException e) { // Handle exceptions appropriately (don't just print stack traces in production!) e.printStackTrace(); } } }
Key Checks to Avoid Headaches
- Path Accuracy: The
classpath:locker_locations.csvpath works because your file lives insrc/main/resources(which becomesbuild/resources/mainafter compilation, part of the classpath root). - JAR Compatibility: Always use
getInputStream()instead ofgetFile()if you plan to package your app as a JAR —getFile()only works for physical files on the filesystem. - Build Tool Configuration: Make sure your build tool (Maven/Gradle) is copying the CSV file to the classpath. Both tools do this by default for files in
src/main/resources, but double-check if you've modified resource settings.
内容的提问来源于stack exchange,提问作者Andrew S.

