自己动手写一个spring之MVC_1

写在前面

本文看下如何定义MVC部分内容。

1:正文

首先定义requestmapping注解,映射url->处理方法

java 复制代码
@Target(value={ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping {
    String value() default "";
}

简单期间,这里暂时只支持用在方法上,spring mvc是支持同时用在类和方法上的,知道即可。接着我们还要定义DispatcherServlet作为拦截web请求的入口(只保留核心代码)

java 复制代码
// com.hc.minispring.web.v2.DispatcherServlet
public class DispatcherServlet extends HttpServlet {
    
    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);

        sContextConfigLocation = config.getInitParameter("contextConfigLocation");

        URL xmlPath = null;
        try {
            xmlPath = this.getServletContext().getResource(sContextConfigLocation);
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
        // 获取所有要扫描的包
        this.packageNames = XmlScanComponentHelper.getNodeValue(xmlPath);
        // 刷新bean
        Refresh();

    }

    protected void Refresh() {
        // 扫描包,获取所有的controller 实例
        initController();
        // 初始化url和对应method的映射
        initMapping();
    }

    protected void initController() {
        this.controllerNames = scanPackages(this.packageNames);

        for (String controllerName : this.controllerNames) {
            Object obj = null;
            Class<?> clz = null;

            try {
                clz = Class.forName(controllerName);
                this.controllerClasses.put(controllerName,clz);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            try {
                obj = clz.newInstance();
                this.controllerObjs.put(controllerName, obj);
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }
    
    protected void initMapping() {
        for (String controllerName : this.controllerNames) {
            // ...
            if(methods!=null){
                for(Method method : methods){
                    // 判断方法上是否使用了@RequestMapping注解,使用了则获取映射的url,以及对应的方法,存储备用
                    boolean isRequestMapping = method.isAnnotationPresent(RequestMapping.class);
                    if (isRequestMapping){
                        String methodName = method.getName();
                        String urlmapping = method.getAnnotation(RequestMapping.class).value();
                        // ...
                        // 存储请求web路径和处理该web路径的方法,将会在doGet方法中使用
                        this.mappingMethods.put(urlmapping, method);
                    }
                }
            }
        }
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String sPath = request.getServletPath();
       // ...
        try {
            // 根据请求路径获取处理方法,反射执行,并返回结果
            Method method = this.mappingMethods.get(sPath);
            obj = this.mappingObjs.get(sPath);
            objResult = method.invoke(obj);
        } catch (SecurityException e) {
            // ...
        }

        response.getWriter().append(objResult.toString());
    }
}

配置component-scan的配置文件如下:

xml 复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<components>
    <component-scan base-package="com.hc.minispring.web.v2.test" />
</components>

在com.hc.minispring.web.v2.test包中我配置了两个类:

java 复制代码
public class HelloWorldBean {
  @RequestMapping("/test")
  public String doTest() {
    return "hello world for doGet!";
  }
}

public class HelloWorldBean1 {
    @RequestMapping("/test1")
    public String doTest() {
        return "1hello world for doGet!";
    }
}

最后配置下web.xml就行了:

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns:web="http://xmlns.jcp.org/xml/ns/javaee"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID">
    <servlet>
        <servlet-name>minisMVC</servlet-name>
        <servlet-class>com.hc.minispring.web.v2.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/v2/minisMVC-servletv2.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>minisMVC</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

启动后访问:

复制代码
C:\Users\dell9020>curl http://localhost:8080/minispring_Web_exploded/test
hello world for doGet!
C:\Users\dell9020>curl http://localhost:8080/minispring_Web_exploded/test1
1hello world for doGet!
C:\Users\dell9020>

这样一个简单的mini springmvc就完成了。这样,应用程序开发人员就不需要关心servlet啥的了,只需要提供一个类似于/WEB-INF/v2/minisMVC-servletv2.xml的配置文件,然后在自定义的component scan路径下写对应的业务代码就可以了。

写在后面

参考文章列表

手把手带你写一个 MiniSpring