手寫web服務(wù)器:定義@value注解,實現(xiàn)配置自動注入

前言
昨天我們定義了Configuration注解和Bean注解,實現(xiàn)了更靈活的類注入,今天我們來看另一個配置注入的注解Value,這個注解也是我們在springboot中經(jīng)常用到的,今天我們就來看下如何通過value注解實現(xiàn)properties配置的自動注入。
實現(xiàn)過程
定義properties工具類
這個工具類的作用主要是解析我們的配置文件,并生成一個配置文件的字典數(shù)據(jù),然后我們可以根據(jù)自己的需要獲取對應(yīng)的配置,這也是我們實現(xiàn)配置自動注入的第一步。
public class PropertiesUtil {
private static HashMap<String, PropertiesUtil> configMap = new HashMap();
private Date loadTime = null;
private ResourceBundle resourceBundle = null;
private static final Integer TIME_OUT = 60000;
private PropertiesUtil(String name) {
this.loadTime = new Date();
try {
this.resourceBundle = ResourceBundle.getBundle(name);
} catch (Exception var3) {
this.resourceBundle = null;
}
}
public static synchronized PropertiesUtil getInstance() {
return getInstance("application");
}
public static synchronized PropertiesUtil getInstance(String name) {
PropertiesUtil conf = configMap.get(name);
if (null == conf) {
conf = new PropertiesUtil(name);
configMap.put(name, conf);
}
if ((new Date()).getTime() - conf.getLoadTime().getTime() > (long)TIME_OUT) {
conf = new PropertiesUtil(name);
configMap.put(name, conf);
}
return conf;
}
public String get(String key) {
try {
String value = this.resourceBundle.getString(key);
return value;
} catch (MissingResourceException var3) {
return "";
} catch (NullPointerException var4) {
return "";
}
}
public Integer getInt(String key) {
try {
String value = this.resourceBundle.getString(key);
return Integer.parseInt(value);
} catch (MissingResourceException var3) {
return null;
} catch (NullPointerException var4) {
return null;
}
}
public boolean getBoolean(String key) {
try {
String value = this.resourceBundle.getString(key);
return "true".equals(value);
} catch (MissingResourceException var3) {
return false;
} catch (NullPointerException var4) {
return false;
}
}
public Date getLoadTime() {
return this.loadTime;
}
public static String getPropertiesValue(String name, String key) {
try {
return getInstance(name).get(key);
} catch (MissingResourceException var3) {
return "";
} catch (NullPointerException var4) {
return "";
}
}
}
定義value注解
依然是輕車熟路,這里的value()是用來接受我們的配置名稱的。
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Value {
String value();
}
實現(xiàn)配置注入
在之前的實現(xiàn)基礎(chǔ)上,我們增加了一個配置處理的類,這個類有兩個方法,分別是用于實現(xiàn)單個屬性注入和批量屬性注入:
public class ConfigurationHandler {
private static final PropertiesUtil propertiesUtil = PropertiesUtil.getInstance("application");
/**
* 初始化value配置信息
* @param instance
* @param field
* @throws IllegalAccessException
*/
public static void initValueConfig(Object instance, Field field) throws IllegalAccessException {
Annotation annotation = field.getAnnotation(Value.class);
if (Objects.nonNull(annotation)) {
String propertiesKeyName = ((Value) annotation).value();
Class<?> type = field.getType();
if (!field.isAccessible()) {
field.setAccessible(Boolean.TRUE);
}
if (Integer.class.equals(type)) {
field.setInt(instance, propertiesUtil.getInt(propertiesKeyName));
} else if (Boolean.class.equals(type)) {
field.setBoolean(instance, propertiesUtil.getBoolean(propertiesKeyName));
} else {
field.set(instance, propertiesUtil.get(propertiesKeyName));
}
}
}
/**
* 批量初始化value配置
* @param aClass
* @param instance
* @throws IllegalAccessException
*/
public static void batchInitValueConfig(Class aClass, Object instance) throws IllegalAccessException {
Field[] declaredFields = aClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
initValueConfig(instance, declaredField);
}
}
}
首先,我們在IoC實例化階段,對各種組件的字段進(jìn)行掃描,拿出有Value注解的屬性,根據(jù)注解的值獲取對應(yīng)的配置。
使用效果
我們在service組件上加入一個屬性,并加上value注解:

然后在我們的配置文件中增加對應(yīng)的配置:

運(yùn)行測試
運(yùn)行測試下:

可以看到,我們的配置已經(jīng)被注入進(jìn)來了,這樣注入配置,既簡單又方便。當(dāng)然,相比于Spring的Value注解,我們的還是顯得比較低級,因為spring的value注解是支持表達(dá)式的,它有一套專門的Spring EL,所以我們看的spring的value是這樣寫的:
@Value("${syske.boot.server.name}")
private String serverName;
// 或者這樣
@Value("#{syske.boot.server.name}")
private String serverName;
好了,今天的內(nèi)容就這么多,我們接下來總結(jié)一下。
總結(jié)
注解本質(zhì)上只是一種標(biāo)記,是為了便于我們通過反射操作類的屬性、方法等資源,實現(xiàn)我們的高級功能。value注解就是獲取配置的一種標(biāo)記,我們通過在實例化對象后對其字段操作,實現(xiàn)配置的自動注入。
在我實際測試的時候,我發(fā)現(xiàn)這種配置注入方式,對靜態(tài)變量也是有效的,但是spring的value對靜態(tài)變量是無效的,暫時沒有去看spring的源碼,不知道是實現(xiàn)方式的問題還是EL表達(dá)式的鍋。
原本我以為是因為字段和方法的反射操作都是基于類的實例,所以對于靜態(tài)方法和變量是沒有任何效果的,但是經(jīng)過實測的時候,發(fā)現(xiàn)并非如此,后面再好好研究下。
下面是項目的開源倉庫,有興趣的小伙伴可以去看看,如果有想法的小伙伴,我真心推薦你自己動個手,自己寫一下,真的感覺不錯:
https://github.com/Syske/syske-boot

