JAVA springMVC原始碼分析
就簡單的寫一寫,面試可能會問,我一箇中級java開發,不會太多東西,大佬繞道!
- 建立一個Servlet工程,分三層寫好: … 2.建立web.xml
<display-name>手寫一個springMVC</display-name> <!-- 手寫一個servlet --> <servlet> <servlet-name>zbmvc</servlet-name> <servlet-class>com.zb.mvcframework.servlet.ZbDispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>application.properties</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>zbmvc</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping>
- 寫註解: package com.zb.mvcframework.anno;
import java.lang.annotation.*;
/**
- class descriptions:()
- the package’s name is com.zb.mvcframework.anno
- this class created by ZB
- this data on 0:30. */ // 作用域範圍 @Target(ElementType.TYPE) // 生命週期執行階段可以執行 @Retention(RetentionPolicy.RUNTIME) // 可見的 @Documented public @interface MvcController { String value() default “”; }
package com.zb.mvcframework.anno;
import java.lang.annotation.*;
/**
- class descriptions:()
- the package’s name is com.zb.mvcframework.anno
- this class created by ZB
- this data on 0:30. */ // 作用域範圍 @Target(ElementType.TYPE) // 生命週期執行階段可以執行 @Retention(RetentionPolicy.RUNTIME) // 可見的 @Documented public @interface MvcService { String value() default “”; } package com.zb.mvcframework.anno;
import java.lang.annotation.*;
/**
- class descriptions:()
- the package’s name is com.zb.mvcframework.anno
- this class created by ZB
- this data on 0:37. */ @Target({ElementType.TYPE,ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface MvcResponseBody { String value() default “”; } package com.zb.mvcframework.anno;
import java.lang.annotation.*;
/**
- class descriptions:()
- the package’s name is com.zb.mvcframework.anno
- this class created by ZB
- this data on 0:30. */ // 作用域範圍 @Target(ElementType.PARAMETER) // 生命週期執行階段可以執行 @Retention(RetentionPolicy.RUNTIME) // 可見的 @Documented public @interface MvcRequestParam { String value() default “”; } package com.zb.mvcframework.anno;
import java.lang.annotation.*;
/**
- class descriptions:()
- the package’s name is com.zb.mvcframework.anno
- this class created by ZB
- this data on 0:37. */ @Target({ElementType.TYPE,ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface MvcRequestMapping { String value() default “”; } package com.zb.mvcframework.anno;
import java.lang.annotation.*;
/**
- class descriptions:()
- the package’s name is com.zb.mvcframework.anno
- this class created by ZB
- this data on 0:30. */ // 作用域範圍 @Target(ElementType.FIELD) // 生命週期執行階段可以執行 @Retention(RetentionPolicy.RUNTIME) // 可見的 @Documented public @interface MvcAutowried { String value() default “”; } package com.zb.mvcframework.anno;
import java.lang.annotation.*;
/**
- class descriptions:()
- the package’s name is com.zb.mvcframework.anno
- this class created by ZB
- this data on 15:28. */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Mapper { String value() default “”; }
- 手寫一個Servlet:
package com.zb.mvcframework.servlet;
import com.zb.mvcframework.anno.MvcAutowried; import com.zb.mvcframework.anno.MvcController; import com.zb.mvcframework.anno.MvcRequestMapping; import com.zb.mvcframework.anno.MvcService;
import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URL; import java.util.*;
/**
-
class descriptions:(核心入口類)
-
the package’s name is com.zb.mvcframework
-
this class created by ZB
-
this data on 0:03. */ public class ZbDispatcherServlet extends HttpServlet {
// properties private Properties contextConfig = new Properties();
// 所有的類 private List classNames = new ArrayList();
// IOC private Map<String,Object> ioc = new HashMap<String,Object>();
// Haddler private Map<String,Method> handlerMapping = new HashMap<String,Method>();
@Override public void init(ServletConfig config) throws ServletException { // 載入配置檔案 config.getInitParameter("contextConfigLocation) 是init-param引數name doLoadConfig(config.getInitParameter(“contextConfigLocation”));
// 解析配置檔案,掃描相關的包.類, 這裡用到Properties對像.getProperty("scanPackage")拿到對應的包名 doScanner(contextConfig.getProperty("scanPackage")); // 初始化所有相關類,並且儲存到IOC容器中 doInstance(); // 完成DI doAutowried(); // 建立HandlerMapping 和 URL+Method建立關係 initHandlerMapping();
}
/** * */ private void initHandlerMapping() { if(ioc.isEmpty()){return;} for(Map.Entry<String,Object> entry : ioc.entrySet()){ Class<?> aClass = entry.getValue().getClass();
// 如果是一個Controller用HandlerMapping if(!aClass.isAnnotationPresent(MvcController.class)){ continue; } String baseUrl = ""; if(!aClass.isAnnotationPresent(MvcRequestMapping.class)){ baseUrl = aClass.getAnnotation(MvcRequestMapping.class).value(); // 獲取方法 Method[] methods = aClass.getMethods(); for (Method method : methods){ if(!method.isAnnotationPresent(MvcRequestMapping.class)){continue;} String url = "/"+baseUrl+"/"+method.getAnnotation(MvcRequestMapping.class).value().replaceAll("/+","/"); handlerMapping.put(url,method); } } }
}
/**
-
依賴注入 */ private void doAutowried() { if(ioc.isEmpty()){ return; } for(Map.Entry<String,Object> entry : ioc.entrySet()){ Field[] declaredFields = entry.getValue().getClass().getDeclaredFields(); for(Field f : declaredFields){ // 不存在繼續 if(!f.isAnnotationPresent(MvcAutowried.class)){continue;} // 存在 // 獲取註解 String beanName = f.getAnnotation(MvcAutowried.class).value(); if("".equals(beanName)){ beanName = f.getType().getName(); }
// 可見,setter方法 f.setAccessible(true); // 強制授權 try { f.set(entry.getValue(),ioc.get(beanName)); } catch (IllegalAccessException e) { e.printStackTrace(); } }
} }
/**
-
將解析後的檔案放入IOC中並例項化,IOC就是一個Map<String,Object>, 最後用List包起來 */ private void doInstance() {
if(classNames.isEmpty()){return;} try{ // List不為空的情況下,遍歷出List,用反射機制或者類名/註解.value()值 for(String className : classNames){ Class<?> aClass = Class.forName(className); // 帶註解的類掃描 if (aClass.isAnnotationPresent(MvcController.class)) { String beanName = lowerFirstCase(aClass.getSimpleName());// 類名 ioc.put(beanName,aClass.newInstance()); }else if(aClass.isAnnotationPresent(MvcService.class)){
// 類名首字母小寫 // 自定義 String beanName = aClass.getAnnotation(MvcService.class).value(); // 獲取value值,如果value沒有值預設類名 if("".equals(beanName)){ beanName = lowerFirstCase(aClass.getSimpleName()); } Object o = aClass.newInstance(); // 例項化 ioc.put(beanName, o); // 介面全稱作為key , 介面例項作為值 Class<?>[] interfaces = aClass.getInterfaces(); for(Class<?> i : interfaces){ if(ioc.containsKey(i.getName())){ throw new Exception("介面已經存在!"); } ioc.put(i.getName(),o); } }else{ continue; } }
}catch (Exception e){
} }
// 首字母小寫 private String lowerFirstCase(String simpleName) { char[] c = simpleName.toCharArray(); c[0] += 32; return String.valueOf©; }
/**
- 解析xml/properties中的配置
- @param scanPackage */ private void doScanner(String scanPackage) { // 將所有包名全部轉為com/zb/xx/xx/ 檔案儲存 URL url = this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\.", “/”)); File classDir = new File(url.getFile()); // 迭代 for(File file : classDir.listFiles()){ // 遞迴,有無這個目錄,沒有:包名.類名, 因為沒有進行解析 if(file.isDirectory()){ doScanner(scanPackage + “.” + file.getName()); }else{ if(!file.getName().contains(".class")){continue;} // 包名.類名去掉.class檔案字尾 String className = (scanPackage + “.” + file.getName().replaceAll(".class","")).trim(); classNames.add(className); } } }
/**
- 初始化載入配置init-param
- @param contextConfigLocation */ private void doLoadConfig(String contextConfigLocation) { // 類路徑下取得properties InputStream is = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation); try { // 通過load方法直接呼叫,load方法引數InputStream contextConfig.load(is); } catch (IOException e) { e.printStackTrace(); } finally { if(null != is){ try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } }
@Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //super.doGet(req, resp); this.doPost(req,resp); }
@Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { try { doDispatch(req, resp); } catch (IOException e) { resp.getWriter().write(“500”); } }
private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws IOException { if(this.handlerMapping.isEmpty()) { return; }
String url = req.getRequestURI(); String contextPath = req.getContextPath(); url = url.replace(contextPath,"").replaceAll("/+","/"); if(!this.handlerMapping.containsKey(url)){ resp.getWriter().write("404");return; } Method method = this.handlerMapping.get(url); Map<String,String[]> params = req.getParameterMap(); String simpleName = lowerFirstCase(method.getDeclaringClass().getSimpleName()); String[] params1 = {""}; if(!"".equals(params.get("xx")) && null != params.get("xx")){ params1 = params.get("xx"); } try { method.invoke(ioc.get(simpleName),new Object[]{req,resp,params1}); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); }
} }
-
- Controller呼叫: package com.zb.mvcframework.demo.action;
import com.zb.mvcframework.anno.*; import com.zb.mvcframework.demo.service.MyService;
import java.util.List;
/**
-
class descriptions:()
-
the package’s name is com.zb.mvcframework.demo.action
-
this class created by ZB
-
this data on 0:24. */ @MvcController @MvcRequestMapping("/api") public class MyAction {
@MvcAutowried private MyService myService;
@MvcRequestMapping("/select") @MvcResponseBody public List select(@MvcRequestParam(“xx”) String xx){ List select = myService.select(xx); return select; } }
就是一個大概,寫的跟垃圾一樣,看看就行了,這個東西百度一堆,淘寶也一堆,就沒事合計發一個,順便體驗一下論壇流程