引入
寫這篇博文,要從java.lang.ExceptionInInitializerError
這個(gè)報(bào)錯(cuò)開始。簡(jiǎn)單的看上去,這是一個(gè)類初始化異常報(bào)錯(cuò)撤蟆。但事實(shí)上并不是這樣,這是由于調(diào)用某個(gè)static
變量屬性時(shí)而該屬性沒有初始化而導(dǎo)致的錯(cuò)誤堂污,所以家肯,在debug
模式下,你第二次再嘗試調(diào)用操作static
屬性的方法時(shí)盟猖,可能就會(huì)拋出一個(gè)空指針異常了讨衣。據(jù)聽請(qǐng)看這段代碼。
Domain.java
public class Domain {
private static Domain domain = new Domain();
private static Map<String,String> domainMap = new HashMap<String
,String>();
private Domain() {
domainMap.put("isTrue","true");
}
public static Domain getInstance() {
return domain;
}
}
TestDomain.java
public class TestDomain {
public static void main(String[] args) {
String domainString = Domain.getInstance().toString();
System.out.println(domainString);
}
}
在執(zhí)行調(diào)用的時(shí)候就會(huì)報(bào)錯(cuò)了式镐!那原因是什么呢反镇?
原因還是很簡(jiǎn)單的,在調(diào)用Domain靜態(tài)方法時(shí)娘汞,由于兩個(gè)變量都是靜態(tài)變量歹茶,不會(huì)對(duì)它進(jìn)行賦值,但會(huì)對(duì)變量按順序進(jìn)行初始化你弦。所以先給domain初始化辆亏,這個(gè)時(shí)候調(diào)用私有構(gòu)造函數(shù),函數(shù)內(nèi)已經(jīng)用到了domainMap,而這個(gè)變量還沒有初始化鳖目,進(jìn)而拋出了沒有初始化類的異常扮叨。
想要解決這個(gè)異常,兩種方案领迈。
- 把domain和domainMap的變量聲明位置調(diào)換彻磁,這樣在調(diào)用私有構(gòu)造函數(shù)時(shí)domainMap已經(jīng)初始化了。
- 將domain的構(gòu)造函數(shù)公有化狸捅,并使外部調(diào)用來初始化衷蜓,而不是聲明時(shí)即初始化。當(dāng)然這樣并不符合工具類的使用方便尘喝,會(huì)造成浪費(fèi)內(nèi)存等后果磁浇。
漸進(jìn)
之所以舉上面這個(gè)例子,是因?yàn)樗臉?gòu)造很像工具類朽褪。
首先你得明白什么是工具類置吓,簡(jiǎn)單來說它是一種可以不用初始化對(duì)象而直接調(diào)用其靜態(tài)方法而達(dá)到某一些功能的以Utils或Helper結(jié)尾的類。說到這里缔赠,要聲明一下衍锚,并不是所有的靜態(tài)類都是推薦使用的,使用靜態(tài)類的過程往往只是為了閱讀方便和調(diào)用簡(jiǎn)單嗤堰,但卻不知會(huì)增加程序的耦合度戴质,破壞設(shè)計(jì)模式等缺點(diǎn)。這里貼一篇文章寫的很不錯(cuò):如何擺脫工具類。
而我們最終的問題是告匠,如何在工具類中初始化賦值靜態(tài)屬性呢戈抄。項(xiàng)目環(huán)境是在spring框架下,舉個(gè)例子:
XXXProperties.java
public class XXXProperties {
private String staticParam1;
private String staticParam2;
public String getStaticParam1() {
return staticParam1;
}
public void setStaticParam1(String staticParam1) {
this.staticParam1 = staticParam1;
}
public String getStaticParam2() {
return staticParam2;
}
public void setStaticParam2(String staticParam2) {
this.staticParam2 = staticParam2;
}
}
XXUtils.java
public class XXUtils {
@Autowired //靜態(tài)變量通過spring并不能直接注入后专,所以這樣是會(huì)報(bào)錯(cuò)的
private static XXXProperties xxxProperties;
private static String do() {
String xxxPropertiesSize = xxxProperties.getSize();
return xxxPropertiesSize;
}
}
當(dāng)外部想要調(diào)用XXUtils
的do()
時(shí)划鸽,發(fā)現(xiàn)xxxProperties并沒有注入值,而我們?nèi)フ{(diào)用時(shí)行贪,程序也自然會(huì)拋出文章開始說的異常漾稀。
怎么去注入呢?
方案
- 采用間接注入方式
//可以換成@Configuration,與@Inject配合使用
@Component
public class XXUtils {
//可以換成@Inject
@Resource
private XXXProperties xxxPropertiesAutowired;
private static XXXProperties xxxProperties;
@PostConstruct
public void init() {
this.xxxProperties = xxxPropertiesAutowired;
}
}
也可以使用在Spring Boot中使用 @ConfigurationProperties 注解 給出的注解方式建瘫,是針對(duì)spring-boot的崭捍。
- 將靜態(tài)參數(shù)用beans提取,以鍵值對(duì)形式存儲(chǔ)啰脚,在工具類中直接調(diào)用
其中
public class org.springframework.beans.factory.config.PropertyPlaceholderConfigurer extends org.springframework.beans.factory.config.PlaceholderConfigurerSupport
- 如果你不想提取bean,也可以直接讀取file.
public class XMLConfig {
private static final Logger logger = Logger.getLogger(XMLConfig.class);
public static final String FILEPATH = "xmlconf.properties";
public static long fileLastModified = 0;
//屬性文件xmlconf.properties中對(duì)應(yīng)的各個(gè)鍵值對(duì)
public static HashMap<String, String> paramsMap = new HashMap<String, String>();
public static HashMap<String, String> loadProperties(String file)
{
HashMap<String, String> map = new HashMap<String, String>();
InputStream in = null;
Properties p = new Properties();
try
{
in = new BufferedInputStream(new FileInputStream(file));
p.load(in);
}
catch (FileNotFoundException e)
{
logger.error(file + " is not exists!");
}
catch (IOException e)
{
logger.error("IOException when load " + file);
}
finally
{
if (in != null)
{
try
{
in.close();
}
catch (IOException e)
{
logger.error("Close IO error!");
}
}
}
Set<Entry<Object, Object>> set = p.entrySet();
Iterator<Entry<Object, Object>> it = set.iterator();
while (it.hasNext())
{
Entry<Object, Object> entry = it.next();
String key = (String) entry.getKey();
String value = (String) entry.getValue();
logger.debug(key + "=" + value);
// System.out.println(key + "=" + value);
if (key != null && value != null)
{
map.put(key.trim(), value.trim());
}
}
return map;
}
}
- 待你完善
期待有更好的方法一起交流討論殷蛇。