SpringMVC笔记

前言:一年前看视频学习这个框架的时候做的笔记。。。现在自己看来觉得还是有点蛋疼。。。想了想还是放上来了,万一开发要看呢,求勿喷。。。

springmvc

  • 执行流程的过程图:
基本流程
  • 具体执行流程(这个就是现在自己手写的啦):

1.用户提交请求。
2.DispatchServlet接收到请求,将请求交给 HandlerMapping处理,而HandlerMapping将会把请求映射为HandlerExecutionChain对象(包含一个Handler处理器(页面控制器)对象、多个HandlerInterceptor拦截器)对象。
3.DispatchSerlvet调用HandlerAdapter来调用Handler(Controller),HandlerAdapter将会把处理器包装为适配器,HandlerAdapter将会根据适配的结果调用真正的处理器的功能处理方法,完成功能处理;并返回一个ModelAndView对象(包含模型数据、逻辑视图名)
4.DispatchServlet调用ViewResolver,ViewResolver将把逻辑视图名解析为具体的View。
5.将Model的数据渲染到View上。
6.将View返回到用户。

  • 基本配置(xml版):
    • 先导入jar包,如下:
      <code>commons-logging.jar
      org.springframework.asm-3.0.5.RELEASE.jar
      org.springframework.beans-3.0.5.RELEASE.jar
      org.springframework.context-3.0.5.RELEASE.jar
      org.springframework.core-3.0.5.RELEASE.jar
      org.springframework.expression-3.0.5.RELEASE.jar
      org.springframework.web.servlet-3.0.5.RELEASE.jar
      org.springframework.web-3.0.5.RELEASE.jar</code>

    • 在web.xml中配置核心过滤器:
      <code><servlet>
      <servlet-name>DispatcherServlet</servlet-name>
      <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
      </servlet>
      <servlet-mapping>
      <servlet-name>DispatcherServlet</servlet-name>
      // 确保了只有action后缀名的请求才可以提交到后台
      <url-pattern>*.action</url-pattern>
      </servlet-mapping>
      </code>

    • 开发action(实现controller接口):
      <code>
      public class HelloAction implements Controller{
      // 构造函数,下文有惊喜
      public HelloAction() {
      System.out.println("HelloAction():"+this.hashCode());
      }
      @Override
      public ModelAndView handleRequest(HttpServletRequest request,
      HttpServletResponse response) throws Exception {
      // ModelAndView表示封装的Model数据和View视图
      System.out.println("handleRequest()");
      ModelAndView modelAndView = new ModelAndView();
      modelAndView.addObject("message", "this is my first springmvc app");
      // 没配置逻辑视图的要配置文件路径
      // 如:modelAndView.setViewName("./jsp/success.jsp");
      modelAndView.setViewName("success");
      return modelAndView;
      }
      }
      </code>

    • 在配置xml中配置映射(要注意的是,上面的配置核心过滤器步骤,没有指定配置文件,所以默认叫做DispatcherServlet-servlet.xml,而且是放在WEB-INF目录下):
      <code>
      <bean name="/hello.action" class="com.ycTime.action.HelloAction"></bean></code>

    • 自定义配置文件路径:
      <code>
      <servlet>
      <servlet-name>DispatcherServlet</servlet-name>
      <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
      <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:springmvc.xml</param-value>
      </init-param>
      </servlet>
      </code>

    • 配置逻辑视图(直接返回页面的名字),需要在springmvc.xml(我的配置叫这个名字而已)那里配置:
      <code>
      <bean
      class="org.springframework.web.servlet.view.InternalResourceViewResolver">
      <property name="prefix" value="/jsp/"></property>
      <property name="suffix" value=".jsp"></property>
      </bean></code>


      结果
访问了两次,HelloAction的构造函数只执行一次,可以看到构造函数下面一行红字上写着,XXXSingletons,证明Controller是单例的。而且创建这个单例的时间,还是在HandlerMapping工作之前的(同样可以看红字看出来,Mapped URL XXXX)

收集参数(不分版本):

  • 收集参数并封装为对象
    <code>
    @RequestMapping(value="/register",method=RequestMethod.POST)
    public String register(Model model, User user){
    System.out.println(user.getUsername()+":"+user.getPassword()+
    ":"+user.getJoinDate().toLocaleString());
    model.addAttribute("message", "注册成功");
    model.addAttribute("user", user);
    return "success";
    }</code>
    直接在函数的参数列表里面写上就好了,参数名字与提交的值的名字必须对应,例子中的user是直接将参数封装成User对象,注意参数的名字在user类里面必须有setter方法
    参数里面里面有个model,这个就是可以操作request域数据的对象
    ![可以直接收集提交的数据,也可以收集URL中的数据,后者需要在value上用{}标注关键词名字,在参数列表中使用@PathVariable收集到该参数

  • 收集多类型参数:

    • list:
      <code>
      在表单那里:
      <input type="text" name="userList[0].username" >
      // action中的方法
      public String getArray(Model model, Bean bean){
      // 在代码中直接获取list或者数组就可以了
      List<User> userList = bean.getUserList();
      }
      // Bean对象
      public class Bean {
      private List<User> userList;
      public List<User> getUserList() {
      return userList;
      }
      public void setUserList(List<User> userList) {
      this.userList = userList;
      }
      }</code>
      其实这个和收集并封装成user是一样的,因为封装user的时候是用表单中提交的值来完成user的实例化操作,其中表单的name和user的属性名是一致的。
      而封装list的话,表单名字中的userList[0]正好作为bean里面的setList(List<User> userList)的参数,也就是用userList来完成实例化操作。

    • 数组:<code>
      // 表单
      <td><input type="checkbox" name="ids" value="1">1</td>
      <td>sivan</td>
      <td><input type="checkbox" name="ids" value="2">2</td>

      <td>tom</td>
      // action 方法
      @RequestMapping(value="/getArray")
      public String getArray(Model model, String[] ids){
      System.out.print("被选中的序号是:");
      for (String string : ids) {
      System.out.print(string+" ");
      }
      model.addAttribute("message", "xuanzhong成功");
      return "success";
      }</code>

返回Json数据

示例
  • 首先需要导包
    <code>jackson-core-asl-1.9.11.jar
    jackson-mapper-asl-1.9.11.jar</code>
  • 然后在action函数返回值那里加一个类型标签@ResponseBody,返回值直接返回某个对象,不返回字符串
    <code>
    @RequestMapping(value="/map2json")
    public @ResponseBody Map<String,User> map2Json(){
    Map<String,User> userMap = new HashMap<>();
    for(int i = 0;i<4;i++){
    User user = new User();
    user.setId(i);
    user.setJoinDate(new Date());
    user.setUsername("Liming");
    user.setAge(i*6-i+3);
    userMap.put(i+"", user);
    }
    return userMap;
    }
    </code>
  • 最后在springmvc.xml配置下适配器(不然会报找不到适配器的错误):
    <code><bean
    class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
    <property name="messageConverters">
    <list>
    <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"></bean>
    </list>
    </property>
    </bean></code>

springmvc拦截器

  • 创建拦截器,实现HandlerInterceptor接口(也可以实现WebRequestInterceptor接口):
    <code>public class FirstInterceptor implements HandlerInterceptor{
    @Override
    public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
    throws Exception {
    System.out.println("执行afterCompletion方法");
    }
    // modelAndView参数:可以控制需要显示的视图
    @Override
    public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView modelAndView)
    throws Exception {
    System.out.println("执行postHandle方法");
    }
    /* 返回值:表示我们是否需要将当前的请求拦截下来
    false: 请求被中止
    true: 请求继续
    obj参数:表示的当前被拦截的请求目标(请求对应的类,比如说user2json请求,即obj是UserAction类)
    */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object obj) throws Exception {
    System.out.println("执行preHandle方法");
    return true;
    }
    }
    jieguo :
    FirstInterceptor执行preHandle方法
    FirstInterceptor执行postHandle方法
    FirstInterceptor执行afterCompletion方法
    </code>
    其中,preHandle方法是在请求被处理之前执行,postHandle在请求被处理之后执行,afterCompletion方法是在请求结束之后执行
  • 在springmvc的配置文件中注册拦截器:
    <code>
    <mvc:interceptors>
    <mvc:interceptor>

    <mvc:mapping path="/user/*.action"/>
    <mvc:mapping path="/json/user2json.action"/>
    <bean class="com.ycTime.interceptor.FirstInterceptor"></bean>
    </mvc:interceptor>
    </mvc:interceptors>
    </code>

  • 当有多个拦截器一起工作的时候,工作流程如图:

    先按照拦截器的配置顺序执行拦截器的preHandle方法,然后执行业务方法,然后按拦截器配置顺序的倒序来执行拦截器的postHandle方法,执行完所有拦截器的postHandle方法在,再按倒序执行afterCompletion方法(个人猜测是责任链模式。。)

    <code>
    <mvc:interceptors>

    <mvc:interceptor>
    <mvc:mapping path="/user/.action"/>
    <mvc:mapping path="/json/user2json.action"/>
    <bean class="com.ycTime.interceptor.FirstInterceptor"></bean>
    </mvc:interceptor>

    <mvc:interceptor>
    <mvc:mapping path="/user/
    .action"/>
    <mvc:mapping path="/json/user2json.action"/>
    <bean class="com.ycTime.interceptor.SecondInterceptor"></bean>
    </mvc:interceptor>
    </mvc:interceptors>
    结果:
    FirstInterceptor执行preHandle方法
    SecondInterceptor执行preHandle方法
    1 Liming 注册于 :26 Mon Mar 13 17:59:40 CST 2017
    SecondInterceptor执行postHandle方法
    FirstInterceptor执行postHandle方法
    SecondInterceptor执行afterCompletion方法
    FirstInterceptor执行afterCompletion方法</code>

  • 拦截起使用场景:解决业务共性问题

比如:
1)字符编码例如request.setCharacterEncoding("utf-8");response.setCharacterEncoding("utf-8");(假设没配置字符串过滤器,)
2)权限校验(request.getSession().getAttribute("user")!=null等)
方法和struts2都是一样的,通过request或者response加以控制来实现。

  • 提一下过滤器
    配置字符过滤器:
    <code><filter>
    <filter-name>encoding</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
    <param-name>encoding</param-name>
    <param-value>utf8</param-value>
    </init-param>
    </filter>
    <filter-mapping>
    <filter-name>encoding</filter-name>
    <url-pattern>*</url-pattern>
    </filter-mapping></code>
  • 拦截器与过滤器区别:
    ①拦截器是基于java的反射机制的,而过滤器是基于函数回调。
    ②拦截器不依赖与servlet容器,过滤器依赖与servlet容器。
    ③拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。
    ④拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。
    ⑤在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次。
    ⑥拦截器可以获取IOC容器中的各个bean,而过滤器就不行,这点很重要,在拦截器里注入一个service,可以调用业务逻辑
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,482评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,377评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,762评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,273评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,289评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,046评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,351评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,988评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,476评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,948评论 2 324
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,064评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,712评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,261评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,264评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,486评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,511评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,802评论 2 345

推荐阅读更多精彩内容