1. 程式人生 > >JAVA springMVC原始碼分析

JAVA springMVC原始碼分析

就簡單的寫一寫,面試可能會問,我一箇中級java開發,不會太多東西,大佬繞道!

  1. 建立一個Servlet工程,分三層寫好: … 2.建立web.xml
<?xml version="1.0" encoding="UTF-8"?>
<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>
  1. 寫註解: 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 “”; }
  1. 手寫一個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();
     }
    

    } }

  1. 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; } }

就是一個大概,寫的跟垃圾一樣,看看就行了,這個東西百度一堆,淘寶也一堆,就沒事合計發一個,順便體驗一下論壇流程