Spring 如何注入静态变量?

927 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 2 天,点击查看活动详情

为什么 Spring 不可以注入静态变量?

@Autowired 不能注入 static 成员属性

静态变量和静态方法属于类,而 Spring 容器中管理的是对象,使用 @Autowired 注入的是容器中的对象实例,静态变量和方法是不能直接使用 @Autowried 注入的。

Spring 不建议开发代码中为静态变量注入属性值,类加载时会优先加载静态变量,此时 Spring 的上下文环境还没有完成加载,因此不可以为静态变量绑定值。

另一方面,在 @Autowired 注解注入扫描 Class 类原数据时,会直接忽略类的使用 static 关键字标识的成员。

使用 @Value 读取配置文件时,配置文件内容会作为 Properties 对象的属性管理在容器中,使用注解加载到变量上时就是使用容器注入的机制将配置内容赋值给相应属性。

@Value 注解标注静态变量

如果 @Value 注解标注在 static 或 final 变量,并不能将属性值注入,最终得到的变量值是 null。

Spring 注入内容到静态变量中

@Value + set 方法注入静态变量

如果想要为 static 变量注入值,可以间接的操作方式,比如可以通过注解标注在 set 方法(非静态)上,使用 set 方法注入的方式将属性值赋值到静态变量中。

 @Component
 public class Customer {
     private static String name;
     @Value("${customer.name}")
     public void setName(String name) {
         Customer.name = name;
     }
 }

定义静态方法工具类时,则不建议在工具类中使用注入的内容,因为工具类定义的是静态方法,使用时容器并没有加载完成,容易出现 NPE 问题。

Properties 注入静态变量

Properties 作为 java.util 包中用来读取配置文件的类,其本身是一个 Hashtable,可以将 *.properties 配置文件内容以键值对的形式存储在对象中。

Properties 使用时可以通过 load() 方法自定义指定需要读取的配置文件,通过流的方式读取文件中内容,并通过 getProperty() 方法读取指定配置信息。

因为 Properties 是通过读取文件内容来获取配置文件,不依赖 Spring 容器的加载,只要指定读取配置文件后,就可以获取到对应的属性,可以在加载类时赋值到静态变量。

具体使用方法为

  • Properties 读取文件工具类
public class PropertiesUtil {
    private static Properties readProperties(String configFile) {
        Properties properties = new Properties();
        try {
           properties.load( new ClassPathResource(path).getInputStream());
        } catch (IOException e) {
            e.printStackTrace();
        }
        return properties;
    }
}
  • 使用 Properties 注入静态变量
@Component
 public class Customer {
     private static Properties properties = PropertiesUtil.readProperties("application.properties");
     private static String name = properties.getProperty("name");
 }

Properties 作为 Java 工具包中读取配置文件的类,可以读取配置并赋值给静态变量,但是 Properties 仅可以读取 .properties 配置文件,而不可以解析 .yml 配置文件。

详细的 Properties 使用方法可见:SpringBoot 使用 Properties 读取配置文件

Environment 注入静态变量

Spring 中有一个类 Environment,它可以被认为是当前应用程序正在运行的环境,继承了 PropertyResolver 接口,因此可以作为一个属性解析器使用。

Environment 可以从容器中注入,并使用对象的 getProperty() 方法来获取属性值。

只要在类加载静态属性时获取到初始化的 Environment 对象信息,就可以为静态变量赋值。

可以通过 @Autowired + set 方法将 Environment 对象注入到静态变量中,然后使用静态变量获取配置项即可。

@Component
public class Customer {
    private static Environment env;
    @Autowired
    public void setEnvironment(Environment environment) {
        env = environment;
    }
    
    private static String name =  env.getProperty("name");
}

Environment 可以读取 .properties 配置文件和 .yml 配置文件,是更好的一种选择。

综述

综上所述,有如下结论:

  1. 在 Spring 项目中不建议将内容注入到静态变量中,因此稍有疏忽则会出现 NPE 问题

  2. 如果使用 @Component、@Value、@Autowired 等注解委婉的实现了注入到静态变量中,但其终究还是在 Spring 容器加载之后进行加载赋值的,实际使用仍依赖 Spring 容器环境

  3. 如果是单纯的 Java 静态工具类,则应该使用 Properties 类加载文件的方式,这样才可以完全不依赖 Spring 环境的运行。