手写springmvc

 SpringMVC框架对于Java后端程序员来说再熟悉不过了,但是也只是熟悉而已,用多了就熟了,但是有多少人去了解过他的原理,其实很多人会说,我会用不就行了吗,了解原理有什么用了,但当你了解之后才知道有很多巧妙的设计在里面。如果不看Spring的源码,你将会失去一次和大师学习的机会,它的代码规范,设计思想很值得学习。会用,你永远只是一个码农,深入原理你才能成为大师。

       废话不多说,我们进入今天的正题,用过springMVC的人,应该都知道SpringMVC以DispatcherServlet为核心,负责协调和组织不同组件以完成请求处理并返回响应的工作,如果连这个都不知道,那你真该好好的反思下自己了,好好的补补课了。所以想要实现自己的SpringMVC框架,需要从DispatcherServlet开始:

     先上一张springMVC的原理图吧(网上找的)

参照上面的图,说下springMVC的工作流程

1、用户发送请求至前端控制器DispatcherServlet

2、 DispatcherServlet收到请求调用HandlerMapping处理器映射器。

3、处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。

4、 DispatcherServlet通过HandlerAdapter处理器适配器调用处理器

5、执行处理器(Controller,也叫后端控制器)。

6、Controller执行完成返回ModelAndView

7、 HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet

8、DispatcherServlet将ModelAndView传给ViewReslover视图解析器

9、ViewReslover解析后返回具体View

10、DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)。

11、DispatcherServlet响应用户。

上面只是我个人的看法,但是实际上是不是这样的呢,读读源码就知道了,源码在哪里,从哪里看起,前面说了,核心在DispatcherServlet,废话跳过,直接上图,本文是手写系列,所以这里就不深入的讲了,看完了这篇博客,大家在反过手来看这个应该就容易多了。

我这里只实现自己的@Controller、@RequestMapping、@RequestParam注解起作用,其余SpringMVC功能照葫芦画瓢即可。

目录结构如下

下面到上源码部分了:

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

    String value() default "";

}

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyRequestMapping {

    String value();
}
 

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

    String value();
}


@MyController
@MyRequestMapping("/test")
public class TestController {

    @MyRequestMapping("/hello")
    public String Hello(String name) {
        return "hello: " + name;
    }
}
 


@SuppressWarnings("serial")
public class MyDispatcherServlet extends HttpServlet {

    private Properties properties = new Properties();
    private List<String> classNames = new ArrayList<>();
    private Map<String, Object> ioc = new HashMap<>();
    private Map<String, Method> handMapping = new HashMap<>();
    private Map<String, Object> controllerMapping = new HashMap<>();

    @Override
    public void init(ServletConfig config) throws ServletException {

        // 加载配置文件
        try {
            load(config);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        // 初始化相关类
        doScanner(properties.getProperty("scanPackage"));

        // 实例化
        doInstance();

        // 初始化handMapping
        initHandMapping();

    }

    private void load(ServletConfig config) throws Exception {
        String location = config.getInitParameter("contextConfigLocation");
        InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(location);
        properties.load(inputStream);
    }

    private void doScanner(String scanPackage) {
        URL url = this.getClass().getClassLoader().getResource("/" + scanPackage.replace(".", "/"));
        File dir = new File(url.getFile());
        for (File file : dir.listFiles()) {
            if (file.isDirectory()) {
                // 递归读取包
                doScanner(scanPackage + "." + file.getName());
            } else {
                String className = scanPackage + "." + file.getName().replace(".class", "");
                classNames.add(className);
            }
        }
    }

    private void doInstance() {
        if (classNames == null || classNames.size() == 0)
            return;
        for (String className : classNames) {
            try {
                Class<?> clazz = Class.forName(className);
                if (clazz.isAnnotationPresent(MyController.class)) {
                    ioc.put(toLowerFirstWord(clazz.getSimpleName()), clazz.newInstance());
                }
            } catch (ClassNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (InstantiationException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

    private String toLowerFirstWord(String name) {
        char[] charArray = name.toCharArray();
        charArray[0] += 32;
        return String.valueOf(charArray);
    }

    private void initHandMapping() {
        if (classNames != null && classNames.size() != 0) {
            for (String className : classNames) {
                try {
                    Class<?> clazz = Class.forName(className);
                    String controllUrl = "";
                    if (clazz.isAnnotationPresent(MyController.class)) {
                        if (clazz.isAnnotationPresent(MyRequestMapping.class)) {
                            MyRequestMapping myRequestMapping = clazz.getAnnotation(MyRequestMapping.class);
                            controllUrl = myRequestMapping.value();
                        }
                        Method[] methods = clazz.getMethods();
                        if (methods != null && methods.length != 0) {
                            for (Method method : methods) {
                                if (method.isAnnotationPresent(MyRequestMapping.class)) {
                                    String methodUrl = method.getAnnotation(MyRequestMapping.class).value();
                                    String url = (controllUrl + "/" + methodUrl).replaceAll("/+", "/");
                                    handMapping.put(url, method);
                                    controllerMapping.put(url, ioc.get(toLowerFirstWord(clazz.getSimpleName())));
                                }
                            }
                        }
                    }
                } catch (ClassNotFoundException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }

            }
        }

    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // TODO Auto-generated method stub
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // TODO Auto-generated method stub
        try {
            doDispatch(req, resp);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            resp.getWriter().write(e.getMessage());
        }
    }

    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        if (handMapping != null && !handMapping.isEmpty()) {
            String url = req.getRequestURI();
            String contextPath = req.getContextPath();
            url = url.replace(contextPath, "").replaceAll("/+", "/");
            if (!this.handMapping.containsKey(url)) {
                resp.getWriter().write("404 NOT FOUND!");
                return;
            } else {
                Method method = handMapping.get(url);
                Object instance = controllerMapping.get(url);
                // req.getp
                String retValue = method.invoke(instance, getMethodParams(method, req, resp)).toString();
                resp.getWriter().write(retValue);
            }
        }
    }

    private Object[] getMethodParams(Method method, HttpServletRequest req, HttpServletResponse resp) {
        // 获取方法的参数列表
        Class<?>[] parameterTypes = method.getParameterTypes();
        // 获取请求的参数
        Map<String, String[]> parameterMap = req.getParameterMap();
        // 保存参数值
        Object[] paramValues = new Object[parameterTypes.length];
        // 方法的参数列表
        for (int i = 0; i < parameterTypes.length; i++) {
            // 根据参数名称,做某些处理
            String requestParam = parameterTypes[i].getSimpleName();

            if (requestParam.equals("HttpServletRequest")) {
                // 参数类型已明确,这边强转类型
                paramValues[i] = req;
                continue;
            }
            if (requestParam.equals("HttpServletResponse")) {
                paramValues[i] = resp;
                continue;
            }
            if (requestParam.equals("String")) {
                for (Entry<String, String[]> param : parameterMap.entrySet()) {
                    String value = Arrays.toString(param.getValue()).replaceAll("\\[|\\]", "").replaceAll(",\\s", ",");
                    paramValues[i] = value;
                }
            }
        }
        return paramValues;
    }

}


application.properties

application.properties


pom.xml

<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/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>springmvc.hand</groupId>
  <artifactId>springmvc-hand</artifactId>
  <packaging>war</packaging>
  <version>0.0.1-SNAPSHOT</version>
  <name>springmvc-hand Maven Webapp</name>
  <url>http://maven.apache.org</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>
   <java.version>1.7</java.version>
 </properties>
 
  <dependencies>
      <dependency>
        <groupId>javax.servlet</groupId> 
      <artifactId>javax.servlet-api</artifactId> 
      <version>3.0.1</version> 
      <scope>provided</scope>
   </dependency>
   </dependencies>
  <build>
    <finalName>springmvc-hand</finalName>
  </build>
</project>
 


web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    id="WebApp_ID" version="2.5">
    <display-name>Archetype Created Web Application</display-name>

    <servlet>
        <servlet-name>myServlet</servlet-name>
        <servlet-class>com.max.servlet.MyDispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>application.properties</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>myServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

到这里代码就贴完了,源代码已经上传到我的github上,感兴趣的可以从上面下载:

https://github.com/zhangjiabin/hand.git

猜你喜欢

转载自blog.csdn.net/hczjb/article/details/83009352
今日推荐