Spring Boot应用Properties读取失败及NPE问题求助
Let's break down your two configuration loading issues one by one and fix them step by step.
1. Issue with java.util.Properties reading configuration
Root Causes
- Swallowed IOException: Your
getHost()method catchesIOExceptionbut does nothing with it. If there's any error loading the properties file (like file not found or read error), you'll never know, andpropertiesmight remain unloaded—returningnullforspring.mysql.host, which triggers theUnknownHostException: null. - JAR compatibility issue:
ResourceUtils.getFile("classpath:application.properties")works in IDEs but fails when packaged into a JAR. Resources inside JARs aren't physical files, soFileInputStreamcan't access them. - Inefficient reload: You load the properties file every time
getHost()is called, which is unnecessary and slows down your application.
Fix
Rewrite your PropertyReader to load resources correctly, handle exceptions properly, and load properties only once:
public class PropertyReader { private static Properties properties; // Load properties once when the class is initialized static { properties = new Properties(); try (InputStream in = PropertyReader.class.getClassLoader().getResourceAsStream("application.properties")) { if (in == null) { throw new IOException("application.properties not found in classpath"); } properties.load(in); } catch (IOException e) { // Don't swallow exceptions - log them or throw a runtime exception to catch loading failures throw new RuntimeException("Failed to load application.properties", e); } } private String host; public String getHost() { // Return the preloaded value directly return properties.getProperty("spring.mysql.host"); } public void setHost(String host) { this.host = host; } }
Key improvements:
- Uses
ClassLoader.getResourceAsStream()to read classpath resources (works in both IDEs and JARs). - Uses try-with-resources to auto-close the input stream, preventing resource leaks.
- Loads properties once in a static block instead of reloading on every method call.
- Properly surfaces exceptions so you can debug loading failures immediately.
2. Issue with @Autowired Environment causing NullPointerException
Root Cause
You're accessing the env variable in the constructor, but Spring injects autowired dependencies after the constructor runs. When the constructor executes, env is still null, hence the NullPointerException.
Fixes
You have a few straightforward options to resolve this:
Option 1: Constructor Injection (Recommended)
Use constructor injection instead of field injection. Spring will pass the Environment directly to the constructor, so it's available when you need it:
@Component // Remove @Configuration unless this class defines @Bean methods for Spring to manage public class UnitDBHelper { private Environment env; private PoolProperties poolProperties; // Spring 4.3+ doesn't require @Autowired here if there's only one constructor public UnitDBHelper(Environment env) { this.env = env; String host = env.getProperty("spring.mysql.host"); this.poolProperties = new PoolProperties(); poolProperties.setUrl("jdbc:mysql://" + host + "/ci"); } }
Option 2: Use @PostConstruct
If you prefer field injection, move initialization logic to a method annotated with @PostConstruct—this method runs after all dependencies are injected:
@Component public class UnitDBHelper { @Autowired private Environment env; private PoolProperties poolProperties; public UnitDBHelper() { // Constructor should only initialize basic fields, not use autowired dependencies } @PostConstruct public void init() { String host = env.getProperty("spring.mysql.host"); this.poolProperties = new PoolProperties(); poolProperties.setUrl("jdbc:mysql://" + host + "/ci"); } }
Option 3: Use @Value (Simpler for single properties)
If you only need the spring.mysql.host property, inject it directly with @Value instead of using Environment:
@Component public class UnitDBHelper { private PoolProperties poolProperties; public UnitDBHelper(@Value("${spring.mysql.host}") String host) { this.poolProperties = new PoolProperties(); poolProperties.setUrl("jdbc:mysql://" + host + "/ci"); } }
Additional Notes
- Remove
@ConfigurationfromUnitDBHelperunless you're defining Spring-managed beans (likeDataSource) inside it with@Beanannotations. For a helper class,@Componentis sufficient. - Double-check that
application.propertiesis correctly placed insrc/main/resourcesso it's included in the classpath.
内容的提问来源于stack exchange,提问作者user2315104

