手写SpringMVC框架

711 阅读9分钟

前面手写过MyBatis、Spring简易框架,今天就来写一下SpringMVC简易版框架。所谓简易版,就是肯定不像源码那些封装完整,就是实现一下基本功能,帮助更好理解底层原理

介绍

Spring MVC 是 Spring 提供的一个基于 MVC 设计模式的轻量级 Web 开发框架,本质上相当于 Servlet。

Spring MVC 角色划分清晰,分工明细。由于 Spring MVC 本身就是 Spring 框架的一部分,可以说和 Spring 框架是无缝集成。性能方面具有先天的优越性,是当今业界最主流的 Web 开发框架,最热门的开发技能。

仓库地址: gitee.com/uuuwhh/Spri…

开始实现

整体架构

mtbatis类结构.png

新建web工程

引入依赖pom.xml

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.springmvc</groupId>
  <artifactId>SpringMVC-Write</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>SpringMVC-Write Maven Webapp</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.13.2</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
    </dependency>
    <dependency>
      <groupId>dom4j</groupId>
      <artifactId>dom4j</artifactId>
      <version>1.1</version>
    </dependency>
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
      <version>3.12.0</version>
    </dependency>
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>RELEASE</version>
      <scope>compile</scope>
    </dependency>

    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.12.5</version>
    </dependency>
  </dependencies>

  <build>
    <finalName>SpringMVC-Write</finalName>
    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
      <plugins>
        <plugin>
          <artifactId>maven-clean-plugin</artifactId>
          <version>3.1.0</version>
        </plugin>
        <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
        <plugin>
          <artifactId>maven-resources-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.8.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>2.22.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-war-plugin</artifactId>
          <version>3.2.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-install-plugin</artifactId>
          <version>2.5.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-deploy-plugin</artifactId>
          <version>2.8.2</version>
        </plugin>
      </plugins>
    </pluginManagement>
      <plugins>
          <plugin>
              <groupId>org.apache.maven.plugins</groupId>
              <artifactId>maven-compiler-plugin</artifactId>
              <configuration>
                  <source>8</source>
                  <target>8</target>
              </configuration>
          </plugin>
      </plugins>
  </build>
</project>

web.xml

<!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>

  <!--springmvc的核心控制器-->
  <servlet>
    <servlet-name>DispatcherServlet</servlet-name>
    <servlet-class>com.springmvc.servlet.DispatcherServlet</servlet-class>
    <!--springmvc的配置文件-->
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:springmvc.xml</param-value>
    </init-param>
    <!--web服务器一旦启动,Servlet就会实例化创建对象,进行初始化(预备创建对象)-->
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>DispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>

</web-app>

springmvc.xml

<?xml version="1.0" encoding="utf-8" ?>
<beans>
    <!-- 加载控制器所在的包 -->
    <component-scan base-package="com.ithong.controller,com.ithong.service"></component-scan>
</beans>

自定义异常类

package com.springmvc.exception;

public class ContextException extends RuntimeException{
    public ContextException(String message) {
        super(message);
    }

    public ContextException(Throwable cause) {
        super(cause);
    }

    protected ContextException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }

    @Override
    public String getMessage() {
        return super.getMessage();
    }
}

配置SpringMVC核心的控制器

swevlet容器需要在应用项目启动时,给应用项目初始化一个ServletContext作为公共环境容器存放公共信息,ServletContext中的信息都是由容器提供的。 WebApplicationContext中包含contextConfigLocationioc容器

ublic class WebApplicationContext {

    // classpath:springmvc.xml
    String contextConfigLocation;

    List<String> classNameList = new ArrayList<String>();

    // Spring的IOC容器
    public Map<String,Object> iocMap = new ConcurrentHashMap<String,Object>();

    public WebApplicationContext(String contextConfigLocation) {
        this.contextConfigLocation = contextConfigLocation;
    }
}

SpringMVC的核心就是DispatcherServletDispatcherServlet实质也是一个HttpServletDispatcherSevlet负责将请求分发,所有的请求都有经过它来统一分发。 SpringMVC请求处理流程如下

mvc.png

用户向服务器发送请求,请求会到DispatcherServletDispatcherServlet 对请求URL进行解析,得到请求资源标识符(URI),然后根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括一个Handler处理器对象、多个HandlerInterceptor拦截器对象),最后以HandlerExecutionChain对象的形式返回。
DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter。提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)。 在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:

  • HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息
  • 数据转换:对请求消息进行数据转换。如String转换成Integer、Double等
  • 数据格式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等
  • 数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中 DispatcherServlet DispatcherServlet类的主要职责有四个
  • 1、Servlet初始化的时候,读取初始化的参数 classpath:springmvc.xml
  • 2、创建Spring容器
  • 3、初始化Spring容器
  • 4、初始化请求映射 /user/query ----> Controller ----> method ----> paramter
public class DispatcherServlet extends HttpServlet {

    private WebApplicationContext webApplicationContext;

    // 存储URL和对象的方法映射关系
    List<MyHandler> handlerList = new ArrayList<MyHandler>();

    @Override
    public void init() throws ServletException {
        // 1.Servlet初始化的时候,读取初始化的参数 classpath:springmvc.xml
        String contextConfigLocation = this.getServletConfig().getInitParameter("contextConfigLocation");
        // 2.创建Spring容器
        webApplicationContext = new WebApplicationContext(contextConfigLocation);
        // 3.初始化Spring容器
        webApplicationContext.refresh();
        // 4、初始化请求映射 /user/query  ----> Controller ----> method ----> paramter
        initHandleMapping();
        System.out.println("请求地址和控制器方法映射关系"+handlerList);
    }
}

业务层和控制层

UserController

package com.ithong.controller;

import com.ithong.entity.User;
import com.ithong.service.UserService;
import com.springmvc.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;

@Controller
public class UserController {

    @AutoWired
    UserService userService;

    @RequestMapping("/user/query")
    public String findUsers(HttpServletRequest request, HttpServletResponse response,@RequestParam("name") String name){
        response.setContentType("text/html;charset=utf-8");
//        try {
//            List<User> users = userService.findUsers(name);
//            PrintWriter out = response.getWriter();
//            out.print("<h1>SpringMvc控制器:"+name+"</h1>");
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
        String userMessage = userService.getUserMessage(name);
        request.setAttribute("userMessage",userMessage);
        // 转发到user.jsp SpringMvc默认就是转发
        return "forward:/user.jsp";
    }

    @RequestMapping("/user/queryjson")
    @ResponseBoby // 返回值的自动转为JSON数据
    public List<User> queryUsers(HttpServletRequest request,HttpServletResponse response,String name){
        return userService.findUsers(name);
    }
}

实体类User

package com.ithong.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {

    private Integer id;
    private String name;
    private String pass;
}

接口UserService

package com.ithong.service;

import com.ithong.entity.User;

import java.util.List;

public interface UserService {

    public List<User> findUsers(String name);

    public String getUserMessage(String name);
}

UserServiceImpl

package com.ithong.service.impl;

import com.ithong.entity.User;
import com.ithong.service.UserService;
import com.springmvc.annotation.Service;

import java.util.ArrayList;
import java.util.List;

@Service
public class UserServiceImpl implements UserService {
    @Override
    public List<User> findUsers(String name) {
        List<User> users = new ArrayList<>();
        users.add(new User(1,"老王","123"));
        users.add(new User(2,"小王","123"));
        return users;
    }

    @Override
    public String getUserMessage(String name) {
        return "我是getUserMessage方法"+name;
    }
}

自定义注解

AutoWired

package com.springmvc.annotation;

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented
public @interface AutoWired {

    String value() default "";
}

Controller

package com.springmvc.annotation;

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface Controller {

}

RequestMapping

package com.springmvc.annotation;

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface RequestMapping {

    String value() default "";
}

RequestParam

package com.springmvc.annotation;

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
@Documented
public @interface RequestParam {

    String value() default "";
}

ResponseBoby

package com.springmvc.annotation;

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface ResponseBoby {
}

Service

package com.springmvc.annotation;

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface Service {

    String value() default "";
}

创建spring容器

Dom4j解析springmvc

就是拿到springmvc.xml里的component-scanbase-package属性的值,就是com.ithong.controller,com.ithong.service

XmlPaser

package com.springmvc.xml;


import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.InputStream;
import java.net.URL;

public class XmlPaser {

    public static String getbasePackage(String xml) {
        try {
            SAXReader saxReader = new SAXReader();
            InputStream inputStream = XmlPaser.class.getClassLoader().getResourceAsStream(xml);
            // XML文档对象
            Document document = saxReader.read(inputStream);
            Element rootElement = document.getRootElement();
            Element componentScan = rootElement.element("component-scan");
            Attribute attribute = componentScan.attribute("base-package");
            String basePackage = attribute.getText();
            return basePackage;
        } catch (DocumentException e) {
            e.printStackTrace();
        }
        return "";
    }
}

扫描SpringMvc中的类

根据上面拿到的值去遍历文件查找类

mvc2.png

/**
 * 扫描包
 */
public void excuteScanPackage(String pack){
    URL url = this.getClass().getClassLoader().getResource("/" + pack.replaceAll("\.", "/"));
    String path = url.getFile();
    File dir = new File(path);
    for (File f : dir.listFiles()) {
        if (f.isDirectory()){
            // 当前是一个文件目录 com.ithong.service.impl
            excuteScanPackage(pack+"."+f.getName());
        }else {
            // 文件目录
            String className = pack + "." + f.getName().replaceAll(".class","");
            classNameList.add(className);
        }
    }
}

执行结果下下

扫描之后的内容是:[com.ithong.controller.UserController, com.ithong.service.impl.UserServiceImpl, com.ithong.service.UserService]

实例化Spring容器中对象并放入IOC容器

当然,SpringIOC容器比这复杂点,这里是直接通过反射把类上有Service、Controller注解的类实例放入IOC容器 执行结果如下SpringIoC容器中对象{userController=com.ithong.controller.UserController@5c344a6f, userService=com.ithong.service.impl.UserServiceImpl@6ee6dab9}

/**
 * 实例化Spring容器中bean对象
 */
private void excuteInstance() {
    if (classNameList.size() == 0){
        // 没有扫描到需要实例化的类
        throw new ContextException("没有要实例化的class");
    }
    try {
        for (String className : classNameList) {
            Class<?> clazz = Class.forName(className);
            if (clazz.isAnnotationPresent(Controller.class)){
                // 控制层的类 com.ithong.controller.UserController
                // userController 控制层对象的名字
                String beanName = clazz.getSimpleName().substring(0,1).toLowerCase() + clazz.getSimpleName().substring(1);
                iocMap.put(beanName,clazz.newInstance());
            }else if (clazz.isAnnotationPresent(Service.class)){
                // com.ithong.service.impl.UserServiceImpl
                Service serviceAnnotation = clazz.getAnnotation(Service.class);
                String beanName = serviceAnnotation.value();
                if ("".equals(beanName)){
                    Class<?>[] interfaces = clazz.getInterfaces();
                    for (Class<?> anInterface : interfaces) {
                        String beanName1 = anInterface.getSimpleName().substring(0,1).toLowerCase() + anInterface.getSimpleName().substring(1);
                        iocMap.put(beanName1,clazz.newInstance());
                    }
                }else {
                    iocMap.put(beanName,clazz.newInstance());
                }
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

实现Spring容器中对象的注入

实际就是扫描类的成员变量有没有AutoWired注解需要依赖注入的

/**
 * 实现Spring容器中对象依赖注入
 */
private void executeAutoWired() {
    try {
        if (iocMap.isEmpty()){
            throw new ContextException("没有要初始化的bean对象");
        }
        for (Map.Entry<String, Object> entry : iocMap.entrySet()) {
            String key = entry.getKey();
            Object bean = entry.getValue();
            Field[] fields = bean.getClass().getDeclaredFields();
            for (Field field : fields) {
                AutoWired autoWiredAnnotation = field.getAnnotation(AutoWired.class);
                String beanName = autoWiredAnnotation.value();
                if ("".equals(beanName)){
                    Class<?> type = field.getType();
                    beanName = type.getSimpleName().substring(0,1).toLowerCase() + type.getSimpleName().substring(1);
                }
                field.setAccessible(true);
                // 属性注入 调用反射 给属性赋值
                field.set(bean,iocMap.get(beanName));
            }
        }
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
}

初始化请求映射

通过对请求的处理,将控制层@RequestMapping注解的路径地址与对应类和方法一一映射。
首先建立一个实例对象将映射属性存入 MyHandler

package com.springmvc.handler;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.lang.reflect.Method;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class MyHandler {

    private String url;
    private Object controller;
    private Method method;
}

建立一个集合存储URL和对象的方法映射关系实例

// 存储URL和对象的方法映射关系
List<MyHandler> handlerList = new ArrayList<MyHandler>();

通过扫描IOC容器中带有RequestMapping注解方法

/**
 * 初始化请求映射
 */
private void initHandleMapping() {
    // 判断IOCmap中是否有bean对象
    if (webApplicationContext.iocMap.isEmpty()){
        throw new ContextException("Spring容器为空");
    }
    for (Map.Entry<String, Object> entry : webApplicationContext.iocMap.entrySet()) {
        Class<?> clazz = entry.getValue().getClass();
        if (clazz.isAnnotationPresent(Controller.class)){
            Method[] declaredMethods = clazz.getDeclaredMethods();
            for (Method declaredMethod : declaredMethods) {
               if (declaredMethod.isAnnotationPresent(RequestMapping.class)){
                   RequestMapping requestMappingAnnotation = declaredMethod.getAnnotation(RequestMapping.class);
                   // /user/query
                   String url = requestMappingAnnotation.value();
                   MyHandler handler = new MyHandler(url, entry.getValue(), declaredMethod);
                   handlerList.add(handler);
               }
            }
        }
    }
}

反射控制器方法名

public List<String> getParameterName(Method method){
    List<String> list = new ArrayList<>();
    Parameter[] parameters = method.getParameters();
    for (Parameter parameter : parameters) {
        list.add(parameter.getName());
    }
    return list;
}

分发用户请求、控制器方法接收参数

当用户请求数据时,调用DispatcherServletdoPost方法,在该方法内对请求的分发进行处理。
根据请求地址获取Handler对象

public MyHandler getHandler(HttpServletRequest req) {
    // /user/query
    String requestURI = req.getRequestURI();
    for (MyHandler myHandler : handlerList) {
        if (myHandler.getUrl().equals(requestURI)) {
            return myHandler;
        }
    }
    return null;
}

此处参数传值方法并不优雅,可优化之处

根据@RequestParam注解在参数上的位置拿到值,反射调用方法后可通过返回值结果。

/**
 * 判断控制器方法的参数是否有requestParam注解,且找到对应value值 如果找到 返回
 * @param method
 * @param name
 * @return
 */
public int hasRequestParam(Method method,String name) {
    Parameter[] parameters = method.getParameters();
    for (int i = 0; i < parameters.length; i++) {
        Parameter p = parameters[i];
        boolean b = p.isAnnotationPresent(RequestParam.class);
        if (b) {
            RequestParam requestParam = p.getAnnotation(RequestParam.class);
            String requestParamValue = requestParam.value();
            if (name.equals(requestParamValue)) {
                return i;
            }
        }
    }
    return -1;
}

分发过程中通过反射调用方法返回类型或@RequestBoby注解判断返回数据格式

此处也可以配置视图解析器返回页面

// 请求分发 处理
private void executeDispatch(HttpServletRequest req, HttpServletResponse resp) {
    MyHandler handler = getHandler(req);
    try {
        if (handler == null){
            resp.getWriter().print("<h1>404 NOT FOUND!</h1>");
        }else {
            Class<?>[] parameterTypes = handler.getMethod().getParameterTypes();
            // 定义一个参数的数组
            Object[] params = new Object[parameterTypes.length];
            for (int i = 0; i < parameterTypes.length; i++) {
                Class<?> parameterType = parameterTypes[i];
                if ("HttpServletRequest".equals(parameterType.getSimpleName())) {
                    params[i] = req;
                }else if ("HttpServletResponse".equals(parameterType.getSimpleName())){
                    params[i] = resp;
                }
            }
            // 获取请求中的参数集合
            Map<String, String[]> parameterMap = req.getParameterMap();
            for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
                String name = entry.getKey();
                Object value = entry.getValue()[0];
                System.out.println("请求参数:"+name+"-----"+value);
                int index = hasRequestParam(handler.getMethod(), name);
                if (index != -1) {
                    params[index] = value;
                }else {
                    List<String> names = getParameterName(handler.getMethod());
                    System.out.println(names);
                }
                // 后期有待优化
                params[2] = value;
            }

            // 调用控制中的方法
            Object result = handler.getMethod().invoke(handler.getController(), parameterTypes);
            if (result instanceof String){
                // 跳转到jsp
                String viewName = (String) result;
                if (viewName.contains(":")){
                    // "forward:/user.jsp"
                    String viewType = viewName.split(":")[0];
                    String viewPage = viewName.split(":")[1];
                    if (viewType.equals("")){
                        req.getRequestDispatcher(viewPage).forward(req,resp);
                    }else {
                        resp.sendRedirect(viewPage);
                    }
                }else {
                    // 默认就转发
                    req.getRequestDispatcher(viewName).forward(req,resp);
                }
            }else {
                // 返回JSON数据
                Method method = handler.getMethod();
                if (method.isAnnotationPresent(ResponseBoby.class)){
                    // 把返回值调用JSON转换工具转为JSON字符串
                    ObjectMapper mapper = new ObjectMapper();
                    String json = mapper.writeValueAsString(result);
                    resp.setContentType("text/html;charset=utf-8");
                    PrintWriter out = resp.getWriter();
                    out.print(json);
                    out.flush();
                    out.close();
                }
            }
        }

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

user.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
    <title>User.jsp</title>
</head>
<body>
    <h1>${requestScope.userMessage}</h1>
</body>
</html>

结果如下图,至此SpringMvc简易框架功能已全部实现

mvc3.png 最后贴一下核心控制器DispatcherServletWebApplicationContext完整代码 DispatcherServlet

package com.springmvc.servlet;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.springmvc.annotation.Controller;
import com.springmvc.annotation.RequestMapping;
import com.springmvc.annotation.RequestParam;
import com.springmvc.annotation.ResponseBoby;
import com.springmvc.context.WebApplicationContext;
import com.springmvc.exception.ContextException;
import com.springmvc.handler.MyHandler;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * SpringMvc核心控制器
 */
public class DispatcherServlet extends HttpServlet {

    private WebApplicationContext webApplicationContext;

    // 存储URL和对象的方法映射关系
    List<MyHandler> handlerList = new ArrayList<MyHandler>();

    @Override
    public void init() throws ServletException {
        // 1.Servlet初始化的时候,读取初始化的参数 classpath:springmvc.xml
        String contextConfigLocation = this.getServletConfig().getInitParameter("contextConfigLocation");
        // 2.创建Spring容器
        webApplicationContext = new WebApplicationContext(contextConfigLocation);
        // 3.初始化Spring容器
        webApplicationContext.refresh();
        // 4、初始化请求映射 /user/query  ----> Controller ----> method ----> paramter
        initHandleMapping();
        System.out.println("请求地址和控制器方法映射关系"+handlerList);
    }

    /**
     * 初始化请求映射
     */
    private void initHandleMapping() {
        // 判断IOCmap中是否有bean对象
        if (webApplicationContext.iocMap.isEmpty()){
            throw new ContextException("Spring容器为空");
        }
        for (Map.Entry<String, Object> entry : webApplicationContext.iocMap.entrySet()) {
            Class<?> clazz = entry.getValue().getClass();
            if (clazz.isAnnotationPresent(Controller.class)){
                Method[] declaredMethods = clazz.getDeclaredMethods();
                for (Method declaredMethod : declaredMethods) {
                   if (declaredMethod.isAnnotationPresent(RequestMapping.class)){
                       RequestMapping requestMappingAnnotation = declaredMethod.getAnnotation(RequestMapping.class);
                       // /user/query
                       String url = requestMappingAnnotation.value();
                       MyHandler handler = new MyHandler(url, entry.getValue(), declaredMethod);
                       handlerList.add(handler);
                   }
                }
            }
        }
    }

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

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 请求的分发处理
        executeDispatch(req,resp);
    }

    // 请求分发 处理
    private void executeDispatch(HttpServletRequest req, HttpServletResponse resp) {
        MyHandler handler = getHandler(req);
        try {
            if (handler == null){
                resp.getWriter().print("<h1>404 NOT FOUND!</h1>");
            }else {
                Class<?>[] parameterTypes = handler.getMethod().getParameterTypes();
                // 定义一个参数的数组
                Object[] params = new Object[parameterTypes.length];
                for (int i = 0; i < parameterTypes.length; i++) {
                    Class<?> parameterType = parameterTypes[i];
                    if ("HttpServletRequest".equals(parameterType.getSimpleName())) {
                        params[i] = req;
                    }else if ("HttpServletResponse".equals(parameterType.getSimpleName())){
                        params[i] = resp;
                    }
                }
                // 获取请求中的参数集合
                Map<String, String[]> parameterMap = req.getParameterMap();
                for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
                    String name = entry.getKey();
                    Object value = entry.getValue()[0];
                    System.out.println("请求参数:"+name+"-----"+value);
                    int index = hasRequestParam(handler.getMethod(), name);
                    if (index != -1) {
                        params[index] = value;
                    }else {
                        List<String> names = getParameterName(handler.getMethod());
                        System.out.println(names);
                    }
                    // 后期有待优化
                    params[2] = value;
                }

                // 调用控制中的方法
                Object result = handler.getMethod().invoke(handler.getController(), parameterTypes);
                if (result instanceof String){
                    // 跳转到jsp
                    String viewName = (String) result;
                    if (viewName.contains(":")){
                        // "forward:/user.jsp"
                        String viewType = viewName.split(":")[0];
                        String viewPage = viewName.split(":")[1];
                        if (viewType.equals("")){
                            req.getRequestDispatcher(viewPage).forward(req,resp);
                        }else {
                            resp.sendRedirect(viewPage);
                        }
                    }else {
                        // 默认就转发
                        req.getRequestDispatcher(viewName).forward(req,resp);
                    }
                }else {
                    // 返回JSON数据
                    Method method = handler.getMethod();
                    if (method.isAnnotationPresent(ResponseBoby.class)){
                        // 把返回值调用JSON转换工具转为JSON字符串
                        ObjectMapper mapper = new ObjectMapper();
                        String json = mapper.writeValueAsString(result);
                        resp.setContentType("text/html;charset=utf-8");
                        PrintWriter out = resp.getWriter();
                        out.print(json);
                        out.flush();
                        out.close();
                    }
                }
            }

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

    /**
     * 根据请求地址获取Handler对象
     * @param req
     * @return
     */
    public MyHandler getHandler(HttpServletRequest req) {
        // /user/query
        String requestURI = req.getRequestURI();
        for (MyHandler myHandler : handlerList) {
            if (myHandler.getUrl().equals(requestURI)) {
                return myHandler;
            }
        }
        return null;
    }

    /**
     * 判断控制器方法的参数是否有requestParam注解,且找到对应value值 如果找到 返回
     * @param method
     * @param name
     * @return
     */
    public int hasRequestParam(Method method,String name) {
        Parameter[] parameters = method.getParameters();
        for (int i = 0; i < parameters.length; i++) {
            Parameter p = parameters[i];
            boolean b = p.isAnnotationPresent(RequestParam.class);
            if (b) {
                RequestParam requestParam = p.getAnnotation(RequestParam.class);
                String requestParamValue = requestParam.value();
                if (name.equals(requestParamValue)) {
                    return i;
                }
            }
        }
        return -1;
    }

    // 反射控制器方法名
    public List<String> getParameterName(Method method){
        List<String> list = new ArrayList<>();
        Parameter[] parameters = method.getParameters();
        for (Parameter parameter : parameters) {
            list.add(parameter.getName());
        }
        return list;
    }

}

WebApplicationContext

package com.springmvc.context;

import com.springmvc.annotation.AutoWired;
import com.springmvc.annotation.Controller;
import com.springmvc.annotation.Service;
import com.springmvc.exception.ContextException;
import com.springmvc.xml.XmlPaser;

import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class WebApplicationContext {

    // classpath:springmvc.xml
    String contextConfigLocation;

    List<String> classNameList = new ArrayList<String>();

    // Spring的IOC容器
    public Map<String,Object> iocMap = new ConcurrentHashMap<String,Object>();

    public WebApplicationContext(String contextConfigLocation) {
        this.contextConfigLocation = contextConfigLocation;
    }

    /**
     * 初始化Spring容器
     */
    public void refresh(){
        // 1.解析springmvc.xml DOM4J springmvc.xml
        String basePackage = XmlPaser.getbasePackage(contextConfigLocation.split(":")[1]);
        String[] basePackages = basePackage.split(",");
        if (basePackages.length > 0){
            for (String pack : basePackages) {
                // com.ithong.controller  com.ithong.service ---> /com/ithong/controller
                excuteScanPackage(pack);
            }
        }
        // com.ithong.controller.UserController, com.ithong.service.impl.UserServiceImpl, com.ithong.service.UserService
        System.out.println("扫描之后的内容是:"+classNameList);
        // 实例化Spring容器中的bean
        excuteInstance();
        // IOC容器中对象
        System.out.println("SpringIoC容器中对象"+iocMap);
        // 实现Spring容器中对象的注入
        executeAutoWired();

    }

    /**
     * 实现Spring容器中对象依赖注入
     */
    private void executeAutoWired() {
        try {
            if (iocMap.isEmpty()){
                throw new ContextException("没有要初始化的bean对象");
            }
            for (Map.Entry<String, Object> entry : iocMap.entrySet()) {
                String key = entry.getKey();
                Object bean = entry.getValue();
                Field[] fields = bean.getClass().getDeclaredFields();
                for (Field field : fields) {
                    AutoWired autoWiredAnnotation = field.getAnnotation(AutoWired.class);
                    String beanName = autoWiredAnnotation.value();
                    if ("".equals(beanName)){
                        Class<?> type = field.getType();
                        beanName = type.getSimpleName().substring(0,1).toLowerCase() + type.getSimpleName().substring(1);
                    }
                    field.setAccessible(true);
                    // 属性注入 调用反射 给属性赋值
                    field.set(bean,iocMap.get(beanName));
                }
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    /**
     * 实例化Spring容器中bean对象
     */
    private void excuteInstance() {
        if (classNameList.size() == 0){
            // 没有扫描到需要实例化的类
            throw new ContextException("没有要实例化的class");
        }
        try {
            for (String className : classNameList) {
                Class<?> clazz = Class.forName(className);
                if (clazz.isAnnotationPresent(Controller.class)){
                    // 控制层的类 com.ithong.controller.UserController
                    // userController 控制层对象的名字
                    String beanName = clazz.getSimpleName().substring(0,1).toLowerCase() + clazz.getSimpleName().substring(1);
                    iocMap.put(beanName,clazz.newInstance());
                }else if (clazz.isAnnotationPresent(Service.class)){
                    // com.ithong.service.impl.UserServiceImpl
                    Service serviceAnnotation = clazz.getAnnotation(Service.class);
                    String beanName = serviceAnnotation.value();
                    if ("".equals(beanName)){
                        Class<?>[] interfaces = clazz.getInterfaces();
                        for (Class<?> anInterface : interfaces) {
                            String beanName1 = anInterface.getSimpleName().substring(0,1).toLowerCase() + anInterface.getSimpleName().substring(1);
                            iocMap.put(beanName1,clazz.newInstance());
                        }
                    }else {
                        iocMap.put(beanName,clazz.newInstance());
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 扫描包
     */
    public void excuteScanPackage(String pack){
        URL url = this.getClass().getClassLoader().getResource("/" + pack.replaceAll("\.", "/"));
        String path = url.getFile();
        File dir = new File(path);
        for (File f : dir.listFiles()) {
            if (f.isDirectory()){
                // 当前是一个文件目录 com.ithong.service.impl
                excuteScanPackage(pack+"."+f.getName());
            }else {
                // 文件目录
                String className = pack + "." + f.getName().replaceAll(".class","");
                classNameList.add(className);
            }
        }
    }
}