使用@Value為靜態(tài)變量導(dǎo)入并使用導(dǎo)入的靜態(tài)變量進(jìn)行初始化方式
1 問(wèn)題描述
在南京出差時(shí),在開(kāi)始開(kāi)發(fā),自己把一些相對(duì)緊密聯(lián)系的不變得配置放進(jìn)一個(gè)類中,這些字段為static的,待交付時(shí),由于這些配置也要是可以通過(guò)配置文件進(jìn)行配置的,因此無(wú)形之中就引入了一個(gè)問(wèn)題。
即使用@Value對(duì)靜態(tài)變量進(jìn)行導(dǎo)入的問(wèn)題。并且還有一種更加復(fù)雜的情形,即需要在生成相關(guān)的Bean時(shí),需要進(jìn)行一些資源的初始化,在當(dāng)時(shí)自己結(jié)結(jié)實(shí)實(shí)的踩了一把坑。
在項(xiàng)目開(kāi)始時(shí)TomcatConfig類是如下的:
public class TomcatConfig {
public static String ip = "192.168.1.112";
public static int port=8080;
public static String username=admin;
public static String password=admin;
}
但在交付之前,要把這些配置值放入配置文件,例如application.properties
tomcat.ip=10.30.102.111 tomcat.port=8080 tomcat.username=admin tomcat.password=admin
并且要在該類再開(kāi)始時(shí)通過(guò)HTTP調(diào)用,使用這些參數(shù)進(jìn)行一次初始化。
2 問(wèn)題結(jié)構(gòu)
2.1 服務(wù)方
可以理解自己開(kāi)發(fā)的模塊依賴于其他的模塊,模塊之間通過(guò)HTTP通信獲取數(shù)據(jù)和狀態(tài)??梢酝ㄟ^(guò)Spring Boot起一個(gè)程序提供一個(gè)對(duì)外的接口,即相當(dāng)于提供服務(wù)。
這就是服務(wù)提供的類。
該程序所在的服務(wù)器ip和端口即上邊已經(jīng)寫(xiě)進(jìn)配置文件的ip和端口。
package com.wisely.ch6_2_3.controller;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.wisely.ch6_2_3.config.TomcatSetting;
import lombok.extern.java.Log;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.UUID;
/**
* 測(cè)試注入
*
* @Owner:
* @Time: 2019/3/31-16:29
*/
@RestController
@Log
public class TestValue {
@Autowired
private TomcatSetting serverConfig;
@RequestMapping(value = "/getImg",produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public String getImgeLoc() {
log.info("Enter getImgeLoc");
System.out.println("-- Handling --");
String fileName = UUID.randomUUID().toString()+".jpg";
JSONObject result = new JSONObject();
result.put("imgUrl", serverConfig.getUrl()+"/"+fileName);
System.out.println("-- over --");
return result.toJSONString();
}
@RequestMapping("/getmapping")
public String service(@RequestParam("username") String username, @RequestParam("password") String password) {
if (!username.equals("admin") ||!password.equals("admin")) {
JSONObject res = new JSONObject();
res.put("state", -1);
res.put("msg", "fail");
return res.toJSONString();
}
JSONObject result = new JSONObject();
result.put("state", 0);
result.put("msg", "success");
JSONObject data = new JSONObject();
data.put("aaa", "bbb");
data.put("ccc", "ddd");
result.put("data", data);
return result.toJSONString();
}
}
2.2 客戶方
由于之前寫(xiě)的代碼是硬編碼,肯定要重構(gòu)這部分的代碼,@Value可以靈活的實(shí)現(xiàn)注入,但通過(guò)在互聯(lián)網(wǎng)上查詢相關(guān)的頁(yè)面可以得到如下的知識(shí):
- SpringBoot中使用@Value()只能給普通變量注入值,不能直接給靜態(tài)變量賦值,并且@Value使用設(shè)值函數(shù)為成員注入值。
- 為了給靜態(tài)變量注入值,需要使用特殊的語(yǔ)法
2.2.1核心類 TomcatConfig
package com.example.staticvalue.config;
import com.alibaba.fastjson.JSONObject;
import com.example.staticvalue.HttpClientUtil.HttpClientUtil;
import com.fasterxml.jackson.databind.util.JSONPObject;
import lombok.Getter;
import lombok.extern.java.Log;
import org.apache.http.protocol.HTTP;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* tomcat配置
*
* @Owner:
* @Time: 2019/3/31-19:25
*/
@Component
@Log
public class TomcatConfig {
@Getter
private static String ip;
@Getter
private static int port;
@Getter
private static String username;
@Getter
private static String password;
private static JSONObject data = new JSONObject();
# 在靜態(tài)初始化塊中調(diào)用set()是無(wú)效的,因?yàn)榇藭r(shí)ip等變量尚未注入。應(yīng)該是在該Bean已經(jīng)生成了,即ip,port,usernam,password已經(jīng)注入之后再調(diào)用set()這樣才有效。
static {
log.info("靜態(tài)化塊");
log.info("TomcatConfig.ip: "+ TomcatConfig.ip);
~~~~try {
~~set();~~
~~} catch (Exception e) {~~
~~e.printStackTrace();~~
}~~~~
}
@Value("${tomcat.ip}")
public void setIp(String ip) {
TomcatConfig.ip = ip;
}
@Value("${tomcat.password}")
public void setPassword(String password) {
TomcatConfig.password = password;
}
@Value("${tomcat.port}")
public void setPort(int port) {
TomcatConfig.port = port;
}
@Value("${tomcat.username}")
public void setUsername(String username) {
TomcatConfig.username = username;
}
// 注意該函數(shù)應(yīng)在類外進(jìn)行調(diào)用,比如set()可以放置在其他標(biāo)注了雷瑟@Componet的Bean類中,這樣可以保證在TomcatConfig的bean已經(jīng)生成。
public static void set() throws Exception {
log.info("Enter set");
log.info("TomcatConfig.ip: "+ TomcatConfig.ip);
String accessUrl = "http://"+getIp()+":"+getPort()+"/getmapping?";
accessUrl += "username="+username+"&"+"password="+password;
log.info("accessUrl = "+accessUrl);
String result = HttpClientUtil.postJson(accessUrl, "{}");
JSONObject ret = JSONObject.parseObject(result);
if (ret.getIntValue("state") == 0) {
data = ret.getJSONObject("data");
}
}
}
注意:set()方法完成了類似初始化的工作,即通過(guò)調(diào)用一次http請(qǐng)求,填充了static成員data的值。
set()的調(diào)用不可以放在本類的靜態(tài)環(huán)境下,尤其在war包運(yùn)行在tomcat之下更是這樣,在jar包中set()函數(shù)的調(diào)用放在靜態(tài)函數(shù)main之中是有效的。
這可以通過(guò)日志進(jìn)行證明,如果是war包跑在tomcat環(huán)境下,若為如下代碼:
public static void main(String[] args) throws Exception{
log.info("Enter main");
SpringApplication.run(StaticvalueApplication.class, args);
log.info("after run");
TomcatConfig.set();
log.info("after set");
}
程序會(huì)僅執(zhí)行到after run日志打印,不會(huì)執(zhí)行TomcatConfig.set()的調(diào)用,切記,切記
如果真的是在war包跑在tomcat下,可以使用如下的方式解決,可以把TomcatConfig.set()的調(diào)用放在其他Bean類的靜態(tài)初始化塊中。
2.2.2 項(xiàng)目結(jié)構(gòu)

2.2.3輔助類HttpClientUtil
package com.example.staticvalue.HttpClientUtil;
import org.apache.http.HttpEntity;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.CharArrayBuffer;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
/**
* 輔助類,使用http獲取數(shù)據(jù)
*
* @Owner:
* @Time: 2019/3/31-19:49
*/
public class HttpClientUtil {
public static String postJson(String url,String jsonString) throws Exception
{
String result = null;
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpPost post = new HttpPost(url);
CloseableHttpResponse response = null;
try {
post.setEntity(new ByteArrayEntity(jsonString.getBytes("UTF-8")));
post.setHeader("Content-Type","application/json" );
response = httpClient.execute(post);
if(response != null && response.getStatusLine().getStatusCode() == 200)
{
HttpEntity entity = response.getEntity();
result = entityToString(entity);
}
return result;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
httpClient.close();
if(response != null)
{
response.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
private static String entityToString(HttpEntity entity) throws IOException {
String result = null;
if(entity != null)
{
long lenth = entity.getContentLength();
if(lenth != -1 && lenth < 2048)
{
result = EntityUtils.toString(entity,"UTF-8");
}else {
InputStreamReader reader1 = new InputStreamReader(entity.getContent(), "UTF-8");
CharArrayBuffer buffer = new CharArrayBuffer(2048);
char[] tmp = new char[1024];
int l;
while((l = reader1.read(tmp)) != -1) {
buffer.append(tmp, 0, l);
}
result = buffer.toString();
}
}
return result;
}
}
2.2.4 啟動(dòng)類
package com.example.staticvalue;
import com.example.staticvalue.config.TomcatConfig;
import lombok.extern.java.Log;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@Log
public class StaticvalueApplication {
public static void main(String[] args) throws Exception{
log.info("Enter main");
SpringApplication.run(StaticvalueApplication.class, args);
log.info("after run");
//在Jar包中運(yùn)行時(shí),下句是可以生效的,但在war包運(yùn)行在tomcat中時(shí),TomcatConfig。set()以及之后的語(yǔ)句不會(huì)執(zhí)行。把Tomcat.set()放在某個(gè)Bean的靜態(tài)初始化塊中即可
TomcatConfig.set();
log.info("after set");
}
}
2.2.5 TaskService
package com.example.staticvalue.service;
import com.example.staticvalue.config.TomcatConfig;
import lombok.extern.java.Log;
import org.springframework.stereotype.Component;
/**
* 任務(wù)類
*
* @Owner:
* @Time: 2019/3/31-19:39
*/
@Component
@Log
public class TaskService {
static {
try {
log.info("TaskService: before set");
TomcatConfig.set();
log.info("TaskService: after set");
} catch (Exception e) {
e.printStackTrace();
}
}
}
3總結(jié)
上述注入靜態(tài)變量的主要原因是靜態(tài)變量因?yàn)橐恍┻z留的原因,為了解決硬編碼的問(wèn)題而引入的,至于在靜態(tài)變量通過(guò)@Value注入時(shí)同時(shí)在該類中進(jìn)行初始化,則是非常不優(yōu)雅的一種編程風(fēng)格,這種初始化的方式更加建議放置在類似開(kāi)機(jī)自啟動(dòng)的應(yīng)用程序中,在Spring Boot中也確實(shí)有這樣的實(shí)現(xiàn)場(chǎng)景。
即CommandLineRunner接口,實(shí)現(xiàn)該接口的類必須實(shí)現(xiàn)run()方法,而該方法會(huì)在所有Bean均已正確生成之后開(kāi)始執(zhí)行。
關(guān)于CommandLineRunner,可以參考CommandLineRunner或者ApplicationRunner接口,至于這種用法,改天再來(lái)寫(xiě)一篇闡述的文章吧。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Slf4j+logback實(shí)現(xiàn)JSON格式日志輸出方式
這篇文章主要介紹了Slf4j+logback實(shí)現(xiàn)JSON格式日志輸出方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12
cmd中javac命令無(wú)法運(yùn)行(java指令能運(yùn)行)解決步驟
這篇文章主要介紹了在安裝JDK后,執(zhí)行javac命令沒(méi)有返回值的問(wèn)題,可能是由于命令提示符窗口緩存問(wèn)題、系統(tǒng)路徑優(yōu)先級(jí)問(wèn)題、文件權(quán)限問(wèn)題或命令行輸入問(wèn)題,文中通過(guò)代碼將解決的步驟介紹的非常詳細(xì),需要的朋友可以參考下2025-02-02
基于Java并發(fā)容器ConcurrentHashMap#put方法解析
下面小編就為大家?guī)?lái)一篇基于Java并發(fā)容器ConcurrentHashMap#put方法解析。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-06-06
Java連接合并2個(gè)數(shù)組(Array)的5種方法例子
最近在寫(xiě)代碼時(shí)遇到了需要合并兩個(gè)數(shù)組的需求,突然發(fā)現(xiàn)以前沒(méi)用過(guò),于是研究了一下合并數(shù)組的方式,這篇文章主要給大家介紹了關(guān)于Java連接合并2個(gè)數(shù)組(Array)的5種方法,需要的朋友可以參考下2023-12-12

