手寫web服務器:定義Autorwired注解,實現屬性自動注入

前言
昨天,我們已經解決了post請求的阻塞問題,所以我們今天又可以繼續(xù)搞事情了,今天我們要實現的也是spring中很核心的注解——Autowired。這個注解想必大家肯定不陌生,在spring項目中,我們經常用它來為我們的屬性注入值,實現屬性的自動裝配。
好了,話不多說,我們來看具體如何實現.
實現過程
定義注解
這一塊就很簡單了,前面我們也不止一次寫過,這里target指定的是屬性
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
}
加到屬性上
直接把@Autowired注解加在我們要注入的屬性上

優(yōu)化掃描方法
屬性是在類初始化的時候,自動被賦值的,所以我們要調整初始化流程
private static void initRequestMappingMap() {
logger.info("start to scanRequestMapping, controllerSet = {}", classSet);
if (classSet == null) {
return;
}
classSet.forEach(aClass -> {
Annotation controller = aClass.getAnnotation(Controller.class);
if (Objects.isNull(controller)) {
return;
}
Method[] methods = aClass.getDeclaredMethods();
for (Method method : methods) {
RequestMapping annotation = method.getAnnotation(RequestMapping.class);
if (Objects.nonNull(annotation)) {
requestMappingMap.put(annotation.value(), method);
}
}
Field[] fields = aClass.getDeclaredFields();
try {
Object o = aClass.newInstance();
for (Field field : fields) {
Autowired annotation = field.getAnnotation(Autowired.class);
if (Objects.nonNull(annotation)) {
field.setAccessible(true);
field.set(o, contentMap.get(field.getType().getName()));
}
}
contentMap.put(aClass.getName(), o);
} catch (InstantiationException e) {
logger.error("初始controller失?。?, e);
} catch (IllegalAccessException e) {
logger.error("初始controller失?。?, e);
}
});
logger.info("scanRequestMapping end, requestMappingMap = {}", requestMappingMap);
}
我們在初始化這里加了一段字段初始化的代碼,上面是完整代碼,字段初始化只有短短幾行:
Field[] fields = aClass.getDeclaredFields();
try {
Object o = aClass.newInstance();
for (Field field : fields) {
Autowired annotation = field.getAnnotation(Autowired.class);
if (Objects.nonNull(annotation)) {
field.setAccessible(true);
field.set(o, contentMap.get(field.getType().getName()));
}
}
contentMap.put(aClass.getName(), o);
} catch (InstantiationException e) {
logger.error("初始controller失?。?, e);
} catch (IllegalAccessException e) {
logger.error("初始controller失?。?, e);
}
這里需要注意的是,因為屬性是私有的,必須通過getDeclaredFields獲取屬性值,getFields方法是沒辦法拿到私有屬性的;
另外一個需要注意的點是,私有屬性必須通過setAccessible設置為可訪問才可以,否則會報錯:

因為字段賦值是基于對象實例的,所以我們要先創(chuàng)建類的實例:
Object o = aClass.newInstance()
然后通過field.set給屬性賦值,這里賦值要通過IOC容器拿到賦值對象的實例,所以被賦值屬性的實例必須先初始化,否則會有問題。
同時,我們把帶有@Autowired注解的類的實例也存進了IOC容器,這樣在后面調用controller對應mapping方法的時候,我們直接從ioc容器中拿出來即可:
Object o = contentMap.get(declaringClass.getName());
Object invoke = method.invoke(o, parameters);

這是因為如果你在調用的時候再去創(chuàng)建實例,這時候屬性也要賦值,否則會報錯的,所以初始化的時候直接創(chuàng)建實例是比較合理的方式。
測試
瀏覽器調用下試下:

可以看到,我們調用的時候,service已經有值了,方法調用完成后,結果正常返回:

總結
好了,今天的內容到這里就結束了。在上面的內容中,我們展示了@Autowired注解的定義、具體的應用,以及Ioc對于Autowired注解的處理過程,最后我們經過測試,結果與預期一致,當然具體springboot是如何實現的,還需要進一步的研究和探討,我這里分享的是自己的實現思路,感興趣的小伙伴可以自己動手試下。
下面是項目的開源倉庫,有興趣的小伙伴可以去看看,如果有想法的小伙伴,我真心推薦你自己動個手,自己寫一下,真的感覺不錯:
https://github.com/Syske/syske-boot

