从Servlet 到 springmvc,简单聊聊

总结:先聊聊 servlet 其实是一套规范具体实现都是交给容器,如 tomcat,jetty 具体实现,

 

   接口如下:

public interface Servlet {
    void init(ServletConfig var1) throws ServletException;

    ServletConfig getServletConfig();

    void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;

    String getServletInfo();

    void destroy();
}

tip:

load-on-startup
元素标记容器是否应该在web应用程序启动的时候就加载这个Servlet,(实例化并调用其init()方法)。
它的值必须是一个整数,表示Servlet被加载的先后顺序。
如果该元素的值为负数或者没有设置,则容器会当Servlet被请求时再加载。
如果值为正整数或者0时,表示容器在应用启动时就加载并初始化这个Servlet,值越小,Servlet的优先级越高,就越先被加载。值相同时,容器就会自己选择顺序来加载。

  生命周期如下:

  • init () 方法进行加载资源等初始化。
  • service() 方法来处理客户端的请求。
  • destroy() 方法终止(结束)。
  • 最后,Servlet 是由 JVM 的垃圾回收器进行垃圾回收的。

sevlert 单例 构造器 初始化函数 只会调用一次,在次请求 都是只 service()方法 不会再初始对象

经过上面简单复习,小伙伴是不是对servlet 有了简单的认识

下面教大家哦 实现 一个简单版mvc

web.xml 配置

<web-app>
  <display-name>Archetype Created Web Application</display-name>
  <servlet>
    <servlet-name>MySpringMvc</servlet-name>
    <servlet-class>com.lyc.framework.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>*.json</url-pattern>
  </servlet-mapping>
</web-app>

  初始化过程:我这里只是简单实现,看过源码的都知道 比我这多

        this.initMultipartResolver(context);
        this.initLocaleResolver(context);
        this.initThemeResolver(context);
        this.initHandlerMappings(context);
        this.initHandlerAdapters(context);
        this.initHandlerExceptionResolvers(context);
        this.initRequestToViewNameTranslator(context);
        this.initViewResolvers(context);
        this.initFlashMapManager(context);

  

    @Override
    public void init(ServletConfig config) throws ServletException {
        logger.info("MyDispatcherServlet init");

        String configLocation = config.getInitParameter(localtion);
        // 初始化ioc容器 di 依赖注入
        MyApplicationContext context = new MyApplicationContext(configLocation);
        // url 对应  实例化类 和 方法 为了  method.invoke(obj, args);
        initHandlerMappings(context);
        // 适配 参数,类型转换 反射 method.invoke(obj, args);
        initHandlerAdapters(context);
        //拿到结果 视图解析
        initViewResolvers(context);

    }

  下面讲一下 具体实现

        第一步:扫包 获取全路径 以便class.forName

       

  private void doRegister(String packageName) {
        URL url = this.getClass().getClassLoader().getResource("/"+packageName.replaceAll("\\.","/"));
        File dir = new File(url.getFile());
        for (File file:dir.listFiles()) {
            if(file.isDirectory()){
                doRegister(packageName+"/"+file.getName());
            }else{
                classCache.add(packageName.replace("/",".")+"."+file.getName().replace(".class",""));
            }
        }
    }

  第二步 实例化 这里就是简单处理 类名小写 对应 实例化对象

private void doCreateBean() {
if(classCache.isEmpty()){
return;
}
try {
for (String className:classCache) {
Class<?> clazz = Class.forName(className);
if(clazz.isAnnotationPresent(MyController.class)){
instanceMapping.put(lowerFirstName(clazz.getSimpleName()),clazz.newInstance());
}else if(clazz.isAnnotationPresent(MyService.class)){
MyService myService = clazz.getAnnotation(MyService.class);
String id = myService.value();
if(!"".equals(id.trim())){
instanceMapping.put(id,clazz.newInstance());
}
Class<?>[] interfaces = clazz.getInterfaces();
for(Class i:interfaces){
instanceMapping.put(i.getName(),clazz.newInstance());
}
}
}
}catch (Exception e){
e.printStackTrace();
}
}


第三步 依赖注入 di

 private void populate() {
        if(classCache.isEmpty()){
            return;
        }
        for(Map.Entry<String,Object> entry:instanceMapping.entrySet()){
            Field[] fields = entry.getValue().getClass().getDeclaredFields();
            for(Field field :fields){
                if(!field.isAnnotationPresent(MyAutoWired.class)){
                    continue;
                }
                MyAutoWired myAutoWired = field.getAnnotation(MyAutoWired.class);
                String id = myAutoWired.value().trim();
                if("".equals(id)){
                    id = field.getType().getName();
                }
                field.setAccessible(true);
                try {
                    field.set(entry.getValue(),instanceMapping.get(id));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

  经过上面的步骤已经 完成了 ioc di 的初始化 操作。

      下面就来 简单的 url 匹配 对应的类#方法

       代码如下

       

   private void initHandlerMappings(MyApplicationContext context) {
        Map<String, Object> ioc = context.findAll();
        if (ioc.isEmpty()) {
            return;
        }
        for (Map.Entry<String, Object> entry : ioc.entrySet()) {
            Class<?> clazz = entry.getValue().getClass();
            if (!clazz.isAnnotationPresent(MyController.class)) {
                return;
            }
            String url = "";
            if (clazz.isAnnotationPresent(MyRequestMapping.class)) {
                MyRequestMapping myRequestMapping = clazz.getAnnotation(MyRequestMapping.class);
                url += myRequestMapping.value();
            }

            Method[] methods = clazz.getMethods();
            for (Method method : methods) {
                if (!method.isAnnotationPresent(MyRequestMapping.class)) {
                    continue;
                }
                MyRequestMapping myRequestMapping = method.getAnnotation(MyRequestMapping.class);
                String mappingUrl = url + myRequestMapping.value();
                handlerMapping.put(mappingUrl, new Handler(entry.getValue(), method));
            }
        }
    }

  找到url 对应的类 实例 和方法 了就要解析参数了 代码如下:

      找到 参数类型 位置

 private void initHandlerAdapters(MyApplicationContext context) {
        if (handlerMapping.isEmpty()) {
            return;
        }
        // 参数的类型为key index为值
        Map<String, Integer> paramMapping = new HashMap<>();
        for (Map.Entry<String, Handler> entry : handlerMapping.entrySet()) {
            Handler handler = entry.getValue();
            Class<?>[] paramsTypes = handler.method.getParameterTypes();
            // 参数有顺序 反射无法拿到参数的名字
            for (int i = 0; i < paramsTypes.length; i++) {
                Class<?> type = paramsTypes[i];
                if (type == HttpServletRequest.class || type == HttpServletResponse.class) {
                    paramMapping.put(type.getName(), i);
                    continue;
                }
            }
            Annotation[][] annos = handler.method.getParameterAnnotations();
            for (int i = 0; i < annos.length; i++) {
                for (Annotation a : annos[i]) {
                    if (a instanceof MyRequestParam) {
                        String param = ((MyRequestParam) a).value();
                        if (!"".equals(param)) {
                            paramMapping.put(param, i);
                        }
                    }
                }
            }
            adapterMap.put(handler, new HandlerAdapter(paramMapping));
        }

    }

  

        参数适配

         

  class HandlerAdapter {
        private Map<String, Integer> paramMapping;

        public HandlerAdapter(Map<String, Integer> paramMapping) {
            this.paramMapping = paramMapping;
        }

        public ModelAndView handle(HttpServletRequest req, HttpServletResponse resp, Handler handler) throws InvocationTargetException, IllegalAccessException {
            Class<?>[] paramTypes = handler.method.getParameterTypes();
            Object[] paramValues = new Object[paramTypes.length];
            Map<String, String[]> map = req.getParameterMap();
            for (Map.Entry<String, String[]> param : map.entrySet()) {
                String value = Arrays.toString(param.getValue()).replaceAll("\\[|\\]", "")
                        .replaceAll(",\\s", ",");
                if (!this.paramMapping.containsKey(param.getKey())) {
                    continue;
                }
                int index = this.paramMapping.get(param.getKey());
                paramValues[index] = castStringValue(value, paramTypes[index]);
            }
            if (this.paramMapping.containsKey(HttpServletRequest.class.getName())) {
                int reqIndex = this.paramMapping.get(HttpServletRequest.class.getName());
                paramValues[reqIndex] = req;
            }
            if (this.paramMapping.containsKey(HttpServletResponse.class.getName())) {
                int respIndex = this.paramMapping.get(HttpServletResponse.class.getName());
                paramValues[respIndex] = resp;
            }
            Class returnType = handler.method.getReturnType();
            boolean isModelAndView = returnType == ModelAndView.class;
            Object result = handler.method.invoke(handler.controller, paramValues);
            if (isModelAndView) {
                return (ModelAndView) result;
            }
            return null;
        }
    }

  类型转换 贴一点 意思一下

private Object castStringValue(String value, Class<?> clazz) {

        if (clazz == String.class) {
            return value;
        } else if (clazz == Integer.class) {
            return Integer.parseInt(value);
        } else if (clazz == Boolean.class) {
            return Boolean.parseBoolean(value);

  使用

     post 里面 调用

   private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws IOException, InvocationTargetException, IllegalAccessException {
        Handler handler = getHandler(req);
        if (handler == null) {
            resp.getWriter().write("404 Not Found");
        }
        HandlerAdapter handlerAdapter = getHandlerAdapter(handler);
        ModelAndView modelAndView = handlerAdapter.handle(req, resp, handler);
        applyDefaultView(resp, modelAndView);

    }

  

简单的 mvc 就写完了,是不是 有点小收货

转载于:https://www.cnblogs.com/lyc88/articles/11096213.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值