【源碼】手寫SpringMVC框架源碼(二)

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <!--定義MyDispatcherServlet類-->
        <servlet-class>com.panda.servlet.MyDispatcherServlet</servlet-class>
        <!--設置爲1後,httpServlet初始化調用init方法-->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <!--攔截所有請求 /*-->
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
</web-app>

註解類

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAutowired {
    String value() default "";
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyController {
    String value() default "";
}
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyRequestMapping {
    String value() default "";
}
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyRequestParam {
    String value() default "";
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyService {
    String value() default "";
}

entity/service/controller類

public class User {

    private String id;
    private String username;
    private String password;

    public User(String id, String username, String password) {
        this.id = id;
        this.username = username;
        this.password = password;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "User{" +
                "id='" + id + '\'' +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}
public interface UserService {

    public String get(String name);
}
@MyService("userService")
public class UserServiceImpl implements UserService {
    @Override
    public String get(String name) {
        return "查詢到姓名爲:" + name;
    }
}
@MyController
public class UserController {

    @MyAutowired
    private UserService userService;

    @MyRequestMapping("/index")
    public String index(HttpServletRequest request, HttpServletResponse response,
                        @MyRequestParam("name") String name) throws IOException {
        String res = userService.get(name);
        System.out.println(name + "=>" + res);
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(res);
        return "index";
    }
}

DispathServlet

public class MyDispatcherServlet extends HttpServlet {
    //類名列表
    private List<String> classNames = new ArrayList<String>();

    //ioc容器,Map<類名,類實例>
    private Map<String, Object> ioc = new HashMap<String, Object>();

    private List<Handler> handlerMapping = new ArrayList<Handler>();

    @Override
    public void init(ServletConfig config) throws ServletException {
        System.out.println("init...");

        //1.掃描包,將包內所有對象類名存儲到list中
        doScanner("com.panda.web");
        System.out.println("========第一步:掃描文件包==========");
        classNames.forEach(value -> System.out.println("保存類名:" + value));

        //2.IOC實現,單例模式,new出對象,放入Map中
        doInstance();
        System.out.println("========第二步:IOC==========");
        ioc.forEach((key, value) -> System.out.println("IOC名稱:" + key + ":" + value));

        //3.DI實現,依賴注入。實現註釋MyAutowired的類,進行賦值
        doAutoWired();

        //4.將requestMapping註釋的method方法,放入到Arraylist中
        initHandlerMapping();

    }

    private void doScanner(String packageName) {
        //1.獲取當前class的文件路徑下的com.panda.web下的文件路徑,
        URL resource =
                this.getClass().getClassLoader().getResource("/" + packageName.replaceAll("\\.", "/"));
        File classDir = new File(resource.getFile());
        for (File classFile : classDir.listFiles()) {
            if (classFile.isDirectory()) {
                //2.遞歸,如果遇到文件夾,遞歸掃描此文件夾下的class
                doScanner(packageName + "." + classFile.getName());
            } else {
                //3.文件,直接獲取文件名稱,存放到List中,形式爲com.panda.web.controller.UserController
                String className = (packageName + "." + classFile.getName()).replace(".class", "");
                classNames.add(className);
            }
        }
    }

    private void doInstance() {
        if (classNames.isEmpty()) {
            return;
        }
        try {
            //循環所有掃描出來的類名,將其實例化,並放入ioc容器中
            for (String className : classNames) {
                //類反射機制,獲取clazz
                Class<?> clazz = Class.forName(className);
                //如果有MyController註解,說明爲controller類,可初始化
                if (clazz.isAnnotationPresent(MyController.class)) {
                    //ioc的beanId的格式爲類名,且首字母小寫
                    String beanName = lowerFirstCase(clazz.getSimpleName());
                    ioc.put(beanName, clazz.newInstance());
                    //如果有MyService註解,說明爲service類,可初始化
                } else if (clazz.isAnnotationPresent(MyService.class)) {

                    //1.如果自己起了名字,優先使用自己的
                    //2.如果沒起名字,使用首字母小寫
                    //3.如果自動注入的爲接口,使用接口的全稱

                    MyService service = clazz.getAnnotation(MyService.class);
                    String beanName = service.value();
                    if ("".equals(beanName)) {
                        beanName = lowerFirstCase(clazz.getSimpleName());
                    }
                    Object instance = clazz.newInstance();
                    ioc.put(beanName, instance);
                    //service註解的時候,如實現接口時,再put一次
                    Class<?>[] interfaces = clazz.getInterfaces();
                    for (Class<?> i : interfaces) {
                        ioc.put(i.getName(), instance);
                    }
                } else {
                    //如無註解,無需初始化。
                    continue;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void doAutoWired() {
        if (ioc.isEmpty()) {
            return;
        }
        //注入就是對類中定義的一些屬性,進行賦值
        //不管是公有的還是私有的,都需要賦值
        for (Map.Entry<String, Object> entry : ioc.entrySet()) {
            //getDeclaredFields獲取類中,所有的屬性值
            Field[] fields = entry.getValue().getClass().getDeclaredFields();
            for (Field field : fields) {
                //如果屬性上,無註解,直接循環下一次。如有MyAutowired才進行操作
                if (!field.isAnnotationPresent(MyAutowired.class)) {
                    continue;
                }
                MyAutowired autowired = field.getAnnotation(MyAutowired.class);
                String beanName = autowired.value();
                if ("".equals(beanName)) {
                    //如果beanName是空,使用默認接口注入
                    beanName = field.getType().getName();
                }
                //開始賦值,setAccessible - 強制訪問
                field.setAccessible(true);

                //通過反射機制,強制賦值。將ioc初始化實例後的對象賦值到MyAutowired屬性值上
                try {
                    field.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();
            //如果類的註解不是MyController,則繼續循環,此方法只處理controller層
            if (!clazz.isAnnotationPresent(MyController.class)) {
                continue;
            }
            String baseUrl = "";
            //如果類註解有MyRequestMapping,說明定義了根路徑,將此路徑存儲起來
            if (clazz.isAnnotationPresent(MyRequestMapping.class)) {
                MyRequestMapping requestMapping = clazz.getAnnotation(MyRequestMapping.class);
                baseUrl = requestMapping.value();
            }
            //獲取controller類的所有方法,並變量
            Method[] methods = clazz.getMethods();
            for (Method method : methods) {
                //如方法無註解,則繼續循環,目的獲取到所有MyRequestMapping註解的方法
                if (!method.isAnnotationPresent(MyRequestMapping.class)) {
                    continue;
                }
                //獲取MyRequestMapping註解的路徑,並與根路徑進行拼接
                MyRequestMapping requestMapping = method.getAnnotation(MyRequestMapping.class);
                String url = (baseUrl + requestMapping.value()).replaceAll("/+", "/");
                Pattern pattern = Pattern.compile(url);
                //url和method的方法,封裝到handler類中
                handlerMapping.add(new Handler(pattern, entry.getValue(), method));
                System.out.println("mapped:" + url + "=>" + method);
            }
        }
    }


    private String lowerFirstCase(String str) {
        char[] chars = str.toCharArray();
        chars[0] += 32;
        return String.valueOf(chars);
    }


    //內部類
    private class Handler {
        protected Object controller;//保存controller的實例對象
        protected Method method;//保存controller類的method方法
        protected Pattern pattern;//url正則表達是
        protected Map<String, Integer> paramIndexMapping;//param註解的map存儲

        protected Handler(Pattern pattern, Object controller, Method method) {
            this.pattern = pattern;
            this.controller = controller;
            this.method = method;
            paramIndexMapping = new HashMap<String, Integer>();
            putParamIndexMapping(method);
        }

        private void putParamIndexMapping(Method method) {
            Annotation[][] pa = method.getParameterAnnotations();
            for (int i = 0; i < pa.length; i++) {
                for (Annotation a : pa[i]) {
                    if (a instanceof MyRequestParam) {
                        String paramName = ((MyRequestParam) a).value();
                        if (!"".equals(paramName)) {
                            paramIndexMapping.put(paramName, i);
                        }
                    }
                }
            }
            Class<?>[] paramTypes = method.getParameterTypes();
            for (int i = 0; i < paramTypes.length; i++) {
                Class<?> type = paramTypes[i];
                if (type == HttpServletRequest.class || type == HttpServletResponse.class) {
                    paramIndexMapping.put(type.getName(), i);
                }
            }
        }
    }


    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse res)
            throws ServletException, IOException {
        this.doPost(req, res);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse res)
            throws ServletException, IOException {
        doDispatcher(req, res);
    }

    public void doDispatcher(HttpServletRequest req, HttpServletResponse res) {
        try {
            //獲取request中的url訪問路徑和參數,從ArrayList的handlerMapping中查找。找到後,執行method方法
            Handler handler = getHandler(req);
            if (handler == null) {
                res.getWriter().write("404 not found.");
                return;
            }
            Class<?>[] paramTypes = handler.method.getParameterTypes();
            Object[] paramValues = new Object[paramTypes.length];
            //獲取訪問鏈接中的params,並對其循環
            Map<String, String[]> params = req.getParameterMap();
            for (Map.Entry<String, String[]> param : params.entrySet()) {
                String value = Arrays.toString(param.getValue()).replaceAll("\\[|\\]", "");
                if (!handler.paramIndexMapping.containsKey(param.getKey())) {
                    continue;
                }
                int index = handler.paramIndexMapping.get(param.getKey());
                paramValues[index] = convert(paramTypes[index], value);
            }
            int reqIndex = handler.paramIndexMapping.get(HttpServletRequest.class.getName());
            paramValues[reqIndex] = req;
            int resIndex = handler.paramIndexMapping.get(HttpServletResponse.class.getName());
            paramValues[resIndex] = res;
            //method方法執行
            handler.method.invoke(handler.controller, paramValues);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //通過request的url鏈接,尋找handler對象中是否有對應的method
    private Handler getHandler(HttpServletRequest req) {
        if (handlerMapping.isEmpty()) {
            return null;
        }
        String url = req.getRequestURI();
        String contextPath = req.getContextPath();
        url = url.replace(contextPath, "").replaceAll("/+", "/");
        for (Handler handler : handlerMapping) {
            Matcher matcher = handler.pattern.matcher(url);
            if (!matcher.matches()) {
                continue;
            }
            //url有對應的method,返回handler
            return handler;
        }
        //url無對應的method,返回null
        return null;
    }

    private Object convert(Class<?> type, String value) {
        if (Integer.class == type) {
            return Integer.valueOf(value);
        }
        return value;
    }
}

碼雲地址:[https://gitee.com/xumiaofeng/springmvc]

發佈了15 篇原創文章 · 獲贊 9 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章