用idea手寫spring

1、利用模板創建一個maven項目。

2、創建項目目錄,架構設計如下

3、添加pom依賴,僅此一個,在<dependencies></dependencies>中 添加,其他爲初始化默認數據。

 <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>4.0.1</version>
      <scope>provided</scope>
 </dependency>

4、 在web.xml添加路徑映射,application.properties會標紅,這個數據正常不用管。

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>

  <servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>com.spring.mvcframework.servlet.DispatcherServlet</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>springmvc</servlet-name>
    <url-pattern>/*</url-pattern>
  </servlet-mapping>
</web-app>

5、application.properties內容就一行。

scanPackage=com.spring.demo

6、各註解實現

package com.spring.mvcframework.annotation;

import java.lang.annotation.*;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DwbAutowired {
    String value() default "";
}
package com.spring.mvcframework.annotation;

import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DwbController {
    String value() default "";
}
package com.spring.mvcframework.annotation;

import java.lang.annotation.*;

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DwbRequestMapping {
    String value() default "";
}
package com.spring.mvcframework.annotation;

import java.lang.annotation.*;

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DwbRequestParam {
    String value() default "";
}
package com.spring.mvcframework.annotation;

import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DwbService {
    String value() default "";
}

7、DemoAction類。注意注入時要寫接口,不能是實現類,不然訪問會報空指針異常,即注入的service爲null。

package com.spring.demo.action;

import com.spring.demo.service.DemoService;
import com.spring.mvcframework.annotation.DwbAutowired;
import com.spring.mvcframework.annotation.DwbController;
import com.spring.mvcframework.annotation.DwbRequestMapping;
import com.spring.mvcframework.annotation.DwbRequestParam;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@DwbController
@DwbRequestMapping("/demo")
public class DemoAction {

    @DwbAutowired
    private DemoService demoService;


    @DwbRequestMapping("/get")
    public void get(HttpServletRequest req, HttpServletResponse res,
                    @DwbRequestParam("name") String name) {
        System.out.println(" 參數:====================" + name);
        String result = demoService.get(name);
        try {
            res.getWriter().write(result);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @DwbRequestMapping("/add")
    public void add(HttpServletRequest req, HttpServletResponse res,
                    @DwbRequestParam("number1") Integer number1, @DwbRequestParam("number2") Integer number2) {
        try {
            res.getWriter().write(number1 + "+" + number2 + "=" + (number1 + number2));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @DwbRequestMapping("delete")
    public void delete(HttpServletRequest req, HttpServletResponse res,
                       @DwbRequestParam("id") Integer id) {
        System.out.println(" 參數:====================" + id);
        String result = "id" + id + "已刪除";
        try {
            res.getWriter().write(result);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

8、DemoService接口

package com.spring.demo.service;

public interface DemoService {
    public String get(String name);
}

9、DemoService接口實現DemoServiceImpl

package com.spring.demo.service;

import com.spring.mvcframework.annotation.DwbService;

@DwbService
public class DemoServiceImpl implements DemoService {

    public String get(String name) {
        return "hello" + name;
    }
    
}

10、DispatcherServlet類,doInstance方法中有newInstance要注意servlet-api的版本,詳見方法上的註釋。

package com.spring.mvcframework.servlet;

import com.spring.mvcframework.annotation.DwbAutowired;
import com.spring.mvcframework.annotation.DwbController;
import com.spring.mvcframework.annotation.DwbRequestMapping;
import com.spring.mvcframework.annotation.DwbService;

import javax.servlet.ServletConfig;
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.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;

public class DispatcherServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    //跟web.xml中param-name的值一致
    private static final String LOCATION = "contextConfigLocation";

    //保存所有配置信息
    private Properties p = new Properties();

    //保存所有被掃描到的相關的類名
    private List<String> classNames = new ArrayList<String>();

    //核心IOC容器, 保存所有初始化的Bean
    private Map<String, Object> ioc = new HashMap<String, Object>();

    private Map<String, Method> handlerMapping = new HashMap<String, Method>();

    public DispatcherServlet() {
        super();
    }

    /**
     *  初始化,加載配置文件
     *
     * @param config 配置
     */
    public void init(ServletConfig config) {
        //1、加載配置文件
        doLoadConfig(config.getInitParameter(LOCATION));

        //2、掃描所有相關的類
        doScanner(p.getProperty("scanPackage"));

        //3、初始化所有相關的實例,並保存到IOC容器中
        doInstance();

        //4、依賴注入
        doAutowired();

        //5、構造HandlerMapping
        initHandleMapping();

        //6、等待請求 匹配URL,定位方法,反射調用執行
        //調用doGet方法或doPost方法

        //提示信息
        System.out.println("spring mvc framework is init");

    }

    private void doLoadConfig(String location) {
        InputStream fis = null;
        try {
            fis = this.getClass().getClassLoader().getResourceAsStream(location);

            //讀取配置文件
            if (null == fis){
                System.out.println("掃描文件不應該爲空=============");
            }
            p.load(fis);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != fis) {
                    fis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private void doScanner(String packageName) {
        //將所有的包路徑替換爲文件路徑
        URL url = this.getClass().getClassLoader().getResource("/" + packageName.replaceAll("\\.", "/"));
        File dir = new File(url.getFile());
        for (File file : dir.listFiles()) {
            //如果是文件夾,繼續遞歸
            if (file.isDirectory()) {
                doScanner(packageName + "." + file.getName());
            } else {
                classNames.add(packageName + "." + file.getName().replaceAll(".class", "").trim());
            }
        }
    }

    /**
     * IOC容器的key默認是類名首字母小寫,如果是自己自己設置類名,則優先使用自定義的。
     *
     * @param str 類名
     * @return 僅轉換首字母的字符串
     */
    private String lowerFirstCase(String str) {
        char[] chars = str.toCharArray();
        chars[0] += 32;
        return String.valueOf(chars);
    }

    /**
     * 
     *  下面的clazz.getDeclaredConstructor().newInstance()
     *  在3.0.1之前使用 clazz.newInstance()
     *     
     */
    private void doInstance() {
        if (classNames.size() == 0) {
            return;
        }
        try {
            for (String classNameItem : classNames) {
                Class<?> clazz = Class.forName(classNameItem);
                if (clazz.isAnnotationPresent(DwbController.class)) {
                    //默認首字母小寫作爲beanName
                    String beanName = lowerFirstCase(clazz.getSimpleName());
                    ioc.put(beanName, clazz.getDeclaredConstructor().newInstance());
                } else if (clazz.isAnnotationPresent(DwbService.class)) {
                    DwbService service = clazz.getAnnotation(DwbService.class);
                    String beanName = service.value();

                    //如果用戶設置了名字,就用用戶自己的設置
                    if (!"".equals(beanName.trim())) {
                        ioc.put(beanName, clazz.getDeclaredConstructor().newInstance());
                        continue;
                    }

                    //如果用戶沒設,就按接口類型創建一個實例
                    Class<?>[] interfaces = clazz.getInterfaces();
                    for (Class<?> i : interfaces) {
                        ioc.put(i.getName(), clazz.getDeclaredConstructor().newInstance());
                    }
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    private void doAutowired() {
        if (ioc.isEmpty()) {
            return;
        }

        for (Map.Entry<String, Object> entry : ioc.entrySet()) {
            //拿到實例對象中的所有屬性
            Field[] fields = entry.getValue().getClass().getDeclaredFields();
            for (Field field : fields) {
                if (!field.isAnnotationPresent(DwbAutowired.class)) {
                    continue;
                }

                DwbAutowired autowired = field.getAnnotation(DwbAutowired.class);
                String beanName = autowired.value().trim();
                if ("".equals(beanName)) {
                    beanName = field.getType().getName();
                }
                field.setAccessible(true);
                try {
                    field.set(entry.getValue(), ioc.get(beanName));
                } catch (Exception e) {
                    e.printStackTrace();
                    continue;
                }
            }
        }
    }


    private void initHandleMapping() {
        if (ioc.isEmpty()) {
            return;
        }

        for (Map.Entry<String, Object> entry : ioc.entrySet()) {
            Class<?> clazz = entry.getValue().getClass();
            if (!clazz.isAnnotationPresent(DwbController.class)) {
                continue;
            }

            String baseUrl = "";

            //獲取Controller的url配置
            if (clazz.isAnnotationPresent(DwbRequestMapping.class)) {
                DwbRequestMapping requestMapping = clazz.getAnnotation(DwbRequestMapping.class);
                baseUrl = requestMapping.value();
            }

            //獲取Method的url值
            Method[] methods = clazz.getMethods();
            for (Method method : methods) {
                //沒有加RequestMapping註解的直接忽略
                if (!method.isAnnotationPresent(DwbRequestMapping.class)) {
                    continue;
                }

                //映射URL
                DwbRequestMapping requestMapping = method.getAnnotation(DwbRequestMapping.class);
                String url = ("/" +baseUrl +"/" + requestMapping.value()).replaceAll("/+", "/");
                handlerMapping.put(url, method);
                System.out.println("mapped " + url + "," + method);
            }
        }
    }

    public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException {
        this.doPost(req, res);
    }

    /**
     * 業務處理
     *
     * @param req
     * @param res
     */
    public void doPost(HttpServletRequest req, HttpServletResponse res) throws IOException {
        try {
            //開始匹配到對應的方法
            doDispatch(req, res);
        } catch (Exception e) {
            //如果匹配過程中出現異常,將異常值打印出去
            res.getWriter().write("500 Exception, Details: \r\n"
                    + Arrays.toString(e.getStackTrace()).replaceAll("\\[|\\}]", "")
                    .replaceAll(",\\s", "\r\n"));
        }
    }

    private void doDispatch(HttpServletRequest req, HttpServletResponse res) 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)) {
            res.getWriter().write("404 Not Found");
            return;
        }

        Method method = this.handlerMapping.get(url);
        //獲取方法的參數列表
        Class<?>[] paramsTypes = method.getParameterTypes();
        //獲取請求的參數
        Map<String, String[]> paramterMap = req.getParameterMap();
        //保存參數值
        Object[] paramValues = new Object[paramsTypes.length];
        //方法的參數列表
        for (int i = 0; i < paramsTypes.length; i++) {
            //根據參數名稱做某些處理,
            Class parameterType = paramsTypes[i];
            if (parameterType == HttpServletRequest.class) {
                //參數類型已明確,強制轉類型
                paramValues[i] = req;
                continue;
            } else if (parameterType == HttpServletResponse.class) {
                paramValues[i] = res;
                continue;
            } else if (parameterType == String.class) {
                for (Map.Entry<String, String[]> param : paramterMap.entrySet()) {
                    String value = Arrays.toString(param.getValue())
                            .replaceAll("\\[|\\]", "")
                            .replaceAll(",\\s", ",");
                    paramValues[i] = value;
                }
            }
        }

        try {
            String beanName = lowerFirstCase(method.getDeclaringClass().getSimpleName());
            //利用反射機制來調用
            method.invoke(this.ioc.get(beanName), paramValues);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

最後添加tomcat並運行,會返回404,沒關係,我們沒有設置默認頁。在輸入http://localhost:8080/demo/get?name=ddd即可看到返回數據,方法調用成功。

代碼獲取github:https://github.com/dwenb/springmvc-demo

本文參考:https://www.jianshu.com/p/f57db8b29be9 

 

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