Spring boot中引數注入,@Value失效以及解決方案
問題
專案中我們都要要儘量避免將引數直接寫程序序裡,這樣一旦需要需要修改配置,我們可以只需要在配置檔案裡做修改,而不必在程式裡找,這樣可以避免很多錯誤,個人專案可能不會注意這一點,但是需要上線釋出的專案,Configure配置檔案就顯得非常重要!現在很多公司其實都有這方面的應用,甚至有專門的中介軟體可以專門管理配置檔案,即時生效,不必去上線修改引數,這不是我們今天說的重點。先看內容:
application-dev.properties
#URL
confirmURL=http://127.0.0.1:8081/confirm
然後在Controller類裡面通過@Value將引數注入進來,最後的確成功了。因此基於此經驗,我便在其他使用的類裡面也採用這樣的方式注入引數,但是發現去失效了,報錯為NULL,說明引數並沒有我們料想的被注入進來。
原因
這是為什麼呢?為什麼在Controller類就成功了?在其他類裡面我嘗試過@Service,@Component,@Configure,但是我沒有成功,經過查詢,原來,在使用這些引數生成Bean類的時候,我們注入的引數還沒有生效,因此獲取不到,而不是由於引數注入的問題,而在某些場景,spring可能做了優化,是的引數優先注入,再生成Bean。那麼有沒有好的方法可以解決這個問題呢?
方案
首先,我們的引數的直接注入是肯定不行了,那麼我們就採用初始化類的方式,將配置資訊集中初始化。
public class PropertyUtil {
private static final Logger logger = LoggerFactory.getLogger(PropertyUtil.class);
private static Properties props;
static {
loadProps();
}
synchronized static private void loadProps() {
logger.info("start to load properties.......");
props = new Properties();
InputStream in = null;
try {
in = PropertyUtil.class.getClassLoader().
getResourceAsStream("application.properties" );
props.load(in);
logger.info(name);
} catch (FileNotFoundException e) {
logger.error("properties not found!");
} catch (IOException e) {
logger.error("IOException");
} finally {
try {
if (null != in) {
in.close();
}
} catch (IOException e) {
logger.error("properties close Exception!");
}
}
// logger.info(props);
logger.info("load properties over...........");
}
public static String getProperty(String key) {
if (null == props) {
loadProps();
}
return props.getProperty(key);
}
}
通過Util類我們一次行載入引數,在需要獲取的地方,直接通過
private static String
unsubscribeUrl = PropertyUtil.getProperty("unsubscribeUrl");
這樣就可以獲取我們所需要的引數了,直接定義為靜態變數,一次讀取,後面都可以直接使用。
引申
在Spring boot中引數配置有三個 application.properties, application-dev.properties, application-prod.properties
application.properties
spring.profiles.active=dev
通過在這裡修改dev 或者 prod 我可以配置兩套配置,一套用於產品,一套是開發,那麼我如果來根據我配置的資訊來讀取不同的配置呢,這又讓我頭疼了,看著程式碼,我想到,既然他可以讀取配置檔案了,那麼我為何不先獲取一下 application.properties裡面的資訊,然後看dev,或者prod來載入不同的配置呢,因此,我加了一段判斷程式碼:
in = PropertyUtil.class.getClassLoader().getResourceAsStream("application.properties");
props.load(in);
String name = getProperty("spring.profiles.active");
if (name.equals("dev")) {
in = PropertyUtil.class.getClassLoader().getResourceAsStream("application-dev.properties");
} else if (name.equals("prod")) {
in = PropertyUtil.class.getClassLoader().getResourceAsStream("application-prod.properties");
}
props.load(in);
logger.info(name);
通過測試,name的確是我 在application.properties配置的資訊,因此,這具很好的解決了兩套配置,線上下測試跟釋出,我只需要修改一個配置檔案就可以完成轉換,節省時間的同時,也避免了錯誤發生,很多上線的問題都是因為配置的問題,因此這個問題一定要小心,當然還有很多方案,後面接觸到我會繼續總結,最近在程式碼的重構,發現自己的程式碼有很多問題。
@Component
public class ExchangeServiceUtil {
private static String mailServer;
private static String user;
private static String password;
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private ExchangeServiceUtil(@Value("${spring.EWS.mailServer}") String mailServer,
@Value("${spring.EWS.user}") String user,
@Value("${spring.EWS.password}") String password) {
this.mailServer = mailServer;
this.user = user;
this.password = password;
}
private static ExchangeServiceUtil instance = new ExchangeServiceUtil(mailServer, user, password);
public static ExchangeServiceUtil getExchangeServiceUtil() {
return instance;
}
public ExchangeService getExchangeService() throws URISyntaxException {
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2010_SP2);
ExchangeCredentials credentials = new WebCredentials(user, password);
service.setCredentials(credentials);
service.setUrl(new URI(mailServer));
// service.autodiscoverUrl("<your_email_address>");
return service;
}
}
我通過建構函式的方式,也成功的把引數注入到了裡面,這個方法也是偶然接觸到的,在某些工具,比如資料池,這是微軟的郵件傳送配置,都是可以採用這樣的方法,建構函式可以載入引數,然後在生成Bean,很好的避開了那個問題,這個問題我還會繼續深究,看看有什麼發現,如果你有高見,歡迎留言!