搭建輕量級Java Web框架
MVC(Model-View-Controller,模型-視圖-控制器)是一種常用的設計模式,可以使用這個模式將應用程序進行解耦。
IOC
通過Controller注解來定義Controller類,在該類中,可通過Inject注解定義一系列Service成員變量,這就是"依賴注入"。此外,有一系列被Action注解所定義的方法(簡稱Action方法),在這些Action方法中,調用了Service成員變量的方法來完成具體的業務邏輯。若返回View對象,則表示JSP頁面;若返回Data對象,則表示一個JSON數據。
我們需要開發一個"類加載器"來加載該基礎包名下的所有類,比如使用了某注解的類,或者實現了某接口的類,再或者繼承了某父類的所有子類等。
獲取類加載器實現起來最為簡單,只需獲取當前線程中的ClassLoader即可。
我們的目標是在控制器類上使用Controller注解,在控制器類的方法上使用Action注解,在服務類上使用Service注解,在控制器類中可使用Inject注解將服務類依賴注入進來。因此需要自定義4個注解類。
可以將帶有Controller注解與Service注解的類所產生的對象,理解為由Smart框架所管理的Bean。
我們需要獲取所有被Smart框架管理的Bean類,根據類來實例化對象,最后將每次創建的對象存放在一個靜態的Map<Class<?>,Object>中,我們需要隨時獲取該Map,還需要通過該Map的key(類名)去獲取所對應的value(Bean對象)。該Map<Class<?>,Object>相當於一個"Bean容器",在Bean Map中存放了Bean類與Bean實例的映射關系,我們只需要通過getBean方法,傳入一個Bean類,就能獲取Bean實例。
我們再Cotroller中定義Service成員變量,然后在Controller的Action方法中調用Service成員變量的方法。那么,如何實例化Service成員變量呢?
還記得之前定義的Inject注解嗎?我們就用它來實現Service實例化。那么,誰來實例化呢?
不是開發者自己通過new的方式來實例化,而是通過框架自身來實例化,像這類實例化過程,稱為IoC(Inversion of Control,控制反轉)。控制不是由開發者來決定的,而是反轉給框架了。一般地,我們也將控制反轉稱為DI(Dependency Injection,依賴注入),可以理解為將某個類需要依賴的成員注入到這個類中。那么,如何實現依賴注入呢?
最簡單的方式是,先通過BeanHelper獲取所有BeanMap(是一個Map<Class<?>,Object>結構,記錄了類與對象的映射關系)。然后遍歷這個映射關系,分別取出Bean類與Bean實例,進而通過反射獲取類中所有成員變量。繼續遍歷這些成員變量,在循環中判斷當前成員變量是否帶有Inject注解,若帶有該注解,則從Bean Map中根據Bean類取出Bean實例。最后通過ReflectionUtil#setField方法來修改當前成員變量的值。
此時,在IoC框架中所管理的對象都是單例的,由於IoC框架底層還是從BeanHelper中獲取Bean Map的,而Bean Map中的對象都是事先創建好並放入到這個Bean容器的,所有的對象都是單利的。
請求轉發器
我們需要創建一個ControllerHelper類,讓它來處理如下邏輯:
通過ControllerHelp,我們可以獲取所有定義了Controller注解的類,可以通過反射獲取該類所有帶有Action注解的方法(簡稱"Action"方法),獲取Action注解中的請求表達式,進而獲取請求方法與請求路徑,封裝一個請求對象(Request)與處理對象(Handler),最后將Request與Handler建立一個映射關系,放入一個Action Map中,並提供一個可根據請求方法與請求路徑獲取處理對象的方法。
現在需要編寫一個Servlet,讓它來處理所有的請求。從HttpServletRequest對象中獲取請求方法與請求路徑,通過ControllerHelper#getHandler方法來獲取Handler對象。當拿到Handler對象后,我們可以方便地獲取Controller的類,進而通過BeanHelp.getBean方法獲取Controller的實例對象。
一個簡單的MVC框架就開發完畢了,通過這個DispatcherServlet來處理所有的請求,根據請求信息從ControllerHelper中獲取對應的Action方法,然后使用反射技術調用Action方法,同時需要具體的傳入方法參數,最后拿到返回值並判斷返回值的類型,進行相應的處理。
通過Controller注解來定義Controller類;通過Inejct注解來實現依賴注入;通過Action注解來定義Action方法。通過一系列的Helper類來初始化MVC框架。通過DispatcherServlet來處理所有的請求;根據請求方法與請求路徑來調動具體的Action方法,判斷Action方法的返回值,若為View類型,則跳轉到JSP頁面,若為Data類型,而返回JSON數據。
AOP
在AOP中,我們需要定義一個Aspect(切面)類來編寫需要橫切業務邏輯的代碼,也就是上面提到的性能監控代碼。此外,我們需要通過一個條件來匹配想要攔截的類,這個條件在AOP中稱為Pointcut(切點)。
代理,或稱為Proxy,意思就是你不用去做,別人代替你去處理。
通過CGLib實現動態代理
在客戶端代碼繼承AspectProxy;
proxy為通過CGLib返回后的具有AOP實現的目標類,targetClass為原來的類
Object proxy = ProxyManager.createProxy(targetClass, proxyList);
BeanHelper.setBean(targetClass, proxy);