• Spring MVC 笔记


     
    Spring MVC基本概念:
    1、DispatcherServlet: 前端控制器
    2、Controller: 调用业务逻辑生成model的地方
    3、HandlerAdapter:是被dispatcherServlet使用的,dispatcherServlet通过handlerAdapter来调用controller方法,handler其实是dispatcherServlet内部使用的一个类。
    4、HandlerInterceptor:拦截器 该接口提供after postHandle preHandle 三个方法,调用controller前后使用
    5、HandlerMapping:负责确定DispatcherServelet与controller之间映射的类,告诉DispatcherServelet,在请求到来后,由哪个controller来响应这个请求
    6、HandlerExecutionChain: preHandle->Controller method->postHandle->afterCompletion的执行链
    7、ModelAndView:model的具体表现
    8、viewResolver:视图解析器,决定需要用哪个视图来进行视图的呈现。
    9、view:界面
     
    Spring MVC的原理流程
     
    ModelAndView是springmvc的封装对象,将model和view封装在一起。
     
    一个中心:(不需要开发
    DispatcherServlet前端控制器
    三个组件:不需要开发
     处理器映射器
    处理器适配器
    视图解析器
     
    Handler:(需要开发,上图的第5步,第6步之间的Handler处理器)
    处理器,理解成action
     
    View
    需要开发页面:jsp
     
    springmvc的框架原理
    包括哪些组件:
    前端控制器(中心)
    处理器映射器(三大组件)
    作用:根据url找到Handler.
    处理器适配器(三大组件)
    作用:执行Handler
    视图解析器(三大组件)
    作用:解析出View,根据逻辑视图名解析出真正的视图
     
    开发springmvc程序的步骤
    第一步:配置前端控制器
    在web.xml配置DispathcherServlet前端控制器
    第二步:配置springmvc的全局配置文件
    配置三大组件:
    处理器映射器:
             根据url查找 Handler
    处理器适配器:
             执行Handler
    视图解析器:
             解析出视图View,根据逻辑视图名解析出真正的视图。
     
    第三步:按照处理器适配器规则开发Handler(action
    第四步:将Handler配置在spring容器中。
    第五步:编写视图(jsp+jstl
     
    处理器映射器:
    作用:根据url找到Handler.
    ①形式 BeanNameUrlHandlerMapping
    根据url匹配bean的name 处理器映射器实现了HandlerMapping接口
    <bean
    class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
     
    <!-- 配置action -->
    <bean id="hello_controller" name="/helloworld.action" class="cn.itcast.springmvc.Hello" />
     
     
    ②形式 SimpleUrlHandlerMapping
    将url进行集中配置
    实现了HandlerMapping接口,处理url到bean的映射
    <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
    <property name="mappings">
    <props>
    <prop key="/hello1.action">hello_controller</prop>
    <prop key="/hello2.action">hello_controller</prop>
    <prop key="/hello3.action">hello_controller3</prop>
    </props>
    </property>
    </bean>
     
    <!-- 配置action -->
    <bean id="hello_controller" name="/helloworld.action" class="cn.itcast.springmvc.Hello" />
     
    总结:
    多个处理器映射器可以共存
     
    ③注解映射器形式
    <!--注解映射器 -->
        <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
     
    ④<mvc:annotation-driven />代替注解映射器
     
     
    处理器适配器
    执行Handler
    ①形式 SimpleControllerHandlerAdapter
    SimpleControllerHandlerAdapter:规则是Handler要实现Controller接口
    <bean
    class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />
     
    public class Hello implements Controller {
    @Override
    public ModelAndView handleRequest(HttpServletRequest request,
    HttpServletResponse response) throws Exception {
    ModelAndView modelAndView = new ModelAndView();
    //向页面显示一行提示信息
    //下边的方法就相当于request.setAttribute(arg0, arg1)
    modelAndView.addObject("message", "helloworldttt@$@!^&&%9966!!!!");
    //指定jsp页面地址
    //指定逻辑视图名,真正的视图地址:前缀+逻辑视图名+后缀
    modelAndView.setViewName("hello");
    return modelAndView;
    }
    }
     
    ②形式 HttpRequestHandlerAdapter
    HttpRequestHandlerAdapter:规则是Handler要实现HttpRequestHandler接口。
    <!-- 配置HttpRequestHandlerAdapter处理器适配器 -->
    <bean class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter" />
    public class Hello1 implements HttpRequestHandler {
     
    @Override
    public void handleRequest(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
     
    //向页面显示一行提示信息
    String message = "hellworld1";
    request.setAttribute("message", message);
     
    //指定转向页面,使用request指定页面完整路径
    request.getRequestDispatcher("/WEB-INF/jsp/hello.jsp").forward(request, response);
     
    }
     
    }
     
    ③ 注解适配器形式
    <!--注解适配器 -->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>
     
    ④<mvc:annotation-driven />代替注解适配器
     
    mvc的注解驱动
    <mvc:annotation-driven />默认注册了注解映射器和注解适配器等bean
     
    mvc组件扫描
    <context:component-scan base-package="springmvc.action" />
    扫描@component@controller@service@repository的注解
    注意:如果使用组件扫描则controller不需要在springmvc-servlet.xml中配置
     
     
     
    视图解析器:
    InternalResourceViewResolver
     
    <!-- 视图解析器 解析jsp视图,默认使用jstl,要求classpath下有jstl的jar包 -->
    <bean
    class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <!-- 视图的前缀 -->
    <property name="prefix" value="/WEB-INF/jsp/" />
    <!-- 视图的后缀 -->
    <property name="suffix" value=".jsp" />
    </bean>
     
     
     
     
     
    <mvc:annotation-driven />
    是一种简写形式,替换掉适配器、映射器
     
    <context:component-scan base-package="com.roof" />
    自动扫描bean
    例子:
    <!-- 扫描所有的controller 但是不扫描service -->
    <context:component-scan base-package="cn.itcast">
    <context:include-filter type="annotation"
    expression="org.springframework.stereotype.Controller" />
    <context:exclude-filter type="annotation"
    expression="org.springframework.stereotype.Service" />
    </context:component-scan>
     
    org.springframework.context.support.ResourceBundleMessageSource
    注册到bean.xml中,这个类的作用是获取资源文件的内容,注册到IoC的bean.xml文件中是为了自动获得此类的对象
     
    org.springframework.web.multipart.commons.CommonsMultipartResolver
     
    @Entity
    标注该类为实体类。
     
    @Table(name = "sys_user")
    @Table注释指定了Entity所要映射带的据库表
     
     
    Controller方法通过形参接收页面传递的参数。
    如下:
    @RequestMapping("/userlist")
    public String userlist(HttpServletRequest request,
    HttpServletResponse response,
    HttpSession session,
    Model model
    ){
    }
     
    默认支持的参数类型
    HttpServletRequest
    通过request对象获取请求信息
    HttpServletResponse
    通过response处理响应信息
    HttpSession
    通过session对象得到session中存放的对象
    Model
    通过model向页面传递数据,如下:
     
    model.addAttribute("user", new User("李四"));
     
    页面通过${user.XXXX}获取user对象的属性值。
     
    springmvc通过参数解析器是将request请求内容解析,并给方法形参赋值,将数据和视图封装成ModelAndView对象,最后又将ModelAndView中的模型数据通过reques域传输到页面。简而言之就是,从把jsp页面的数据,以对象的形式可以在后台获取到。前提是jsp的中的名称和model的名称是一致的。页面上以pojo对象中属性名称命名
     
     
     
     
        表单对象-pojo
    1.1.1      使用pojo属性名传递
    页面定义:
     
    Action方法的定义:
     
    在形参使用pojo接收上边的参数。
     
    1.1.2      使用pojo点属性名传递
    页面定义:
     
    Action方法的定义:
    在形参不能直接使用pojo接收上边的参数。
    应该使用包装对象接收上边的参数。
     
     
    Action形参的定义:
     
    字符串数组绑定
    需求:在学生列表上多选,删除学生信息。
     
    页面定义:
     
     
    Action方法定义:
    使用字符串数组接收。
    批量删除学生信息方法,接收页面复选框的值(学生id
     
     
    List绑定
     
    页面向action传递复杂的批量数据,比如学生的成绩信息(课程名称、成绩)
    页面定义:
     
     
     
    Scores:包装对象中list属性的名称
    Coursename:包装对象中list中pojo的属性名称。
    Score:包装对象中list中pojo的属性名称。
    如果上边下标相同的Coursename和Score设置在一个pojo中。
     
    Action方法定义:
    使用List<pojo>接收上边的接收,pojo中包括上边课程名称(coursename)和成绩(score)
    List通过包装对象接收。
     
    在UserVo包装中定义属性:List<>
     
    Action方法形参使用包装对象接收list
     
     
    @RequestMapping
    URL路径映射
    @RequestMapping(value="/user")或@RequestMapping("/user")
    根路径+子路径
    根路径:
    @RequestMapping放在类名上边,如下:
    @Controller
    @RequestMapping("/user")
    子路径:
    @RequestMapping放在方法名上边,如下:
    @RequestMapping("/useradd")
    public String useradd(….
     
    URI 模板模式映射
    @RequestMapping(value="/useredit/{userId}"):{×××}占位符,请求的URL可以是“/useredit/001”或“/useredit/abc”,通过在方法中使用@PathVariable获取{×××}中的×××变量。
     
    例如:
    // 修改学生信息页面
    // @RequestMapping指定url
    @RequestMapping(value="/editstudent/{id}",method = RequestMethod.GET)
    public String editstudent(HttpServletRequest request,Model model,@PathVariable String id) throws Exception {
    System.out.println("id="+id);
    ......
    jsp页面上
    <td><a href="editstudent/${stu.id }.action">修改</a></td>
     
    实现restFul,所有的url都是一个资源的链接,有利于搜索引擎对网址收录。
     
    多个占位符:
     
    @RequestMapping("/useredit/{groupid}/{userid}")
    public String useredit(@PathVariable String groupid,@PathVariable String userid,Model model) throws Exception{
    //方法中使用@PathVariable获取useried的值,使用model传回页面
    model.addAttribute("groupid", groupid);
    model.addAttribute("userid", userid);
    return"/user/useredit";
    }
     
    请求方法限定
    限定GET方法
    @RequestMapping(method = RequestMethod.GET)
     
    如果通过Post访问则报错:
    HTTP Status 405 - Request method 'POST' not supported
     
    例如:
    @RequestMapping(value="/useredit/{userid}",method=RequestMethod.GET)
    限定POST方法
     
    @RequestMapping(method = RequestMethod.POST)
     
    如果通过Post访问则报错:
    HTTP Status 405 - Request method 'GET' not supported
     
    GET和POST都可以
    @RequestMapping(method={RequestMethod.GET,RequestMethod.POST})
     
     
    @RequestParam绑定单个请求参数
     
    value:参数名字,即入参的请求参数名字,如value=“studentid”表示请求的参数区中的名字为studentid的参数的值将传入;
    required:是否必须,默认是true,表示请求中一定要有相应的参数,否则将报400错误码;
    defaultValue:默认值,表示如果请求中没有同名参数时的默认值
     
    定义如下:
    public String userlist( @RequestParam(defaultValue="2",value="group",required=true) String groupid) {=
    }
    形参名称为groupid,但是这里使用value="group"限定参数名为group,所以页面传递参数的名必须为group。
    这里通过required=true限定groupid参数为必需传递,如果不传递则报400错误,由于使用了defaultvalue=”2”默认值即使不传group参数它的值为”2”,所以页面不传递group也不会报错,如果去掉defaultvalue=”2”且定义required=true则如果页面不传递group则会报错。
     
    结果转发
    Redirect
    //请求重定向
    Contrller方法返回结果重定向到一个url地址,如果方式:
    return "redirect:/user/userlist.action";
     
     
    redirect方式相当于“response.sendRedirect()”,转发后浏览器的地址栏变为转发后的地址,因为转发即执行了一个新的request和response。
    由于新发起一个request原来的参数在转发时就不能传递到下一个url,如果要传参数可以/user/userlist.action后边加参数,如下:
    /user/userlist.action?groupid=2&…..
     
    forward
    //页面转发
    controller方法执行后继续执行另一个controller方法。
    return "forward:/user/userlist.action";
     
    forward方式相当于“request.getRequestDispatcher().forward(request,response)”,转发后浏览器地址栏还是原来的地址。转发并没有执行新的request和response,而是和转发前的请求共用一个request和response。所以转发前请求的参数在转发后仍然可以读取到。
     
    如下例子:
     
    @RequestMapping("/c")
    public String c(String groupid,UserVo userVo)throws Exception{
     
    System.out.println("...c...."+groupid+"...user..."+userVo.getUser());
    return "forward:/to/d.action";
    }
     
    @RequestMapping("/d")
    public String d(String groupid,UserVo userVo)throws Exception{
     
    System.out.println("...d...."+groupid+"...user..."+userVo.getUser());
    return "success";
    }
     
    @RequestBody @ResponseBody实现json数据交互
    可以使用@RequestBody将请求的json串转成java对象。
    使用@ResponseBody将action方法返回java对象转成json输出。
     
    请求json响应json 
     
    <!--注解适配器 -->
    <bean
    class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
    json转换器
    <property name="messageConverters">
    <list>
    <bean
    class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"></bean>
    </list>
    </property>
     
    </bean>
     
    如果使用 了<mvc:annotation-driven />则替换上边定义的处理器映射器和适配器 -->
     
     
     
    1       拦截器
     
    拦截器是针对handlerMapping的拦截器,由handlerMapping查找Handler后,将拦截器返回给前端控制器。
     
    1.1      配置拦截器
     
    针对某一个handlerMapping配置拦截器
    <bean
        class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
        <property name="interceptors">
           <list>
               <ref bean="handlerInterceptor1"/>
               <ref bean="handlerInterceptor2"/>
           </list>
        </property>
    </bean>
        <bean id="handlerInterceptor1" class="springmvc.intercapter.HandlerInterceptor1"/>
        <bean id="handlerInterceptor2" class="springmvc.intercapter.HandlerInterceptor2"/>
     
     
     
     
    间接配置全局拦截器:让springmvc框架自动向每个handlerMapping中注册拦截器
    <!--拦截器 -->
    <mvc:interceptors>
        <!--多个拦截器,顺序执行 -->
        <mvc:interceptor>
           <mvc:mapping path="/**"/>//拦截所有请求
           <bean class="cn.itcast.springmvc.interceptor.HandlerInterceptor1"></bean>
        </mvc:interceptor>
        <mvc:interceptor>
           <mvc:mapping path="/**"/>
           <bean class="cn.itcast.springmvc.interceptor.HandlerInterceptor2"></bean>
        </mvc:interceptor>
    </mvc:interceptors>
     
     
     
    1.2      定义拦截器
    实现HandlerInterceptor接口。
     
    public class HandlerInterceptor1 implements HandlerInterceptor {
     
       //handlerspringmvc根据url找到Handler(只有一个方法)
       //执行时机:进入Handler方法之前执行,如果返回false表示拦截,如果返回true表示放行
       //使用场景:用于用户身份校验,用户权限拦截校验
       @Override
       public boolean preHandle(HttpServletRequest request,
             HttpServletResponse response, Object handler) throws Exception {
         
          System.out.println("HandlerInterceptor1..preHandle");
         
          return false;
       }
       //执行时机:进入Handler方法之后 ,在返回modelAndView之前
       //使用场景:使用modelAndView,向页面传递通用数据,使用统一的view
       @Override
       public void postHandle(HttpServletRequest request,
             HttpServletResponse response, Object handler,
             ModelAndView modelAndView) throws Exception {
          System.out.println("HandlerInterceptor1..postHandle");
         
       }
       //执行时机:Handler方法执行完成,(modelAndView已经返回)
       //使用场景:统一异常处理,统一记录系统日志,用于action方法执行监控(在preHandle记录一个时间点,在afterCompletion记录执行结束时间点,将结束时间点减去开始执行时间点,得到执行时长)
       @Override
       public void afterCompletion(HttpServletRequest request,
             HttpServletResponse response, Object handler, Exception ex)
             throws Exception {
          System.out.println("HandlerInterceptor1..afterCompletion");
         
       }
     
     
    1.3      测试
     
    1.3.1      两个拦截器都放行
     
    HandlerInterceptor1..preHandle
    HandlerInterceptor2..preHandle
    HandlerInterceptor2..postHandle
    HandlerInterceptor1..postHandle
    HandlerInterceptor2..afterCompletion
    HandlerInterceptor1..afterCompletion
     
    结论:
    preHandle是按照拦截器定义顺序执行,
    postHandleafterCompletion是按照拦截器定义逆向执行。
     
     
    1.3.1      第一个放行第二个不放行
    HandlerInterceptor1..preHandle
    HandlerInterceptor2..preHandle
    HandlerInterceptor1..afterCompletion
     
    结论:
    只要有一个拦截器不放行,action方法无法完成。
    如果拦截器放行,afterCompletion才会执行。
    只要有一个拦截器不放行,postHandle不执行。
     
    1.3.2      两个都不放行
     
    HandlerInterceptor1..preHandle
     
    结论:
    只要有一个拦截器不放行,action方法无法完成。
    只要有一个拦截器不放行,postHandle不执行。
     
     
     
    1.4      拦截器应用
     
    案例:
    用户身份认证拦截,用户登陆成功后,系统记录session(用户身份信息),用户去操作url时,拦截器需要校验用户身份是否合法(查看session中是否有用户身份信息,如果没有说明用户身份不合法,不合法重新登陆)
     
     
    1.4.1      action
    @Controller
    public class LoginAction {
      
       //登陆页面
       @RequestMapping("/login")
       public String login(Model model)throws Exception{
         
          return "login";
       }
      
       //登陆提交
       //userid:用户账号,pwd:密码
       @RequestMapping("/loginsubmit")
       public String loginsubmit(HttpSession session,String userid,String pwd)throws Exception{
         
          //session记录用户身份信息
          session.setAttribute("activeUser", userid);
         
          return "redirect:stu/querystudent.action";
       }
      
       //退出
       public String logout(HttpSession session)throws Exception{
         
          //session过期
          session.invalidate();
         
          return "redirect:stu/querystudent.action";
       }
      
     
    }
     
    1.4.2      页面
     
    @Controller
    public class LoginAction {
      
       //登陆页面
       @RequestMapping("/login")
       public String login(Model model)throws Exception{
         
          return "login";
       }
      
       //登陆提交
       //userid:用户账号,pwd:密码
       @RequestMapping("/loginsubmit")
       public String loginsubmit(HttpSession session,String userid,String pwd)throws Exception{
         
          //session记录用户身份信息
          session.setAttribute("activeUser", userid);
         
          return "redirect:stu/querystudent.action";
       }
      
       //退出
    @RequestMapping("/logout")
       public String logout(HttpSession session)throws Exception{
         
          //session过期
          session.invalidate();
         
          return "redirect:stu/querystudent.action";
       }
      
     
    }
     
    1.4.3      拦截器
     
    拦截所有url(将公开地址排除(无需登陆即可操作的url)),校验用户身份是否合法(查看session中是否有用户身份信息,如果没有说明用户身份不合法,不合法重新登陆)
     
    @Override
       public boolean preHandle(HttpServletRequest request,
             HttpServletResponse response, Object handler) throws Exception {
         
          //判断请求的url是否公开 地址(无需登陆即可操作url)
          //正常开发时,需要将公开地址配置在配置文件中。
          //取出请求的url
          String url = request.getRequestURI();
          if(url.indexOf("loginsubmit.action")>=0){
             //说明 公开地址
             //放行
             return true;
          }
         
         
          //得到session
          HttpSession session = request.getSession();
         
          // session取出用户身份信息
          String userid = (String) session.getAttribute("activeUser");
         
          if(userid!=null){
             //说明 用户已登陆(用户身份合法)
             //放行
             return true;
            
          }
         
          //执行到这里说明 用户身份不合法,拦截,跳转到登陆页面
          request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
     
          return false;
       }
     
     
    1.1      拦截器应用
     
    案例:
    用户身份认证拦截,用户登陆成功后,系统记录session(用户身份信息),用户去操作url时,拦截器需要校验用户身份是否合法(查看session中是否有用户身份信息,如果没有说明用户身份不合法,不合法重新登陆)
     
     
    1.1.1      action
    @Controller
    public class LoginAction {
      
       //登陆页面
       @RequestMapping("/login")
       public String login(Model model)throws Exception{
         
          return "login";
       }
      
       //登陆提交
       //userid:用户账号,pwd:密码
       @RequestMapping("/loginsubmit")
       public String loginsubmit(HttpSession session,String userid,String pwd)throws Exception{
         
          //session记录用户身份信息
          session.setAttribute("activeUser", userid);
         
          return "redirect:stu/querystudent.action";
       }
      
       //退出
       public String logout(HttpSession session)throws Exception{
         
          //session过期
          session.invalidate();
         
          return "redirect:stu/querystudent.action";
       }
      
     
    }
     
    1.1.2      页面
     
    @Controller
    public class LoginAction {
      
       //登陆页面
       @RequestMapping("/login")
       public String login(Model model)throws Exception{
         
          return "login";
       }
      
       //登陆提交
       //userid:用户账号,pwd:密码
       @RequestMapping("/loginsubmit")
       public String loginsubmit(HttpSession session,String userid,String pwd)throws Exception{
         
          //session记录用户身份信息
          session.setAttribute("activeUser", userid);
         
          return "redirect:stu/querystudent.action";
       }
      
       //退出
    @RequestMapping("/logout")
       public String logout(HttpSession session)throws Exception{
         
          //session过期
          session.invalidate();
         
          return "redirect:stu/querystudent.action";
       }
      
     
    }
     
    1.1.3      拦截器
     
    拦截所有url(将公开地址排除(无需登陆即可操作的url)),校验用户身份是否合法(查看session中是否有用户身份信息,如果没有说明用户身份不合法,不合法重新登陆)
     
    @Override
       public boolean preHandle(HttpServletRequest request,
             HttpServletResponse response, Object handler) throws Exception {
         
          //判断请求的url是否公开 地址(无需登陆即可操作url)
          //正常开发时,需要将公开地址配置在配置文件中。
          //取出请求的url
          String url = request.getRequestURI();
          if(url.indexOf("loginsubmit.action")>=0){
             //说明 公开地址
             //放行
             return true;
          }
         
         
          //得到session
          HttpSession session = request.getSession();
         
          // session取出用户身份信息
          String userid = (String) session.getAttribute("activeUser");
         
          if(userid!=null){
             //说明 用户已登陆(用户身份合法)
             //放行
             return true;
            
          }
         
          //执行到这里说明 用户身份不合法,拦截,跳转到登陆页面
          request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
     
          return false;
       }
  • 相关阅读:
    GAMIT中遇到的错误
    bash: ./install_software: Permission denied
    xmanager无法加载远程桌面
    GMT的安装
    小总结:Gamit中常见常用命令
    动态分配指针数组(以解决)
    Gamit使用gftp软件下载数据
    Python基础(1)
    JAVA中关于多线程的理解
    JAVA 基本绘图——利用JFrame JPanel 绘制扇形
  • 原文地址:https://www.cnblogs.com/YangBinChina/p/8966341.html
Copyright © 2020-2023  润新知