手写一个简易的springmvc

 

 

一:思路:

用户在网址上输入请求链接如:www.baidu.com/user/girl,我们根据/user/girl这个链接  找到相应的对象方法进行调用。

怎么找呢:

1)根据反射的方法getAnnotation(“注解”) ,我们就可以找到注解对应的方法和类,然后把链接(key)对应的方法(value)存到map里面。

2)然后再根据用户输入的user/girl(key) 就可以找到对应的方法,然后用反射去调用就行了。这里主要涉及的只是点就是java反射。

 

项目结构:

这里只实现Controller,RequestMapping,RequestParam 功能

 

二:代码

 

1.application.properties

扫描包:

scanPackage=com.learing.core

 2.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_3_0.xsd"
version="3.0">


<servlet>
    <servlet-name>MySpringMVC</servlet-name>
    <servlet-class>com.learing.servlet.MyDispatcherServlet</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>MySpringMVC</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>

</web-app>

 3.MyController

package com.learing.annotation;


import java.lang.annotation.*;


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

    /**
     * 表示给controller 注册别名
     * @return
     */
    String value() default  "";

}

 4.MyRequestMapping

 

package com.learing.annotation;
import java.lang.annotation.*;

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

    /**
     * 表示访问该方法的url
     * @return
     */
    String value() default "";
}

5.MyRequestParam

 

package com.learing.annotation;
import java.lang.annotation.*;

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

    /**表示参数的别名,必填
     * @return
     */
String value();
}

 6.TestController

package com.learing.core.controller;

import com.learing.annotation.MyController;
import com.learing.annotation.MyRequestMapping;
import com.learing.annotation.MyRequestParam;

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

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

    @MyRequestMapping("/doTest")
    public void test1(HttpServletRequest request, HttpServletResponse response,
                      @MyRequestParam("param") String param){
        System.out.println(param);
        try {
            response.getWriter().write( "doTest method success! param:"+param);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    @MyRequestMapping("/doTest2")
    public void test2(HttpServletRequest request, HttpServletResponse response){
        try {
            response.getWriter().println("doTest2 method success!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

7.MyDispatcherServlet

这个就是重点了:

package com.learing.servlet;

import com.learing.annotation.MyController;
import com.learing.annotation.MyRequestMapping;

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

public class MyDispatcherServlet  extends HttpServlet {

    private Properties properties = new Properties();
    private List<String> classNames  = new ArrayList<>();
    private Map<String, Object> iocMap = new HashMap<>();
    private Map<String, Method> handlerMappingMap= new HashMap<>();
    private Map<String, Object> controllerMap  =new HashMap<>();
    @Override
    public void init(ServletConfig config) throws ServletException {

        //1.从web.xml里面加载配置文件
         doLoadConfig(config.getInitParameter("contextConfigLocation"));
        //2.初始化所有相关联的类,扫描用户设定的包下面所有的类
         doScanner(properties.getProperty("scanPackage"));
        //3.拿到扫描到的类,通过反射机制,实例化,并且放到ioc容器中(k-v  beanName-bean) beanName 默认是首字母小写
          doInstance();
        //4.初始化HandlerMapping(将url和method对应上)
         initHandlerMapping();

    }


    /**加载配置文件
     * @param location
     */
    private void doLoadConfig(String location){
        //把web.xml 中的contextConfigLocation对应value值的文件加载到流里面
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(location);

        try {
            //用Properties 文件加载文件里的内容
            properties.load(resourceAsStream);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //关流
            if(null != resourceAsStream){
                try {
                    resourceAsStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }


    /**把要扫描的类放到集合里面
     * @param packageName
     */
    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{
                String className = packageName + "." + file.getName().replace(".class", "");
                classNames.add(className);
            }
        }

    }


    /**
     * 根据注解实例化
     */
    private void doInstance() {
        if(classNames.isEmpty()){
            return;
         }
        for(String className : classNames) {
            //把类搞出来,反射来实例化(只有加@MyController需要实例化)
            try {
                Class<?> clazz = Class.forName(className);
                //这里就是重点了,判断类上面是否有需要实例化的注解,进行实例化。A.isAnnotationPresent(B.class):B类型的注解是否在A类上;
                if(clazz.isAnnotationPresent(MyController.class)) {
                    iocMap.put(toLowerFirstWord(clazz.getSimpleName()), clazz.newInstance());
                }else {
                    continue;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
  }


    private void initHandlerMapping(){
        if(iocMap.isEmpty()){
            return;
        }

        try {
            for(Map.Entry<String, Object> entry   : iocMap.entrySet()) {
                Class<? extends  Object> clazz = entry.getValue().getClass();

                if(!clazz.isAnnotationPresent(MyController.class)){
                    continue;
                }
                //拼url时,是controller头的url并拼上方法上的url(即类上面的url + 方法上面的url)
                String baseUrl = "";
                if(clazz.isAnnotationPresent(MyRequestMapping.class)){
                    MyRequestMapping annotation = clazz.getAnnotation(MyRequestMapping.class);
                    baseUrl = annotation.value();
                 }
                Method[] methods = clazz.getMethods();
                for(Method method : methods) {
                    if(!method.isAnnotationPresent(MyRequestMapping.class)){
                        continue;
                    }

                    MyRequestMapping annotation = method.getAnnotation(MyRequestMapping.class);
                    String url = annotation.value();
                    //不懂
                     url = (baseUrl + "/" +url).replaceAll("/+" , "/");
                    //这里应该放置实例和method
                    handlerMappingMap.put(url,method);
                    controllerMap.put(url, clazz.newInstance());
                    System.out.println(url + "," +method);

                }

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


    }



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

         }


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

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {


        try {//处理请求
            doDispath(req,resp);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void doDispath(HttpServletRequest req, HttpServletResponse resp) throws Exception {

              if(handlerMappingMap.isEmpty()){
                     return;
               }

              String url = req.getRequestURI();
              String contextPath = req.getContextPath();

              //拼接url并把多个/替换成一个
              url = url.replace(contextPath, "").replaceAll("/+", "/");
               if(!this.handlerMappingMap.containsKey(url)){
                   resp.getWriter().write("404 NOT FOUND");
                   return;
               }

               //根据用户的url请求找到对应的方法
              Method method = this.handlerMappingMap.get(url);
               //获取方法的参数列表
              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 (Map.Entry<String, String[]> param : parameterMap.entrySet()) {
                          String value =Arrays.toString(param.getValue()).replaceAll("\\[|\\]", "").replaceAll(",\\s", ",");
                          paramValues[i]=value;
                      }
               }
                  //利用反射机制来调用
                  try {
                      method.invoke(this.controllerMap.get(url),paramValues);
                  } catch (IllegalAccessException e) {
                      e.printStackTrace();
                  }


              }

}}

 

三.测试

输入:  http://localhost:8088/EasySpringmvcHandWritingDemo/test/doTest?param=liugh

结果:

 

原文地址:https://mp.weixin.qq.com/s/36F_fFbGKkRL20DJgX4ahg

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值