?? 第二部分緊接第一部分。我們在第一部分中講到了這個(gè)mini框架的幾個(gè)注解毅厚,嵌入的tomcat服務(wù)器塞颁,以及執(zhí)行類掃描和DispatcherSevlet。需要的話可以根據(jù)下面的鏈接回看一下~
友情鏈接:
手寫簡易SpringMVC框架(一):注解吸耿、內(nèi)嵌Tomcat祠锣、類掃描
手寫簡易SpringMVC框架(三):極其簡單的應(yīng)用
1.6 BeanFactory實(shí)例化bean并管理
public class BeanFactory {
private static Map<Class<?>, Object> classToBean = new ConcurrentHashMap<>();
public static Object getBean(Class<?> cls) {
return classToBean.get(cls);
}
public static void initBean(List<Class<?>> classList) throws Exception {
//xxxxxxxxxxxxxxx
}
private static boolean notNeedCreateBean(Class<?> aClass) {
//xxxxxxxxxxxxxxxxx
}
private static boolean finishCreateBean(Class<?> aClass, Map<Class<?>, Object> notCreateFinishedBeanMap)
throws IllegalAccessException, InstantiationException {
//xxxxxxxxxxxxxxx
}
}
??classToBean
記錄了每個(gè)類型class及其對(duì)應(yīng)的實(shí)例對(duì)象,相當(dāng)于容器咽安。getBean
方法會(huì)返回對(duì)應(yīng)的bean實(shí)例伴网。initBean
方法(誒?好像在哪見過妆棒?澡腾?對(duì)了,是在手寫簡易SpringMVC框架(一)中1.2節(jié)的第3個(gè)步驟中出現(xiàn)過糕珊,有印象了嗎)負(fù)責(zé)實(shí)例化bean并將其放入classToBean
容器中动分。我們重點(diǎn)來看一下initBean(List<Class<?>> classList)
方法。
?? initBean
方法中需要進(jìn)行bean的實(shí)例化红选,1 當(dāng)實(shí)例化一個(gè)bean的時(shí)候澜公,需要首先實(shí)例化它所依賴的bean,例如A中有個(gè)字段指向B實(shí)例喇肋,那么應(yīng)當(dāng)首先實(shí)例化B坟乾,然后在實(shí)例化A迹辐。2 另外還需要考慮到循環(huán)依賴的問題,簡單說就是A依賴于B但是B有依賴于A甚侣,結(jié)果這就導(dǎo)致沒法實(shí)例化這倆實(shí)例明吩。結(jié)合這兩點(diǎn),我們的程序如下(代碼中做了注釋殷费,可以直接看注釋印荔,仔細(xì)體味一下這段代碼):
public static void initBean(List<Class<?>> classList) throws Exception {
//toCreate存放了所有類的類型
List<Class<?>> toCreate = new ArrayList<>(classList);
//用于存放那些還沒創(chuàng)建成功的bean,比如按照順序先要實(shí)例化A宗兼,但是A依賴B躏鱼,B卻沒有被實(shí)例化,那么A
//自然就沒法實(shí)例化殷绍,所以會(huì)把A對(duì)象暫時(shí)存放在這里染苛。 class<A> --> 未創(chuàng)建完成的A對(duì)象
Map<Class<?>, Object> notCreateFinishedBeanMap = new HashMap<>();
while (toCreate.size() != 0) {
//在這一輪創(chuàng)建之前,先記錄列表里面還有多少個(gè)類
int size = toCreate.size();
//下面就是遍歷toCreate中的每一個(gè)class主到,如果它不需要實(shí)例化或者能對(duì)其實(shí)例化成功就將其移除出隊(duì)列茶行,
//不行的話就保留并添加到notCreateFinishedBeanMap中。
//注意這里涉及到刪除操作登钥,所以就不能簡單的使用for循環(huán)了畔师。
int i = 0;
while (toCreate.size() > i) {
if (notNeedCreateBean(toCreate.get(i)) || finishCreateBean(toCreate.get(i), notCreateFinishedBeanMap)) {
toCreate.remove(i);
} else {
// 這個(gè)i記錄了,沒有被實(shí)例化的class的數(shù)量
i++;
}
}
//這一輪創(chuàng)建完成后牧牢,查看是否列表中的元素有減少看锉,減少了說明這一輪有bean創(chuàng)建成功,
//沒有減少說明沒有bean可以被成功創(chuàng)建 (也就是出現(xiàn)了cycle dependency)
if (toCreate.size() == size) {
throw new Exception("cycle dependency");
}
}
}
?? 上面這段代碼的最終結(jié)束循環(huán)的條件toCreate隊(duì)列為空塔鳍,也就是不需要實(shí)例化的對(duì)象移除出了隊(duì)列伯铣,然后需要實(shí)例化的對(duì)象全部實(shí)例化完成。
?? notNeedCreateBean
方法十分簡單:class沒有注解的話轮纫,我們就不需要實(shí)例化這些對(duì)象
private static boolean notNeedCreateBean(Class<?> aClass) {
return !(aClass.isAnnotationPresent(MyBean.class) || aClass.isAnnotationPresent(MyController.class));
}
?? finishCreateBean
方法的主要思路是:如果能夠?qū)嵗?dāng)前對(duì)象腔寡,則完成實(shí)例化并添加到classToBean容器中,返回true掌唾。 如果當(dāng)前類實(shí)例依賴于其他對(duì)象但是此對(duì)象還沒有出現(xiàn)在容器中放前,那么當(dāng)前類實(shí)例添加到notCreateFinishedBeanMap,返回false糯彬,等待后續(xù)創(chuàng)建完成凭语。
private static boolean finishCreateBean(Class<?> aClass, Map<Class<?>, Object> notCreateFinishedBeanMap)
throws IllegalAccessException, InstantiationException {
//首先檢查一下這個(gè)類的對(duì)象是否出存在于notCreateFinishedBeanMap中
Object instance = notCreateFinishedBeanMap.get(aClass);
if (instance == null) {
instance = aClass.newInstance();
}
Field[] fields = aClass.getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(MyAutoWired.class)) {
Object bean = BeanFactory.getBean(field.getType());
//判斷容器中是否已有它所依賴的實(shí)例
if (bean == null) {
//there not exist the dependent bean in bean factory, then we can not create bean for this aClass
//put the not completely-created bean(instance) into notCreateFinishedBeanMap
notCreateFinishedBeanMap.put(aClass, instance);
//只要出現(xiàn)依賴的bean還未存在,那么就直接返回false
return false;
}
//如果已經(jīng)存在它所依賴的bean撩扒,那么就注入它
field.setAccessible(true);
field.set(instance, bean);
}
}
//將這個(gè)類實(shí)例化成功的實(shí)例對(duì)象放入容器
classToBean.put(aClass, instance);
return true;
}
1.7 MappingHandler和MappingHandlerManager
?? 至此我們的程序還差一部分似扔,那就是目前系統(tǒng)還不知道哪個(gè)請(qǐng)求uri應(yīng)該映射到哪個(gè)方法中去執(zhí)行呢。還記得1.2中提到的springmvc的入口類的執(zhí)行流程吧,這個(gè)十分重要虫几,記得回看一下。在第4步:
MyMappingHandlerManager.resolveMappingHandler(classList);
這里面就建立了請(qǐng)求uri和對(duì)應(yīng)的方法之間的映射關(guān)系挽拔。我們先點(diǎn)進(jìn)MyMappingHandlerManager
看一看辆脸。
public class MyMappingHandlerManager {
/**
* MappingHandler列表,一個(gè)MappingHandler可以認(rèn)為是一對(duì) 請(qǐng)求uri-->執(zhí)行方法 的封裝
*/
public static List<MyMappingHandler> mappingHandlerList = new ArrayList<>();
/**
* 在解析mapping的時(shí)候螃诅,只會(huì)講controller進(jìn)行解析
* @param classList 是類掃描時(shí)掃描到的所有的類啡氢。
*/
public static void resolveMappingHandler(List<Class<?>> classList) {
for (Class<?> aClass : classList) {
if (aClass.isAnnotationPresent(MyController.class)) {
parseMappingHandlerFromController(aClass);
}
}
}
/**
*
* @param controllerClass
*/
private static void parseMappingHandlerFromController(Class<?> controllerClass) {
// get all methods
Method[] methods = controllerClass.getDeclaredMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(MyRequestMapping.class)) {
//獲取到當(dāng)前方法上@MyRequestMapping注解中的請(qǐng)求uri
String mappingUri = method.getDeclaredAnnotation(MyRequestMapping.class).value();
List<String> requestParamList = new LinkedList<>();
for (Parameter parameter : method.getParameters()) {
if (parameter.isAnnotationPresent(MyRequestParam.class)) {
//獲取到當(dāng)前方法中@MyRequestParam所需要的請(qǐng)求參數(shù)的名字,并將其添加到當(dāng)前方法的請(qǐng)求參數(shù)列表中
requestParamList.add(parameter.getDeclaredAnnotation(MyRequestParam.class).value());
} else {
//如果沒有用@MyRequestParam注解术裸,那使用方法參數(shù)的名字
requestParamList.add(parameter.getName());
}
}
String[] requestParams = requestParamList.toArray(new String[requestParamList.size()]);
//對(duì)每一個(gè)uri和method的映射封裝一個(gè)MyMappingHandler對(duì)象(其中還包括了方法所述的controller以及方法需要的參數(shù)名列表)倘是,
// 并放入mappingHandlerList中。
MyMappingHandler mappingHandler = new MyMappingHandler(mappingUri, method, controllerClass, requestParams);
mappingHandlerList.add(mappingHandler);
}
}
}
}
?? 好的袭艺,現(xiàn)在映射關(guān)系也已經(jīng)建立好了搀崭,但是還差一丟丟,大家還記得當(dāng)時(shí)講MyDispatcherServlet的時(shí)候猾编,我提到了一句瘤睹,它只負(fù)責(zé)分發(fā)請(qǐng)求,不負(fù)責(zé)具體執(zhí)行的答倡,那么到底是誰在執(zhí)行請(qǐng)求呢轰传,
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String requestURI = req.getRequestURI();
for (MyMappingHandler mappingHandler : MyMappingHandlerManager.mappingHandlerList) {
if(requestURI.equals(mappingHandler.getUri())){
try {
mappingHandler.handle(req, resp);
} catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
e.printStackTrace();
}
break;
}
}
}
?? 沒錯(cuò),就是這個(gè)MyMappingHandler
這個(gè)玩意兒瘪撇。也就是說它除了負(fù)責(zé)建立uri和method的映射(mapping)之外获茬,還負(fù)責(zé)執(zhí)行請(qǐng)求(handler),大概這也就是為啥給他起了名字叫MappingHandler吧~(我猜的)倔既。那我們就不得不來看一看這個(gè)MyMappingHandler
了恕曲。
public class MyMappingHandler {
private String uri;
private Method method;
private Class<?> controllerClass;
private String[] requestParams;
public MyMappingHandler(String uri, Method method, Class<?> controllerClass, String[] requestParams) {
this.uri = uri;
this.method = method;
this.controllerClass = controllerClass;
this.requestParams = requestParams;
}
public String getUri() {
return uri;
}
public Method getMethod() {
return method;
}
public Class<?> getControllerClass() {
return controllerClass;
}
public String[] getRequestParams() {
return requestParams;
}
public void handle(HttpServletRequest req, HttpServletResponse resp)
throws IllegalAccessException, InstantiationException, InvocationTargetException, IOException {
String[] args = new String[this.requestParams.length];
for (int i = 0; i < args.length; i++) {
//根據(jù)方法需要的參數(shù)的名稱去從request中獲取對(duì)應(yīng)的參數(shù)值
args[i] = req.getParameter(requestParams[i]);
}
Object controller = BeanFactory.getBean(controllerClass);
//反射的方式執(zhí)行方法時(shí)需要方法所處的對(duì)象實(shí)例,也就是我們已經(jīng)創(chuàng)建好的Controller bean
Object result = this.method.invoke(controller, (Object[]) args);
String resultString = result.toString();
//將結(jié)果返回叉存。這里返回的方式也很簡單码俩,直接寫入字符串
resp.getWriter().print(resultString);
}
}
2 梳理一下這個(gè)簡單框架的執(zhí)行流程
??讓我們再來看一下MVCApplication
這個(gè)入口類的執(zhí)行流程,唉歼捏,我都已經(jīng)不知道多少次提起它了稿存,應(yīng)該就結(jié)束了了吧~
public class MVCApplication {
public static void run(Class<?> cls) {
System.out.println("=============================start!!!!==========================");
TomcatServer tomcatServer = new TomcatServer();
try {
tomcatServer.startServer();
List<Class<?>> classList = ClassScanner.scanClasses(cls.getPackage().getName());
BeanFactory.initBean(classList);
MyMappingHandlerManager.resolveMappingHandler(classList);
for (MyMappingHandler myMappingHandler : MyMappingHandlerManager.mappingHandlerList) {
System.out.println(myMappingHandler.getUri() + ":" + myMappingHandler.getMethod());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
?? 1首先就是要配置(端口,主機(jī)名瞳秽,注冊servlet等)并啟動(dòng)tomcat瓣履;
?? 2掃描用戶的應(yīng)用程序中的類
?? 3將需要實(shí)例化的bean實(shí)例化,其中解決了bean的依賴以及注意循環(huán)依賴問題练俐。
?? 4MappingHandlerManager
解析請(qǐng)求和method之間的映射袖迎,同時(shí)解析出每個(gè)方法需要哪些參數(shù)名稱,MappingHandler
對(duì)象還具有執(zhí)行對(duì)應(yīng)的請(qǐng)求的邏輯。
這樣整個(gè)系統(tǒng)就啟動(dòng)初始化并配置完成了燕锥,下面的第三部分辜贵,將結(jié)合一個(gè)十分簡單的應(yīng)用來驗(yàn)證一下這個(gè)微小框架的正確性。
最后依然甩一下鏈接:
·手寫簡易SpringMVC框架(一):注解归形、內(nèi)嵌Tomcat托慨、類掃描
·手寫簡易SpringMVC框架(三):極其簡單的應(yīng)用