簡單設計實踐spring框架(手寫spring)

目錄

1 應用spring的流程

2 梳理設計思路

3 coding spring實戰

核心流程:

Step1 配置依賴(僅僅依賴servlet-api包)

Step2 定義一個核心控制器

Step3 自定義一些類spring的註解

Step4 配置容器啓動時的初始化

Step5 執行請求,請求分發

Step6 啓動

Step7 驗證功能

4 源碼?just do it 動手實踐吧

公衆號搜索:DeanKano

企鵝羣號: 561932405


應用spring的流程

 

 


 

梳理設計思路

 

 

coding spring實戰

 

核心流程:

Step1 配置依賴(僅僅依賴servlet-api包)

<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>servlet-api</artifactId>
    <version>2.4</version>
</dependency>

Step2 定義一個核心控制器

DkDispatcherServlet extends HttpServlet {...}

在web.xml配置核心控制器

 <!-- 核心控制器 -->
    <servlet>
        <servlet-name>dkDispatcher</servlet-name>
        <servlet-class>com.dean.framework.DkDispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:application.properties</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dkDispatcher</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>

Step3 自定義一些類spring的註解

@DkController

@DkService

@DkAutowired

@DkRequestMapping

@DkRequestParam

Step4 配置容器啓動時的初始化

// 啓動DkDispatcherServlet做初始化@Overridepublic void init(ServletConfig config) throws ServletException {    // 1 加載配置文件    doLoadConfig(config.getInitParameter("contextConfigLocation"));    // 2 掃描配置包路徑    doScanner(contextConfig.getProperty("scanPackage"));    // 3 反射實例化加載到IOC容器中    doInstance();    // 4 DI依賴注入,針對IOC容器中加載到的類,自動對需要賦值的屬性進行初始化操作    doAutowired();    // 5 初始化HandlerMapping    initHandlerMapping();}
privatevoid doLoadConfig(String contextConfigLocation) {    contextConfigLocation = contextConfigLocation.replace("classpath:", EMPTY_STRING);    try (InputStream resourceAsStream = this.getClass().getClassLoader()            .getResourceAsStream(contextConfigLocation)) {        contextConfig.load(resourceAsStream);    } catch (Exception e) {        e.printStackTrace();    }}
private void doScanner(String scanPackage) {    URL url = this.getClass().getClassLoader().getResource(            SLASH_STRING + scanPackage.replaceAll("\\.", SLASH_STRING));    if (url == null) return;    File classDir = new File(url.getFile());    for (File file : classDir.listFiles()) {        if (file.isDirectory()) {            doScanner(scanPackage + SPOT_STRING + file.getName());        } else {            String className = scanPackage + SPOT_STRING +                    file.getName().replace(".class", EMPTY_STRING);            classNames.add(className);        }    }}
private void doInstance() {
  if (classNames.isEmpty()) return;
  for (String className : classNames) {
      try {
          Class<?> clazz = Class.forName(className);
          if (clazz.isInterface()) continue;
          // 針對指定掃描到的包進行實例化
          // 默認bean名稱是類名的首字母小寫
          String beanName = getFirstLower(clazz.getName());
          Object newInstance = clazz.newInstance();
          if (clazz.isAnnotationPresent(DkController.class)) {
              // 指定了bean名稱
              DkController dkController = clazz.getAnnotation(DkController.class);
              if (!EMPTY_STRING.equals(dkController.value())) {
                  beanName = dkController.value();
              }

          } else if (clazz.isAnnotationPresent(DkService.class)) {
              // 指定bean名稱
              DkService dkService = clazz.getAnnotation(DkService.class);
              if (!EMPTY_STRING.equals(dkService.value())) {
                  beanName = dkService.value();
              }

              // 針對接口的,bean名稱用接口的名稱
              Class<?>[] interfaces = clazz.getInterfaces();
              if (interfaces.length == 1) {
                  for (Class<?> anInterface : interfaces) {
                      beanName = getFirstLower(anInterface.getName());
                  }
              } else if (interfaces.length > 1) {
                  // TODO 多接口
              }

          } else {
              continue;
          }
          ioc.put(beanName, newInstance);
      } catch (Exception e) {
          e.printStackTrace();
      }
  }
}
private void doAutowired() {
  if (ioc.isEmpty()) return;
  for (Map.Entry<String, Object> entry : ioc.entrySet()) {
      Field[] declaredFields = entry.getValue().getClass().getDeclaredFields();
      for (Field declaredField : declaredFields) {
          if (!declaredField.isAnnotationPresent(DkAutowired.class)) {
              continue;
          }
          String beanName = getFirstLower(declaredField.getType().getName());
          DkAutowired annotation = declaredField.getAnnotation(DkAutowired.class);
          if (!EMPTY_STRING.equals(annotation.value())) {
              beanName = annotation.value();
          }
          try {
              declaredField.setAccessible(true);
              declaredField.set(entry.getValue(), ioc.get(beanName));
          } catch (IllegalAccessException e) {
              e.printStackTrace();
              continue;
          }
      }
  }
}
private void initHandlerMapping() {
  if (ioc.isEmpty()) return;
  for (Map.Entry<String, Object> entry : ioc.entrySet()) {
      Class<?> clazz = entry.getValue().getClass();
      if (!clazz.isAnnotationPresent(DkController.class)) {
          continue;
      }
      String baseUrl = EMPTY_STRING;
      if (clazz.isAnnotationPresent(DkRequestMapping.class)) {
          DkRequestMapping dkRequestMapping = clazz.getAnnotation(DkRequestMapping.class);
          baseUrl = dkRequestMapping.value();
      }

      for (Method method : clazz.getMethods()) {
          if (!method.isAnnotationPresent(DkRequestMapping.class)) {
              continue;
          }
          DkRequestMapping dkRequestMapping = method.getAnnotation(DkRequestMapping.class);
          String regexUrl = (SLASH_STRING + baseUrl + dkRequestMapping.value()).replaceAll("/+", SLASH_STRING);
          Pattern pattern = Pattern.compile(regexUrl);
          handlerMapping.add(new Handler(method, entry.getValue(), pattern));
          System.out.println("Mapping: [" + regexUrl + "] ==>" + method);
      }
  }
}

Step5 執行請求,請求分發

private voiddoDispatcher(HttpServletRequest req, HttpServletResponse resp) throws InvocationTargetException, IllegalAccessException, IOException {
    Handler handler = retrieveHandler(req);
    if (handler == null) {
        resp.getWriter().write("404 NOT FOUND");
        return;
    }

    Object[] paramValues = new Object[handler.method.getParameterTypes().length];
    // String[]可能多個參數,例如:?name=tom&name=jaine
    Map<String, String[]> reqParams = req.getParameterMap();
    for (Map.Entry<String, String[]> stringEntry : reqParams.entrySet()) {
        String paramName = stringEntry.getKey();
        if (!handler.paramsIndexMapping.keySet().contains(paramName)) {
            continue;
        }

        int paramIndex = handler.paramsIndexMapping.get(paramName);
        paramValues[paramIndex] = Arrays.toString(stringEntry.getValue()).replaceAll("\\[|\\]", EMPTY_STRING);
    }

    int respIndex = handler.paramsIndexMapping.get(HttpServletResponse.class.getName());
    int reqIndex = handler.paramsIndexMapping.get(HttpServletRequest.class.getName());
    paramValues[reqIndex] = req;
    paramValues[respIndex] = resp;
    handler.method.invoke(handler.controller, paramValues);
}

doDispatcher(req, resp);

Step6 啓動

一 直接運行maven 插件 jetty:run

二 命令行啓動,在cmd窗口執行:

  `$mvn jetty:run`

Step7 驗證功能

啓動日誌:

...
[INFO] Starting jetty 6.1.7 ...
[INFO] jetty-6.1.7
[INFO] No Transaction manager found - if your webapp requires one, please configure one.
Mapping: [/sample/query.do] ==>public void com.dean.framework.sample.UserAction.query(java.lang.String,javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
Mapping: [/sample/add.do] ==>public void com.dean.framework.sample.UserAction.addUser(java.lang.String,javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
Mapping: [/sample/remove.do] ==>public void com.dean.framework.sample.UserAction.removeUser(java.lang.String,javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
[INFO] Started [email protected]:8080
[INFO] Started Jetty Server

 

請求示例:

http://localhost:8080/sample/query.do?name=Tom

 

源碼?just do it 動手實踐吧

源碼地址: https://github.com/lingqibaobei/rangers-framework-spring

公衆號搜索:DeanKano

企鵝羣號: 561932405

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章