【SSM框架】SpringMVC笔记 --- 汇总

1、什么是 SpringMVC?


SpringMVC 是基于 MVC 开发模式的框架,用来优化控制器。它是 Spring 家族的一员,它也具备 IOC 和 AOP。

  • 什么是MVC?

MVC 是一种开发模式,它是模型视图控制器的简称,所有的 web 应用都是基于 MVC 开发。

1)M:模型层,包含实体类,业务逻辑层,数据访问层

2)V:视图层,html,javaScript,vue 等都是视图层,用来显现数据

3)C:控制器,它是用来接收客户端的请求,并返回响应到客户端的组件,Servlet就是组件

 

2、SpringMVC 框架的优点?


1)轻量级,基于 MVC 的框架

2)易于上手,容易理解,功能强大

3)它具备 IOC 和 AOP

4)完全基于注解开发

 

3、Springmvc 的执行流程:


执行流程说明:

1)向服务器发送HTTP请求,请求被前端控制器 DispatcherServlet 捕获。

2)DispatcherServlet 根据<servlet-name>中的配置对请求的URL进行解析,得到请求资源标识符(URI)。然后根据该URI,调用 HandlerMapping 获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以 HandlerExecutionChain 对象的形式返回。

3)DispatcherServlet 根据获得的 Handler,选择一个合适的 HandlerAdapter。

4)提取 Request 中的模型数据,填充 Handler 入参,开始执行 Handler(Controller)。在填充 Handler的入参过程中,根据你的配置,Spring 将帮你做一些额外的工作:

     HttpMessageConveter:将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息。

    数据转换:对请求消息进行数据转换。如String转换成Integer、Double等。

    数据格式化:对请求消息进行数据格式化。如将字符串转换成格式化数字或格式化日期等。

    数据验证:验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中。

5)Handler(Controller)执行完成后,向 DispatcherServlet 返回一个 ModelAndView 对象。

6)根据返回的ModelAndView,选择一个适合的 ViewResolver(必须是已经注册到Spring容器中的ViewResolver)返回给DispatcherServlet。

7)ViewResolver 结合Model和View,来渲染视图。

8)视图负责将渲染结果返回给客户端

 

4、基于注解的 SpringMVC 框架开发的步骤:


1)新建项目,选择 webapp 模板

2)修改目录,添加缺失的 test 目录,java目录,resources目录(两套),并修改目录属性

3)修改 pom.xml 文件,添加 SpringMVC 的依赖,添加 Servlet 的依赖

<!--添加springmvc的依赖-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.5.RELEASE</version>
</dependency>
<!--添加servlet的依赖-->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
</dependency>

 

4)添加 springmvc.xml 配置文件,指定包扫描,添加视图解析器.

SpringMVC 框架为了避免请求资源路径与扩展名上的冗余,在视图解析器 InternalResouceViewResolver 中引入了请求的前辍与后辍。而 action 中只需给出要跳转页面的文件名即可,对于具体的文件路径与文件扩展名,视图解析器会自动完成拼接;

<context:component-scan>:用来进行包扫描,这里用于指定@Controller注解所在的包路径。

<!--添加包扫描-->
<context:component-scan base-package="com.bjpowernode.controller"></context:component-scan>
<!--添加视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <!--配置前缀-->
    <property name="prefix" value="/admin/"></property>
    <!--配置后缀-->
    <property name="suffix" value=".jsp"></property>
</bean>

 

5)删除 web.xml 文件,新建 web.xml

6)在 web.xml 文件中注册 springMVC 框架(所有的 web 请求都是基于 servlet 的)

<!--注册SpringMVC框架-->
<servlet>
    <servlet-name>springmvc</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>
<servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <!--
          指定拦截什么样的请求
          <a href="${pageContext.request.contextPath}/demo.action">访问服务器</a>
     -->
    <!--指定拦截以.action结尾的请求,交给核心处理器DispatcherServlet处理。-->
    <url-pattern>*.action</url-pattern>
</servlet-mapping>

 

7)在 webapp 目录下新建 admin 目录,在 admin 目录下新建 main.jsp 页面,删除 index.jsp 页面,并新建

8)开发控制器(Servlet),这是一个普通的类,不用继承和实现接口。类中的每个方法就是一个具体的action控制器。

@Controller  //表示当前类为处理器,交给Spring去创建对象
public class DemoAction {
    /**
    * 以前的Servlet的规范
    * protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {}
    * action中所有的功能实现都是由方法来完成的
    * action方法的规范
    * 1)访问权限是public
    * 2)方法的返回值任意
    * 3)方法名称任意
    * 4)方法可以没有参数,如果有可是任意类型
    * 5)要使用@RequestMapping注解来声明一个访问的路径(名称)
    *
    */
    @RequestMapping("/demo")
    public String demo(){
    System.out.println("服务器被访问到了.......");
    return "main";  //可以直接跳到/admin/main.jsp页面上
    }
}

 

9)添加 tomcat 进行测试功能

 

5、分析 web 请求:


web请求执行的流程:

index.jsp<--------------->DispatcherServlet<------------------->SpringMVC的处理器(加了@Controller的类中的方法)

 one.jsp  <--------------->DispatcherServlet<------------------->SpringMVC的处理器(加了@Controller的类中的方法)

  • DispatcherServlet 要在 web.xml 文件中注册才可用

 

6、@RequestMapping 注解详解:


  • 此注解就是来映射服务器访问的路径。@RequestMapping 的 value 属性用于定义所匹配请求的 URI。

1)此注解可加在方法上,是为此方法注册一个可以访问的名称(路径)。

@RequestMapping("/demo")
public String demo(){
System.out.println("服务器被访问到了.......");
return "main";  //可以直接跳到/admin/main.jsp页面上
}
//<a href="${pageContext.request.contextPath}/demo.action">访问服务器</a>

 

2)此注解可以加在类上,相当于是包名(虚拟路径),区分不同类中相同的 action 的名称。URI 的请求是相对于 Web 的根目录。换个角度说,要访问处理器的指定方法,必须要在方法指定 URI 之前加上处理器类前定义的模块名称。

@RequestMapping("/user")
public class DemoAction1 {..}
//<a href="${pageContext.request.contextPath}/user/demo.action">访问服务器</a>

 

3)此注解可区分 get 请求和 post 请求:

  • 对于@RequestMapping,其有一个属性 method,用于对被注解方法所处理请求的提交方式进行限制,即只有满足该 method 属性指定的提交方式的请求,才会执行该被注解方法。
 @Controller
    public class ReqAction {
        @RequestMapping(value = "/req",method = RequestMethod.GET)
        public String req(){
            System.out.println("我是处理get请求的........");
            return "main";
        }
        @RequestMapping(value = "/req" ,method = RequestMethod.POST)
        public String req1(){
            System.out.println("我是处理post请求的........");
            return "main";
        }
    }

 

7、五种数据提交方式:


  • 前四种数据注入的方式,会自动进行类型转换。但无法自动转换日期类型。

1)单个提交数据

  • 在方法中声明一个和表单提交的参数名称相同的参数,由框架按照名称直接注入。

栗子:

/*  
    <form action="${pageContext.request.contextPath}/one.action">
        姓名:<input name="myname"><br>
        年龄:<input name="age"><br>
        <input type="submit" value="提交">
    </form>
*/

    @RequestMapping("/one")
    public String one(String myname,int age){  ===>自动注入,并且类型转换
        System.out.println("myname="+myname+",age="+(age+100));
        return "main";
    }

 

2)对象封装提交数据:

  • 声明一个自定义的实体类参数,框架调用实体类中相应的setter方法注入属性值,只要保证实体类中成员变量的名称与提交请求的name属性值一致即可。

实体类:

public class Users {
    private String name;
    private int age;
    //构造方法,setter方法等省略。。。
}

 

页面:

<form action="${pageContext.request.contextPath}/two.action" method="post">
    姓名:<input name="name"><br>
    年龄:<input name="age"><br>
    <input type="submit" value="提交">
</form>

 

action:

@RequestMapping("/two")
public String two(Users u){
    System.out.println(u);
    return "main";
}


3)动态占位符提交

  • 仅限于超链接或地址拦提交数据,它是一杠一值,一杠一大括号,使用注解 @PathVariable 来解析。
//<a href="${pageContext.request.contextPath}/three/张三/22.action">动态提交</a>    
@RequestMapping("/three/{uname}/{uage}")
public String three(@PathVariable("uname") //用来解析路径中的请求参数
                    String name,
                    @PathVariable("uage")
                    int age){
    System.out.println("name="+name+",age="+(age+100));
    return "main";
}

 

4)映射名称不一致

  • 提交请求参数与action方法的形参的名称不一致,使用注解@RequestParam来解析
/**
*  姓名:<input name="name"><br>
*  年龄:<input name="age"><br>
*/
@RequestMapping("/four")
public String four(@RequestParam("name") //专门用来解决名称不一致的问题
                    String uname,
                    @RequestParam("age")
                    int uage){
    System.out.println("uname="+uname+",uage="+(uage+100));
    return "main";
}

 

5)使用HttpServletRequest对象提取;

/**
*  姓名:<input name="name"><br>
*  年龄:<input name="age"><br>
*/
@RequestMapping("/five")
    public String five(HttpServletRequest request){
    String name = request.getParameter("name");
    int age = Integer.parseInt(request.getParameter("age"));
    System.out.println("name="+name+",age="+(age+100));
    return "main";
}   

 

8、中文乱码解决方案:


  • 对于前面所接收的请求参数,若含有中文,则会出现中文乱码问题。Spring 对于请求参数中的中文乱码问题,给出了专门的字符集过滤器:
  • spring-web-5.2.5.RELEASE.jar 的 org.springframework.web.filter 包下的 CharacterEncodingFilter 类。

解决方案:

  • 在 web.xml 中注册字符集过滤器,即可解决 Spring 的请求参数的中文乱码问题。不过,最好将该过滤器注册在其它过滤器之前。因为过滤器的执行是按照其注册顺序进行的。
<filter>
    <filter-name>encode</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <!--
            配置参数
            private String encoding;
            private boolean forceRequestEncoding;
            private boolean forceResponseEncoding;
        -->
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
    <init-param>
        <param-name>forceRequestEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
    <init-param>
        <param-name>forceResponseEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>encode</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

     
9、action方法的返回值:


1)String:

处理器方法返回的字符串可以指定逻辑视图名,通过视图解析器解析可以将其转换为物理视图地址;

可以自动拼接前缀和后缀,可以指定返回的路径;

当然,也可以直接返回资源的物理视图名。不过,此时就不需要再在视图解析器中再配置前辍与后辍了。

2)Object:

返回 json 格式的对象,自动将对象或集合转为 json,使用的 jackson 工具进行转换,必须要添加 jackson 依赖,一般用于 ajax 请求;

处理器方法也可以返回 Object 对象,这个 Object 可以是 Integer,自定义对象,Map,List 等。但返回的对象不是作为逻辑视图出现的,而是作为直接在页面显示的数据出现的。返回对象,需要使用@ResponseBody 注解,将转换后的 JSON 数据放入到响应体中。Ajax请求多用于Object返回值类型。

3)void:

无返回值,一般用于ajax请求;

若处理器对请求处理后,无需跳转到其它任何资源,此时可以让处理器方法返回 void。

4)基本数据类型,用于 ajax 请求。

5)ModelAndView:返回数据和视图对象,现在用的很少。

 

10、完成 ajax 请求访问服务器,返回学生集合:


1)添加jackson依赖

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.9.8</version>
</dependency>

 

2)在 webapp 目录下新建 js目录,添加 jQuery 函数库

3)在 index.jsp 页面上导入函数库:

4)在 action 上添加注解@ResponseBody,用来处理ajax请求

@Controller
public class AjaxAction {
    //处理ajax请求,一定要加@ResponseBody
    @ResponseBody
    @RequestMapping("/ajax")
    public List<Student> ajax(){
        Student stu1 = new Student("张三",22);
        Student stu2 = new Student("李四",24);
        Student stu3 = new Student("王五",23);
        List<Student> list = new ArrayList<>();
        list.add(stu1);
        list.add(stu2);
        list.add(stu3);
        //调用json转换工具ObjectMapper进行转换
        return list;  //===>springmvc负责转换成json
        }
    }

 

5)在 springmvc.xml 文件中添加注解驱动 <mvc:annotationdriven/>,它用来解析 @ResponseBody 注解

<mvc:annotation-driven></mvc:annotation-driven>

 

11、SpringMVC 的四种跳转方式:


  • 本质就是两种跳转:请求转发和重定向,衍生出四种是请求转发页面,转发action,重定向页面,重定向action;
  • 默认的跳转是请求转发,直接跳转到 jsp 页面展示;

可以使用框架提供的关键字 redirect: ,进行一个重定向操作,包括重定向页面和重定向action;

可以使用框架提供的关键字forward:,进行服务器内部转发操作,包括转发页面和转发action;

  • 当使用 redirect: 和 forward: 关键字时,视图解析器中前缀后缀的拼接就无效了。

 

栗子:

@RequestMapping("/one")
public String one(){
    System.out.println("这是请求转发页面跳转.........");
    return "main";  //默认是请求转发,使用视图解析器拼接前缀后缀进行页面跳转
}

@RequestMapping("/two")
public String two(){
    System.out.println("这是请求转发action跳转.........");
    //  /admin/  /other.action  .jsp
    //forward: 这组字符串可以屏蔽前缀和后缀的拼接.实现请求转发跳转
    return "forward:/other.action";  //默认是请求转发,使用视图解析器拼接前缀后缀进行页面跳转
}

@RequestMapping("/three")
public String three(){
    System.out.println("这是重定向页面.......");
    //redirect:  这组字符串可以屏蔽前缀和后缀的拼接.实现重定向跳转
    return "redirect:/admin/main.jsp";
}

@RequestMapping("/four")
public String four(){
    System.out.println("这是重定向action.......");
    //redirect:  这组字符串可以屏蔽前缀和后缀的拼接.实现重定向跳转
    return "redirect:/other.action";
}

@RequestMapping("/five")
public String five(){
    System.out.println("这是随便跳.......");

    return "forward:/fore/login.jsp";
}

 

12、SpringMVC 默认的参数类型:


不需要去创建,直接拿来使用即可。

1)HttpServletRequest

2)HttpServletResponse

3)HttpSession

4)Model

5)Map

6)ModelMap

 

栗子:

//做一个数据,传到main.jsp页面上
Users u = new Users("张三",22);

//传递数据
request.setAttribute("requestUsers",u);
session.setAttribute("sessionUsers",u);
model.addAttribute("modelUsers",u);
map.put("mapUsers",u);
modelMap.addAttribute("modelMapUsers",u);

 

注意:Map,Model,ModelMap 和 request 一样,都使用请求作用域进行数据传递,所以服务器端的跳转必须是请求转发,页面才可以得到值。

 

13.日期处理:


1)日期的提交处理

A、单个日期处理

  • 要使用注解@DateTimeFormat,此注解必须搭配 springmvc.xml 文件中的 <mvc:annotationdriven标签>

在方法的参数上使用@DateTimeFormat注解;

在类的成员setXXX()方法上使用@DateTimeFormat注解

  • 但这种解决方案要在每个使用日期类型的地方都去添加使用@DateTimeFormat注解,比较麻烦,我们可以使用@InitBinder注解来进行类中统一日期类型的处理。

 

B.类中全局日期处理

注册一个注解,用来解析本类中所有的日期类型,自动转换

@InitBinder;
public void initBinder(WebDataBinder dataBinder){
    dataBinder.registerCustomEditor(Date.class,new CustomDateEditor(sf,true));
}

 

2)日期的显示处理:

  • 在页面上显示好看的日期,必须使用 JSTL

步骤:

A)添加依赖 jstl

<dependency>
    <groupId>jstl</groupId>
    <artifactId>jstl</artifactId>
    <version>1.2</version>
</dependency>

 

B)在页面上导入标签库 :

  • 如果是单个日期对象,直接转为好看的格式化的字符串进行显示

如果是 list 中的实体类对象的成员变量是日期类型,则必须使用 jstl 进行显示。

<%--导入jstl核心标签库--%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%--导入jstl格式化标签库--%>
<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

 

C)使用标签显示数据:

<table width="800px" border="1">
    <tr>
        <th>姓名</th>
        <th>生日</th>
    </tr>
    <c:forEach items="${list}" var="stu">
        <tr>
            <td>${stu.name}</td>
            <td>${stu.birthday}------ <fmt:formatDate value="${stu.birthday}" pattern="yyyy-MM-dd"></fmt:formatDate></td>
        </tr>
    </c:forEach>
</table>

 

14、SpringMVC 的拦截器:


  • 针对请求和响应进行的额外的处理,在请求和响应的过程中添加预处理,后处理和最终处理

拦截器执行的时机:

1)preHandle():在请求被处理之前进行操作(预处理)

2)postHandle():在请求被处理之后,但结果还没有渲染前进行操作,可以改变响应结果(后处理)

3)afterCompletion:所有的请求响应结束后执行善后工作,清理对象,关闭资源(最终处理)

 

拦截器实现的两种方式:

1)继承 HandlerInterceptorAdapter 的父类

2)实现 HandlerInterceptor 接口

 

拦截器实现的步骤:

1)创建登录方法,在 session 中存储用户信息,用于进行权限验证

@RequestMapping("/login")
public String login(String name, String pwd, HttpServletRequest request){
    if("zar".equalsIgnoreCase(name) && "123".equalsIgnoreCase(pwd)){
        //在session中存储用户信息,用于进行权限验证
        request.getSession().setAttribute("users",name);
        return "main";
    }else{
        request.setAttribute("msg","用户名或密码不正确!");
        return "login";
    }
}

 

2)开发拦截器的功能,实现 HandlerInterceptor 接口,重写 preHandle() 方法:

public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //是否登录过的判断
        if(request.getSession().getAttribute("users") == null){
            //此时就是没有登录,打回到登录页面,并给出提示
            request.setAttribute("msg","您还没有登录,请先去登录!");
            request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request,response);
            return false;
        }
        return true;//放行请求
    }
}

 

3)在 springmvc.xml 文件中注册拦截器:

<mvc:interceptors>
    <mvc:interceptor>
        <!--映射要拦截的请求-->
        <mvc:mapping path="/**"/>
        <!--设置放行的请求-->
        <mvc:exclude-mapping path="/showLogin"></mvc:exclude-mapping>
        <mvc:exclude-mapping path="/login"></mvc:exclude-mapping>
        <!--配置具体的拦截器实现功能的类-->
        <bean class="com.bjpowernode.interceptor.LoginInterceptor"></bean>
    </mvc:interceptor>
</mvc:interceptors>

 

 资源在WEB-INF目录下:

很多企业会将动态资源放在WEB-INF目录下,这样可以保证资源的安全性,因为在WEB-INF目录下的动态资源不可以直接访问,必须要通过请求转发的方式进行访问,这样避免了通过地址栏直接对资源的访问,重定向也无法访问动态资源。

 

栗子:

 

@Controller
public class WebInfAction {
    @RequestMapping("/showMain")
    public String showMain(){//通过请求转发的方式访问webapp目录下的WEB-INF目录下的main.jsp
        System.out.println("访问main.jsp");
        return "main";
    }
    @RequestMapping("/showLogin")
    public String showLogin(){////通过请求转发的方式访问webapp目录下的WEB-INF目录下的login.jsp
        System.out.println("访问login.jsp");
        return "login";
    }
}

 

 

 

posted @ 2022-06-04 20:19  猿头猿脑的王狗蛋  阅读(133)  评论(0编辑  收藏  举报
1