出差在外,利用零零星星的时间进行学习,三部曲总算进入总结尾声阶段

SpringMVC

MVC三层架构

重点:SpringMVC的执行流程

实践:SSM框架整合

将要接触的注解举例:

 @Component      组件
 @Service service
 @Controller controller
 @Repository repository

完成前端和后端的交互

1、回顾MVC

Model模型(dao,service)

View视图(jsp,html)

Controller控制器(servlet)转发、重定向

软件设计规范

MVC主要作用的降低了视图与业务逻辑的双向耦合

MVC不是一种设计模式,而是一种架构模式,不同的MVC也存在差异

Model(模型):数据模型,提供要展示的数据,因此包含数据和行为,可以认为是领域模型或JavaBean组件(包含数据和行为)。现在一般都分离开来:Value Object(数据Dao)和服务层(Service)。模型提供了模型数据查询和模型数据状态更新等功能,包括数据和业务。

View(视图):负责进行模型的展示,一般就是用户界面,客户想看到的东西

Controller(控制器):接收用户请求、委托给模型进行处理(状态改变),处理完毕后把返回的模型数据返回给地面,由视图负责展示。控制器做了一个调度员的工作。

最典型的MVC就是JSP+Servlet+JavaBean的模式

1.用户发请求

2.Servlet接收请求数据,并调用对应的业务逻辑方法

3.业务处理完毕,返回更新后的数据给Servlet

4.servlet转向到JSP,由JSP渲染页面

5.相应给前端更新后的页面

职责分析:

Controller:控制器

1.取得表单数据

2.调用业务逻辑

3.转向指定的页面

Model:模型

1.业务逻辑

2.保存数据的状态

View:视图

1.显示页面


创建一个空Maven项目,删掉src文件夹

导入总项目依赖

 <!--    依赖-->
 <dependencies>
     <dependency>
         <groupId>junit</groupId>
         <artifactId>junit</artifactId>
         <version>3.8.2</version>
         <scope>test</scope>
     </dependency>
     <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-webmvc</artifactId>
         <version>5.2.8.RELEASE</version>
     </dependency>
     <dependency>
         <groupId>javax.servlet</groupId>
         <artifactId>servlet-api</artifactId>
         <version>2.5</version>
     </dependency>
     <dependency>
         <groupId>javax.servlet.jsp</groupId>
         <artifactId>jsp-api</artifactId>
         <version>2.2</version>
     </dependency>
     <dependency>
         <groupId>javax.servlet</groupId>
         <artifactId>jstl</artifactId>
         <version>1.2</version>
     </dependency>
 </dependencies>

 

2、回顾Servlet

新建一个空模板模块,新增框架支持(Add FrameWork Support),选择Java Web

新增Servlet依赖,以后的模块需要和主pom文件依赖保持版本一致

 <dependencies>
     <dependency>
         <groupId>javax.servlet</groupId>
         <artifactId>servlet-api</artifactId>
         <version>2.5</version>
     </dependency>
     <dependency>
         <groupId>javax.servlet.jsp</groupId>
         <artifactId>jsp-api</artifactId>
         <version>2.2</version>
     </dependency>
 </dependencies>

 

编写一个Servlet类,用来处理用户的需求

 package com.zhou.servlet;
 ​
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
 ​
 /**
  * @Name com.zhou.servlet.HelloServlet
  * @Description 回顾
  * @Author 88534
  * @Date 2021/8/30 21:54
  */
 public class HelloServlet extends HttpServlet {
     @Override
     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         //1.获取前端参数
         String method = req.getParameter("method");
         if (method.equals("add")){
             req.getSession().setAttribute("msg","执行了add方法");
         }
         if (method.equals("delete")){
             req.getSession().setAttribute("msg","执行了delete方法");
         }
         //2.调用业务层
//3.视图转发或者重定向
         req.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(req, resp);
     }
 ​
     @Override
     protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         doGet(req, resp);
     }
 }

 

增加Tomcat9作为启动服务器,增加Web模块,创建test.jsp页面

访问http://localhost:8080/hello?method=addhttp://localhost:8080/hello?method=delete可以得到执行方法反馈

MVC框架要做哪些事情?

  1. 将url映射到Java类或Java类的方法

  2. 封装用户提交的数据

  3. 处理请求--调用相关的业务处理--封装响应数据

  4. 将响应的数据进行渲染进行渲染,jsp/html等表示层数据

3、SpringMVC

SpringMVC的特点

1.轻量级,简单易学

2.搞笑,基于请求响应的MVC框架

3.与Spring兼容性好,无缝结合

4.约定优于配置

5.功能强大:RESTful、数据验证、格式化、本地化、主题等

6.简洁灵活,用的人多

用户和Servlet之间增加了一层调度层,负责处理请求,适配URL,跳转页面,可以控制多个Servlet访问不同的服务

3.1、中心控制器

Spring的web框架围绕DispatcherServlet设计,将请求分发到不同的处理器。从Spring 2.5开始,使用Java 5或者以上版本的用户可以采用基于注解的controller声明方式

Spring MVC像许多其他框架一样,以请求为驱动,围绕一个中心Servlet分派请求及提供其他功能。DispatcherServlet是一个实际的Servlet,继承自HttpServlet基类。

3.2、SpringMVC的原理

当发起请求时被前置的控制器拦截到请求,根据请求参数生成代理请求,找到请求对应的实际控制器,控制器处理请求,创建数据模型,访问数据库,将模型响应给中心控制器,控制器使用模型与视图渲染视图效果,将结果返回给中心控制器,再将结果返回给请求者。

 

ModelAndView:模型视图,给前端携带一些数据(model),并且指定前端页面是哪一个(view)

3.3、SpringMVC执行原理

 

 

 

 

Auto表示SpringMVC框架提供的技术,不需要开发者实现,Manual表示需要开发者实现

Controller需要指定具体哪个服务实现

简要分析执行流程

1.DispatcherServlet表示前端控制器,是整个SpringMVC的控制中心,用户发出请求,DispatcherServlet接收请求并拦截请求。

假设请求URL为:http://localhost:8080/SpringMVC/hello,将其拆分成三部分

http://localhost:8080:服务器域名

SpringMVC:部署在服务器上的web站点

hello表示控制器

如上URL表示为:发送HTTP请求位于服务器localhost:8080上的SpringMVC站点的hello控制器

2.HandlerMapping为处理器映射,DispatcherServlet调用HandlerMapping,其根据请求url查找Handler

3.HandlerExecution表示具体的Handler,其主要作用是根据URL查找控制器hello

4.HandlerExecution将解析后的信息传递给DispatcherServlet,解析控制器映射

5.HandlerAdapter表示处理器适配器,其按照特定的规则执行Handler,找处理请求的Controller

6.Handler让具体的Controller执行

7.Controller将具体的执行信息返回给HandlerAdapter,如ModelAndView

8.HandlerAdapter将视图逻辑名或模型传递给DispatcherServlet

9.DispatcherServlet调用视图解析器(ViewResolver)来解析HandlerAdapter传递的逻辑视图名

10.视图解析器将解析的逻辑视图名传给DispatcherServlet

11.DispatcherServlet根据视图解析器解析的视图结果,调用具体的视图

12.最终视图呈现给用户

4、HelloSpringMVC

4.1、配置版

1.新建一个Moudle,添加web的支持

2.确定Maven导入了SpringMVC的依赖

新增lib文件夹,导入依赖(空maven的原因,无法自主创建lib文件夹导入依赖)

打开工程设置(F4)

 

 

 

或者

 

 

 

3.配置web.xml,注册DispatcherServlet

 <?xml version="1.0" encoding="UTF-8"?>
 <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
          version="4.0"><!--    1.注册DispatcherServlet,这个是SpringMVC的核心;请求分发器,前端控制器-->
     <servlet>
         <servlet-name>springmvc</servlet-name>
         <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
 <!--        关联绑定一个springMVC的配置文件:【servlet-name】servlet.xml-->
         <init-param>
             <param-name>contextConfigLocation</param-name>
             <param-value>classpath:springmvc-servlet.xml</param-value>
         </init-param>
 <!--        启动级别1-->
         <load-on-startup>1</load-on-startup>
     </servlet><!--    “/” 匹配所有的请求:不包括.jsp-->
 <!--    “/*” 匹配所有的请求:包括.jsp-->
     <servlet-mapping>
         <servlet-name>springmvc</servlet-name>
         <url-pattern>/</url-pattern>
     </servlet-mapping></web-app>

 

4.编写SpringMVC的配置文件,名称springmvc-servlet.xml:【ServletName】-servlet.xml

5.添加处理映射器BeanNameUrlHandlerMapping

6.添加处理器适配器SimpleControllerHandlerAdapter

7.添加视图解析器InternalResourceViewResolver,增加搜索的前缀和后缀,拼接视图名字,找到对应的视图

 
<?xml version="1.0" encoding="UTF-8"?>
 <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
         https://www.springframework.org/schema/beans/spring-beans.xsd"><!--    处理器映射器HandlerMapping-->
     <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
 <!--    处理器适配器HandlerAdapter-->
     <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/><!--    视图解析器:DispatcherServlet的ModelAndView-->
 <!--    模板引擎:Thymeleaf、Freemarker……-->
 <!--    1.获取了ModelAndView的数据-->
 <!--    2.解析了ModelAndView的视图名字-->
 <!--    3.拼接视图名字,找到对应的视图 hello-->
     <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
 <!--        前缀,最后需要/-->
         <property name="prefix" value="/WEB-INF/jsp/"/>
 <!--        后缀-->
         <property name="suffix" value=".jsp"/>
     </bean></beans>

 

8.编写要操作业务的Controller,要么实现Controller接口,要么增加注解;需要返回一个ModelAndView,装数据,封视图。

 package com.zhou.controller;
 ​
 import org.springframework.web.servlet.ModelAndView;
 import org.springframework.web.servlet.mvc.Controller;
 ​
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 ​
 /**
  * @Name HelloController
  * @Description 导入Controller接口,写业务代码、视图跳转
  * @Author 88534
  * @Date 2021/8/31 10:17
  */
 public class HelloController implements Controller {
     @Override
     public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
         //ModelAndView模型和视图
         ModelAndView mv = new ModelAndView();
 ​
         //封装对象,放在ModelAndView中,业务代码
         mv.addObject("msg","HelloSpringMVC!");
 ​
         //封装要跳转的视图,放在ModelAndView中,跳转视图
         //:/WEB-INF/jsp/hello.jsp
         mv.setViewName("hello");
         return mv;
     }
 }

 

9.将自己的类交给SpringIOC容器,注册bean,springmvc-servlet.xml

 <!--    BeanNameUrlHandlerMapping:bean-->
     <bean id="/hello" class="com.zhou.controller.HelloController"/>

 

10.写要跳转的jsp页面,显示ModelAndView存放的数据,以及我们的正常画面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Hello</title>
</head>
<body>
    ${msg}
</body>
</html>

 

11.配置Tomcat的启动测试

 

 

 

12.运行成功,输入/hello发送请求,跳转到hello页面

 

 

 

全程使用Controller代替了Servlet,不需要写Servlet

4.2、注解版

由于Maven可能存在资源过滤问题,需要在pom文件中加入一些build过滤器

新建一个模块

<build>
 <resources>
   <resource>
     <directory>
       src/main/resources
     </directory>
     <excludes>
       <exclude>**/*.properties</exclude>
       <exclude>**/*.xml</exclude>
     </excludes>
     <filtering>true</filtering>
   </resource>
   <resource>
     <directory>
       src/main/java
     </directory>
     <excludes>
       <exclude>**/*.properties</exclude>
       <exclude>**/*.xml</exclude>
     </excludes>
     <filtering>true</filtering>
   </resource>
 </resources>
</build>

 

配置web.xml

注意:

  • web.xml的版本,必须要最新版

  • 关联SpringMVC的配置文件

  • 注册DispatcherServlet

  • 启动级别为1

  • 映射路径为/【不要用/*,会404】

  • 其余操作同 4.1 配置版第3步

添加SpringMVC配置文件

  • 让IOC的注解生效

  • 静态资源过滤:HTML、JS、CSS、图片、视频……

  • MVC的注解驱动

  • 配置视图解析器

在resource目录下添加springmvc-servlet.xml配置文件,配置的形式与Spring容器配置基本类似,为了支持基于注解的IOC,设置了自动扫描包的功能,具体配置信息如下:

开头的bean需要增加context和mvc的约束文件,加入context、mvn导包后自动添加

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        https://www.springframework.org/schema/mvc/spring-mvc.xsd">

<!--    自动扫描包,让指定包下的注解生效,由IOC容器统一管理-->
    <context:component-scan base-package="com.zhou.controller"/>

<!--    让SpringMVC不处理(过滤)静态资源       .css    .js    .html   .mp3    .mp4 -->
    <mvc:default-servlet-handler/>

<!--
        支持mvc注解驱动
            在Spring中一般采用@RequestMapping注解来完成映射关系
            要想使@RequestMapping注解生效
            必须向上下文中注册DefaultAnnotationHandlerMapping
            和一个AnnotationMethodHandlerAdapter实例
            这两个实例分别在类级别和方法级别处理
            而annotation-driven配置帮助我们自动完成上述两个实例的注入
-->
    <mvc:annotation-driven/>

    <!--    视图解析器:DispatcherServlet的ModelAndView-->
    <!--    模板引擎:Thymeleaf、Freemarker……-->
    <!--    1.获取了ModelAndView的数据-->
    <!--    2.解析了ModelAndView的视图名字-->
    <!--    3.拼接视图名字,找到对应的视图 hello-->
    <!--    4.将数据渲染到这个视图上-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
          id="internalResourceViewResolver">
        <!--        前缀-->
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <!--        后缀-->
        <property name="suffix" value=".jsp"/>
    </bean>

</beans>

 

创建Controller

package com.zhou.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * @Name HelloController
 * @Description 使用注解实现
 * @Author 88534
 * @Date 2021/8/31 13:43
 */
@Controller
@RequestMapping("/HelloController")
public class HelloController {
    /**
     * 真实访问地址:项目名/HelloController/hello
     * @param model
     * @return
     */
    @RequestMapping("/hello")
    public String hello(Model model){
        //封装数据,向模型中添加属性msg与值,可以在JSP页面中取出再渲染
        model.addAttribute("msg","Hello,SpringMVCAnnotation!");

        //hello会被视图解析器处理,地址://WEB-INF/jsp/hello.jsp
        return "hello";
    }
}

 

  • @Controller是为了让Spring IOC容器初始化时自动扫描到

  • @RequestMapping是为了映射请求路径,类与方法都有映射时访问地址应该叠加

  • 方法中声明Model类型的参数是为了把Action中的数据带到视图中

  • 方法返回的结果是视图的名称hello,加上配置文件配置的视图解析器的前后缀得到前端页面地址

  • 有了注解就不需要去Spring配置文件中注册bean了,需要去掉

@RestController不会被视图解析器解析,返回的直接是一个字符串,如用来传json

创建视图层

hello.jsp直接取出并展示从Controller带回的信息msg

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>hello</title>
</head>
<body>
${msg}
</body>
</html>

 

4.3、小结

  1. 新建一个web项目

  2. 导入相关jar包

  3. 编写web.xml,注册DispatcherServlet

  4. 编写springmvc配置文件

  5. 接下来就是创建对应的控制类Controller

  6. 最后完善前端视图和controller之间的对应

  7. 测试运行调试

使用SpringMVC必须配置的三大件

处理器映射器、处理器适配器、视图解析器

通常只需要手动配置视图解析器,确定如何寻找前端页面的地址,有哪些公共部分设置为前后缀(加入包名、文件类型等)

而处理器映射器和处理器适配器只需要开启注解驱动即可,省去了大段的xml配置

5、控制器Controller

5.1、定义

控制器提供访问复杂应用程序的行为,通常通过接口定义或注解定义两种方法实现

控制器负责解析用户的请求并将其转换为一个模型

在SpringMVC中一个控制器类可以包含多个方法

在SpringMVC中,对于Controller的配置方式有很多种

5.2、实现Controller接口

一种原生方法,不推荐

重复配置内容不再赘述

package com.zhou.controller;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @Name Controller
 * @Description 只要实现了Controller接口的类,说明这就是一个控制器了
 * @Author 88534
 * @Date 2021/8/31 14:21
 */
public class ControllerTest1 implements Controller {
    @Override
    public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
        ModelAndView mv = new ModelAndView();
        mv.addObject("msg","ControllerTest1");
        mv.setViewName("test1");
        return mv;
    }
}

 

此时前端视图层为

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>test1</title>
</head>
<body>
    ${msg}
</body>
</html>

 

5.3、使用注解@Controller(重要)

用于声明Spring类的实例是一个控制器

Spring可以使用扫描机制找到应用程序中所有基于注解的控制器类,为了保证Spring能找到控制器,需要在配置文件中声明组件扫描

<context:component-scan base-package="com.zhou.controller"/>
package com.zhou.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * @Name ControllerTest2
 * @Description 使用注解代表这个类会被Spring接管,被这个注解的类中的所有方法如果返回值是String,并且有具体的页面可以跳转,name就会被视图解析器解析
 * @Author 88534
 * @Date 2021/8/31 14:37
 */
@Controller
public class ControllerTest2 {
    @RequestMapping("/t2")
    public String test1(Model model){
        model.addAttribute("msg","ControllerTest2");
        return "test1";
    }
}

 

如果两种Controller的返回值相同,则两个请求都可以指向同一个视图,但是显示不一样的页面结果,msg发生了改变。从这里可以看出视图是被复用的,而控制器与视图之间是弱耦合关系。

注解方式是使用最多的方式

5.4、RequestMapping

@RequestMapping

用于映射URL到控制器类或一个特定的处理程序方法,可用于类或方法上,用于类上,表示类中的所有相应请求的方法都是以改地址作为父路径。

只注解在方法上面

访问路径:http://localhost:8080/项目名/t1

@Controller
public class ControllerTest1 {
    @RequestMapping("/t1")
    public String test3(Model model){
        model.addAttribute("msg","ControllerTest1");
        return "test1";
    }
}

 

同时注解类与方法

访问路径:http://localhost:8080/项目名/c3/t3

需要先指定类的路径再指定方法的路径

类似于总-分结构

@Controller
@RequestMapping("/c3")
public class ControllerTest3 {
    @RequestMapping("/t3")
    public String test3(Model model){
        model.addAttribute("msg","ControllerTest3");
        return "test3";
    }
}

 

6、RestFul风格

是一个资源定位及资源操作的风格,不是标准也不是协议,只是一种风格,基于这个风格设计的软件可以更简洁、更有层次,更基于实现缓存等机制。

6.1、功能

资源:互联网所有的事物都可以被抽象为资源

资源操作:使用POST、DELETE、PUT、GET,使用不同方法对资源进行操作

分别对应添加、删除、修改、查询

6.2、传统方式操作资源

通过不同的参数来实现不同的效果,方法单一,POST和GET

6.3、使用RESTFul操作资源

可以通过不同的请求方式来实现不同的效果

请求地址一样,功能可以不同

6.4、学习测试

原先做法

@Controller
public class RestTulController {
    
    @RequestMapping("/add")
    public String test1(int a, int b, Model model){
        int res = a+b;
        model.addAttribute("msg","结果为" + res);
        return "test1";
    }
}

 

输入:http://localhost:8080/add?a=1&b=2

得到输出结果:

结果为3

RestFul:http://localhost:8080/add/a/b

在@RequestMapping里加入{参数},函数参数列表里加入@PathVariable

@Controller
public class RestTulController {

    @RequestMapping("/add/{a}/{b}")
    public String test1(@PathVariable int a, @PathVariable int b, Model model){
        int res = a+b;
        model.addAttribute("msg","结果为" + res);
        return "test1";
    }
}

 

输出结果相同

使用method可以约束请求类型PUT/GET/POST/DELETE

@RequestMapping(value = "/add/{a}/{b}",method = RequestMethod.GET)

优点:不暴露内部操作,隐藏程序内的东西。简洁,高效。缓存机制。

7、结果跳转方式

7.1、ModelAndView

设置ModelAndView对象,根据view的名称,如视图解析器跳转到指定的页面

页面真实地址:(视图解析器前缀)+viewName+(视图解析器后缀)

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
      id="internalResourceViewResolver">
    <!--        前缀-->
    <property name="prefix" value="/WEB-INF/jsp/"/>
    <!--        后缀-->
    <property name="suffix" value=".jsp"/>
</bean>

 

如果不使用,需要在return返回的字符串里加入前后缀/WEB-INF/jsp/xxx.jsp

7.2、ServletAPI

通过设置ServletAPI,不需要视图解析器

  1. 通过HttpServletResponse进行输出resp.getWriter().println("xxx")

  2. 通过HttpServletResponse实现重定向resp.sendRedirect("xxx")

  3. 通过HttpServletResponse实现转发resp.getRequestDispatcher("xxx").forward(req,resp)

7.3、SpringMVC

通过SpringMVC来实现转发和重定向,无需视图解析器

测试前,需要将视频解析器注释掉

重定向:redirect

@Controller
public class ModelTest1 {
    @RequestMapping("/m1/t1")
    public String test(Model model){
        model.addAttribute("msg","ModelTest1");
        //重定向
        return "redirect:/index.jsp";
    }
}

 

转发forward,会调用视图解析器

@Controller
public class ModelTest1 {
    @RequestMapping("/m2/t2")
    public String test2(Model model){
        model.addAttribute("msg","ModelTest1");
        //转发
        return "/index.jsp";
        //forward:/index.jsp
    }
}

 

重定向不需要视图解析器,无法访问WEB-INF的内容,本质是重新请求一个新地方

扩展:重定向和转发的区别

转发:

req.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(req, resp);

重定向:

resp.sendRedirect("/WEB-INF/jsp/test.jsp");

1、请求次数

重定向是浏览器向服务器发送一个请求并收到响应后再次向一个新地址发出请求,转发是服务器收到请求后为了完成响应跳转到一个新的地址;重定向至少请求两次,转发请求一次;

2、地址栏不同

重定向地址栏会发生变化,转发地址栏不会发生变化;

3、是否共享数据

重定向两次请求不共享数据,转发一次请求共享数据(在request级别使用信息共享,使用重定向必然出错);

4、跳转限制

重定向可以跳转到任意URL,转发只能跳转本站点资源;

5、发生行为不同

重定向是客户端行为,转发是服务器端行为;

举例:在淘宝内点击各类商品界面是转发行为,在淘宝打开支付宝是重定向

区别转发forward()重定向sendRedirect()
根目录 包含项目访问地址 没有项目访问地址
地址栏 不会发生变化 会发生变化
哪里跳转 服务器端进行的跳转 浏览器端进行的跳转
请求域中数据 不会丢失 会丢失

8、数据处理

8.1、处理提交数据

1.提交的域名称个处理方法的参数名一致,@RequestParam可以不加

http://localhost:8080/user/t1?name=xxx

2.提交的域名称和处理方法的参数名不一致

@Controller
@RequestMapping("/user")
public class UserController {
    @RequestMapping("/t1")
    public String test1(@RequestParam("username") String name, Model model){
        //1.接收前端参数
        System.out.println("接收到前端的参数为:" + name);
        //2.将返回的结果传递给前端
        model.addAttribute("msg",name);
        //3.跳转视图
        return "test1";
    }
}

 

提交数据:http://localhost:8080/user/t1?username=xxx

@RequestParam与提交的参数名保持一致

输出:

网页:xxx

控制台:接收到前端的参数为:xxx

如果使用对象的话,前端传递的参数名和对象名保持一致,否则就是null

8.2、数据显示到前端

8.2.1、ModelAndView

参考之前使用的方法handleRequest()、addAttribute()、addObject()等等

/**
 * @Name Controller
 * @Description 只要实现了Controller接口的类,说明这就是一个控制器了
 * @Author 88534
 * @Date 2021/8/31 14:21
 */
public class ControllerTest1 implements Controller {
    public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
        ModelAndView mv = new ModelAndView();
        mv.addObject("msg","ControllerTest1");
        mv.setViewName("test1");
        return mv;
    }
}

 

8.2.2、Model

封装要显示到视图中的数据

类似8.1

8.2.3、ModelMap

ModelMap modelmap替换Model

三者对比:

Model只有几个方法,只适合用于存储数据,简化了操作和理解

ModelMap继承了LinkedHashMap,除了实现自身的方法,还继承了一些别的方法和特性

ModelAndView可以在存储数据的同时,可以进行设置返回的逻辑视图

9、乱码问题

输入中文乱码时,通过过滤器解决,在web.xml中配置

<!--    配置Spring MVC的乱码过滤-->
    <filter>
        <filter-name>encoding</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encoding</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

 


前后端分离时代:

后端部署后端,提供接口,提供数据

--

json实现前后端交互

--

前端独立部署,负责渲染后端的数据

10、JSON

JSON(JavaScript Object Notation,JS对象标记)是一种轻量级的数据交换格式

采用完全独立于编程语言的文本格式来存储和表示数据

简介和清晰的层次结构,理想的数据交换语言

易于人阅读和编写,也易于机器解析和生成,并有效的提升网络传输效率

对象表示键值对,数据由逗号分隔

花括号保存对象

方括号保存数组

键值对组合中的键名写在前边并用双引号“”包裹,使用冒号:分隔,然后紧接数值

{"name":"xxx"}
{"age":"20"}
{"sex":"男"}

JSON是JavaScript对象的字符串表示法,使用文本表示一个JS对象的信息,本质是一个字符串

10.1、JSON和JavaScript对象互转

JSON-->JavaScript: JSON.parse(JSON) 变成可展开的JavaScript对象

var obj = JSON.parse('{a:"hello"},{b:"world"}');

输出:

{a:"hello"},{b:"world"}

JavaScript-->JSON: JSON.stringify(JavaScript对象) 变成JSON字符串输出

var json = JSON.stringify({a:"hello"},{b:"world"});

输出:

‘{a:"hello"},{b:"world"}’

10.2、Controller返回JSON数据

Jackson应该是目前比较好的JSON解析工具,还有阿里巴巴的fastjson等

使用Jackson工具,导入jar包

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

 

resource文件夹加入配置文件springmvc-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <context:component-scan base-package="com.zhou.controller"/>

    <mvc:default-servlet-handler/>

<!--    json乱码配置-->
    <mvc:annotation-driven>
        <mvc:message-converters>
            <bean class="org.springframework.http.converter.StringHttpMessageConverter">
                <constructor-arg value="UTF-8"/>
            </bean>
            <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                <property name="objectMapper">
                    <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
                        <property name="failOnEmptyBeans" value="false"/>
                    </bean>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
          id="internalResourceViewResolver">
        <!--        前缀-->
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <!--        后缀-->
        <property name="suffix" value=".jsp"/>
    </bean>

</beans>

 

简单用法http://localhost:8080/j0

@RequestMapping(value = "/j0")
@ResponseBody
public String json0() throws JsonProcessingException {
    User user = new User("周",10,"男");
    return user.toString();
}

 

输出结果:

User(name=周, id=10, sex=男)

使用@Controller和@RequestBody组合,等效于只是用@RestController

package com.zhou.controller;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zhou.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * @Name UserController
 * @Description
 * @Author 88534
 * @Date 2021/9/14 19:15
 */
//@RestController
@Controller
public class UserController {
    /**
     * 加了@ResponseBody不会走视图解析器,会直接返回一个字符串
     * @return
     */
    @RequestMapping(value = "/j1")
    @ResponseBody
    public String json1() throws JsonProcessingException {
        //创建一个jackson对象映射器,可解析数据
        ObjectMapper mapper = new ObjectMapper();
        //创建一个对象
        User user = new User("周",10,"男");
        //对象解析成JSON格式
        String str = mapper.writeValueAsString(user);
        System.out.println(user);
        return str;
    }
}

 

输入网址:http://localhost:8080/j1

输出结果:

{"name":"周","id":10,"sex":"男"}

也可以输出一组JSON数据,结果利用[ ]括起来

package com.zhou.controller;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zhou.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.ArrayList;
import java.util.List;

/**
 * @Name UserController
 * @Description
 * @Author 88534
 * @Date 2021/9/14 19:15
 */
@Controller
public class UserController {
    /**
     * 加了ResponseBody不会走视图解析器,会直接返回一个字符串
     * @return
     */
    @RequestMapping(value = "/j2")
    @ResponseBody
    public String json2() throws JsonProcessingException {
        //创建一个jackson对象映射器,可解析数据
        ObjectMapper mapper = new ObjectMapper();

        List<User> userList = new ArrayList<>();

        User user1 = new User("周1",11,"男");
        User user2 = new User("周2",12,"男");
        User user3 = new User("周3",13,"男");
        User user4 = new User("周4",14,"男");

        userList.add(user1);
        userList.add(user2);
        userList.add(user3);
        userList.add(user4);

        //对象解析成JSON格式
        String str = mapper.writeValueAsString(userList);
        System.out.println(userList);
        return str;
    }
}

 

输出结果:

[{"name":"周1","id":11,"sex":"男"},{"name":"周2","id":12,"sex":"男"},{"name":"周3","id":13,"sex":"男"},{"name":"周4","id":14,"sex":"男"}]

传递时间

/**
 * 显示时间
 * @return
 * @throws JsonProcessingException
 */
@RequestMapping(value = "/j3")
@ResponseBody
public String json3() throws JsonProcessingException {
    ObjectMapper mapper = new ObjectMapper();

    //时间戳
    Date date = new Date();

    //自定义日期格式
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    return mapper.writeValueAsString(sdf.format(date));
}

 

输出结果:

"2021-09-14 22:29:38"

10.3、fastjson

阿里开发的一款专门用于Java开发的包,可以方便的实现json对象与JavaBean对象的转换,实现JavaBean对象与JSON字符串的转换,实现JSON对象与JSON字符串的转换。

pom文件导入jar包

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.78</version>
</dependency>

 

三个主要的类:

JSONObject:对象

实现了Map接口,底层由Map实现

对应JSON对象,通过各种形式的get()方法获取对象中的数据

可利用size()、isEmpty()等方法获取键值对的个数和判断是否为空

本质是通过Map接口并调用接口中的方法完成的

JSONArray:对象数组

内部是List接口完成操作

JSON:完成前两者的转换

示例代码:

package com.zhou.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zhou.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @Name UserController
 * @Description
 * @Author 88534
 * @Date 2021/9/14 19:15
 */
@Controller
public class UserController {
    
    /**
     * fastjson
     * @return
     * @throws JsonProcessingException
     */
    @RequestMapping(value = "/j4")
    @ResponseBody
    public String json4() {
        List<User> userList = new ArrayList<>();

        User user1 = new User("周1",11,"男");
        User user2 = new User("周2",12,"男");
        User user3 = new User("周3",13,"男");
        User user4 = new User("周4",14,"男");

        userList.add(user1);
        userList.add(user2);
        userList.add(user3);
        userList.add(user4);

        //Java对象转JSON字符串
        String s1 = JSON.toJSONString(user1);
        System.out.println(s1);
        //JSON字符串转Java对象
        User jp_user1 = JSON.parseObject(s1,User.class);
        System.out.println(jp_user1);
        //Java对象转JSON对象
        JSONObject jsonObject = (JSONObject) JSON.toJSON(user2);
        System.out.println(jsonObject.getString("name"));
        //JSON对象转Java对象
        User to_java_user = JSON.toJavaObject(jsonObject,User.class);
        System.out.println(to_java_user);

        String s = JSON.toJSONString(userList);
        return s;
    }
}

 

输出结果:

{"id":11,"name":"周1","sex":"男"}

User(name=周1, id=11, sex=男)

周2

User(name=周2, id=12, sex=男)

11、Ajax技术

AJAX = Asynchronous JavaScript and XML (异步的JavaScript和XML)

无需重新加载整个网页的情况下,能够更新部分网页

不是一种新的编程语言,而是一种用于创建更好更快以及交互性更强的Web应用程序的技术

类似于百度搜索框,加入文字后会自动弹出下拉框,不需要刷新网页

传统的网页不使用Ajax技术,想要更新内容或者提交一个表单,都需要重新加载整个网页。

使用Ajax技术的网页,通过在后台服务器进行少量的数据交换,就可以实现异步局部更新

使用Ajax,用户可以创建接近本地桌面应用的指节、高可用、更丰富、更动态的Web用户界面。

11.1、伪造Ajax

新建一个module,导入web支持

简单写一个test.html,使用iframe测试

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>iframe测试体验页面无刷新</title>

    <script>
        function go() {
            //所有的值变量,提前获取
            var url = document.getElementById("url").value;
            document.getElementById("iframe1").src=url;
        }
    </script>

</head>
<body>
<div>
    <p>请输入地址:</p>
    <p>
        <input type="text" id="url" value="http://localhost:8080/">
        <input type="button" value="提交" onclick="go()">
    </p>

</div>
<div>
    <iframe id="iframe1" style="width: 100%;height: 500px"></iframe>
</div>
</body>
</html>

 

访问http://localhost:8080/t1

 

 

 

 

 

实现了网页内页面的跳转

利用Ajax可以做

  • 注册时,输入用户名自动检测用户是否已经存在

  • 登录时,提醒用户名密码错误

  • 删除数据行时,将行ID发送到后台,后台在数据库中删除,数据库删除成功后,在页面DOM中将数据行也删除,不需要刷新页面重新查询更新

  • ……

11.2、jQuery.ajax

Ajax的核心是XMLHttpRequest对象(XHR),XHR为向服务器发送请求和解析服务器响应提供了接口,能够以异步方式从服务器获取新数据。jQuery对XHR进行了封装,方便调用

通过jQuery AJAX方法,使用HTTP GET和HTTP POST从远程服务器上请求文本、HTML、XML或JSON,能够把这些外部数据直接载入网页的备选元素中

去官网下载最新的jquery-3.6.0.js,导入到工程目录中

 

 

jQuery.ajax简写$

$.type({
    url:"${pageContext.request.contextPath}/xxx",
    data:{"key":"value"},
    success:function(){xxx},
    error:function(){yyy};
})

 

 

jQuery.ajax(...) 部分参数: url:请求地址 type:请求方式,GET、POST(1.9.0之后用method)可以写成$.get()、$.post() headers:请求头 data:要发送的数据(key:value键值对形式) contentType:即将发送信息至服务器的内容编码类型(默认: "application/x-www-form-urlencoded; charset=UTF-8") async:是否异步 timeout:设置请求超时时间(毫秒) beforeSend:发送请求前执行的函数(全局) complete:完成之后执行的回调函数(全局) success:成功之后执行的回调函数(全局)callback error:失败之后执行的回调函数(全局)callback accepts:通过请求头发送给服务器,告诉服务器当前客户端可接受的数据类型 dataType:将服务器端返回的数据转换成指定类型 "xml": 将服务器端返回的内容转换成xml格式 "text": 将服务器端返回的内容转换成普通文本格式 "html": 将服务器端返回的内容转换成普通文本格式,在插入DOM中时,如果包含JavaScript标签,则会尝试去执行。 "script": 尝试将返回值当作JavaScript去执行,然后再将服务器端返回的内容转换成普通文本格式 "json": 将服务器端返回的内容转换成相应的JavaScript对象 "jsonp": JSONP 格式使用 JSONP 形式调用函数时,如 "myurl?callback=?" jQuery 将自动替换 ? 为正确的函数名,以执行回调函数

获取失焦动作

环境搭建

加入依赖(父工程有之前的也可)

配置文件导入applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <context:component-scan base-package="com.zhou.controller"/>
    <!--静态资源过滤-->
    <mvc:default-servlet-handler/>
    
    <!--    json乱码配置-->
    <mvc:annotation-driven>
        <mvc:message-converters>
            <bean class="org.springframework.http.converter.StringHttpMessageConverter">
                <constructor-arg value="UTF-8"/>
            </bean>
            <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                <property name="objectMapper">
                    <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
                        <property name="failOnEmptyBeans" value="false"/>
                    </bean>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
          id="internalResourceViewResolver">
        <!--        前缀-->
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <!--        后缀-->
        <property name="suffix" value=".jsp"/>
    </bean>

</beans>

 

创建controller文件夹——com.zhou.controller.AjaxController

package com.zhou.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Name AjaxController
 * @Description
 * @Author 88534
 * @Date 2021/9/22 22:50
 */
@RestController
public class AjaxController {
    @RequestMapping("/a1")
    public void a1(String name, HttpServletResponse response) throws IOException {
        System.out.println("a1:param=>" + name);
        if (name.equals("zhou")){
            response.getWriter().print("true");
        } else {
            response.getWriter().print("false");
        }
    }
}

 

编写index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>$Title$</title>
  </head>

  <script src="${pageContext.request.contextPath}/static/js/jquery-3.4.1.js"></script>

  <script>
    function a() {
      $.post({
        url:"${pageContext.request.contextPath}/a1",
        data:{"name":$("#username").val()},
        success:function (data) {
            alert(data);
        }
      });
    }
  </script>

  <body>

<%--  失去焦点的时候,发起一个请求到后台--%>
  用户名:<input type="text" id="username" onblur="a()"/>

  </body>
</html>

 

鼠标离开输入框的时候(输入框光标消失)

 

 

前后端分离思想

 

 

后端只需要提供Json数据字符串

前端通过Ajax发起各种url请求,完成data数据的收集,实现各类callback事件的处理

11.3、Ajax异步加载请求

导入转换为JSON的依赖

<dependencies>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.16</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-core</artifactId>
        <version>2.11.0</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.11.0</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-annotations</artifactId>
        <version>2.11.0</version>
    </dependency>
</dependencies>

 

实体类User

package com.zhou.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @Name User
 * @Description
 * @Author 88534
 * @Date 2021/9/23 15:29
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private String name;
    private int age;
    private String sex;
}

 

controller层获取集合对象

@RequestMapping("/a2")
public List<User> a2(){
    List<User> userList = new ArrayList<>();

    //添加数据
    userList.add(new User("后端",25,"男"));
    userList.add(new User("前端",24,"女"));
    userList.add(new User("运维",26,"男"));

    //由于@RestController注解,将list转成json格式返回
    return userList;
}

 

前端页面test2.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>数据展示</title>

  <script src="${pageContext.request.contextPath}/static/js/jquery-3.4.1.js"></script>

  <script>
    $(function () {
      $("#btn").click(function () {
        $.post("${pageContext.request.contextPath}/a2",function (data) {
          console.log(data);
          var html = "";
          for (let i = 0; i < data.length; i++) {
            html += "<tr>" +
                    "<td>" + data[i].name + "</td>" +
                    "<td>" + data[i].age + "</td>" +
                    "<td>" + data[i].sex + "</td>" +
                    "</tr>";
          }
          $("#content").html(html);
        });
      })
    });
  </script>
</head>

<body>
<input type="button" value="加载数据" id="btn">
<table width="80%" align="center">
  <tr>
    <td>姓名</td>
    <td>年龄</td>
    <td>性别</td>
  </tr>
  <tbody id="content">
<%--  从后台拿数据--%>
  </tbody>
</table>
</body>

</html>

 

访问http://localhost:8080/test2.jsp

点击按钮,实现数据回显

 

 

11.4、Ajax验证用户名

controller

@RequestMapping("/a3")
public String a3(String name,String pwd){
    String msg = "";
    if (name != null){
        //这些数据可以在数据库中查
        if (name.equals("admin")){
            msg = "OK";
        } else {
            msg = "用户名有误";
        }
    }if (pwd != null){
        //这些数据可以在数据库中查
        if (pwd.equals("admin")){
            msg = "OK";
        } else {
            msg = "密码有误";
        }
    }
    return msg;
}

 

前端页面 login.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登录界面</title>

    <script src="${pageContext.request.contextPath}/static/js/jquery-3.4.1.js"></script>

    <script>
        function a1() {
            $.post({
                url:"${pageContext.request.contextPath}/a3",
                data:{"name":$("#name").val()},
                success:function (data) {
                    if (data.toString()=="OK"){
                        $("#userInfo").css("color","green");
                    }else {
                        $("#userInfo").css("color","red");
                    }
                    $("#userInfo").html(data);
                }
            })
        }
        function a2() {
            $.post({
                url:"${pageContext.request.contextPath}/a3",
                data:{"pwd":$("#pwd").val()},
                success:function (data) {
                    if (data.toString()=="OK"){
                        $("#pwdInfo").css("color","green");
                    }else {
                        $("#pwdInfo").css("color","red");
                    }
                    $("#pwdInfo").html(data);
                }
            })
        }
    </script>

</head>
<body>
<p>
    用户名:<input type="text" id="name" onblur="a1()">
    <span id="userInfo"></span>
</p>
<p>
    密码:<input type="password" id="pwd" onblur="a2()">
    <span id="pwdInfo"></span>
</p>
</body>
</html>

 

动态请求响应,局部更新

输入http://localhost:8080/login.jsp

 

 

12、拦截器

SpringMVC的处理器拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。

过滤器与拦截器的区别:拦截器是AOP思想的具体应用

过滤器:

Servlet规范中的一部分,任何JavaWeb工程都可以使用

在url-pattern中配置了/*之后,可以对所有要访问的资源进行拦截

拦截器:

属于SpringMVC,只有使用了SpringMVC框架的工程才能使用

拦截器只会拦截访问的控制器controller方法,如果访问的是jsp/html/css/image/js是不会进行拦截的(自带静态过滤),效率更高

12.1、自定义拦截器

写一个类,实现HandleInterceptor接口

新建一个module,添加web支持

pom文件加入jackson解析JSON

<dependencies>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.16</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-core</artifactId>
        <version>2.11.0</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.11.0</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-annotations</artifactId>
        <version>2.11.0</version>
    </dependency>
</dependencies>

 

配置web.xml、applicationContext.xml与Ajax类似,创建controller类

applicationContext.xml增加一个拦截器配置

<!--    拦截器配置-->
    <mvc:interceptors>
        <mvc:interceptor>
<!--            包括这个请求下面的所有请求-->
            <mvc:mapping path="/**"/>
            <bean class="com.zhou.config.MyInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>

 

在structure里导包Artifacts,配置Tomcat启动选项

加入controller

package com.zhou.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Name TestController
 * @Description
 * @Author 88534
 * @Date 2021/9/23 19:40
 */
@RestController
public class TestController {
    @RequestMapping("/interceptor")
    public String test(){
        System.out.println("TestController==>test()执行了");
        return "OK";
    }
}

 

加入拦截器

package com.zhou.config;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @Name interceptor
 * @Description
 * @Author 88534
 * @Date 2021/9/23 19:43
 */
public class MyInterceptor implements HandlerInterceptor {
    /**
     * return true:执行下一个拦截器,放行
     * return false:不执行下一个拦截器,卡住
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("=============处理前================");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("=============处理后================");
    }

    /**
     * 清理
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("=============清理================");
    }
}

 

输入:http://localhost:8080/interceptor

后台控制台查看拦截器执行顺序

=============处理前================
TestController==>test()执行了
=============处理后================
=============清理================

12.2、登录判断验证

12.2.1、实现思路

1、有一个登录页面,需要写一个controller访问页面。

2、登录页面有一提交表单的动作。需要在controller中处理。判断用户名密码是否正确。如果正确,向session中写入用户信息。返回登录成功,进入首页。错误,重新登录。

3、拦截用户请求,判断用户是否登录。如果用户已经登录。放行, 如果用户未登录,跳转到登录页面。

12.2.2、前端页面

1.初始页index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>拦截器</title>
  </head>
  <body>
  <a href="${pageContext.request.contextPath}/user/goLogin">登录页面</a>
  <a href="${pageContext.request.contextPath}/user/main">首页</a>
  </body>
</html>

 

2.登录页面login.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登录界面</title>
</head>
<body>
<h1>登录页面</h1>
<form action="${pageContext.request.contextPath}/user/login" method="post">
    用户名:<input type="text" name="username">
    密码:<input type="password" name="password">
    <input type="submit" value="提交">
</form>
</body>
</html>

 

3.登录成功页面、首页main.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>首页</title>
</head>
<body>
<h1>首页</h1>
<span>${username}</span>
<p>
    <a href="${pageContext.request.contextPath}/user/goOut">注销</a>
</p>
</body>
</html>

 

12.2.3、跳转页面controller

package com.zhou.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

/**
 * @Name LoginController
 * @Description 跳转页面
 * @Author 88534
 * @Date 2021/9/23 20:39
 */
@Controller
@RequestMapping("/user")
public class LoginController {
    @RequestMapping("/login")
    public String login(HttpSession session, String username, String password, Model model){
        //把用户的信息存在session中
        //判断可以通过访问数据库解决
        if ("admin".equals(username)&&"admin".equals(password)){
            session.setAttribute("userLoginInfo",username);
            model.addAttribute("username",username);
            return "main";
        } else {
            return "login";
        }
    }

    @RequestMapping("/main")
    public String main(HttpSession session, Model model){
        String userLoginInfo = (String) session.getAttribute("userLoginInfo");
        model.addAttribute("username",userLoginInfo);
        return "main";
    }

    @RequestMapping("/goLogin")
    public String goLogin(HttpServletRequest request, Model model){
        HttpSession session = request.getSession();
        if (session.getAttribute("userLoginInfo") != null){
            String userLoginInfo = (String) session.getAttribute("userLoginInfo");
            model.addAttribute("username",userLoginInfo);
            return "main";
        } else {
            return "login";
        }
    }

    @RequestMapping("/goOut")
    public String goOut(HttpSession session){
        session.removeAttribute("userLoginInfo");
        return "login";
    }
}

 

12.2.4、登录拦截器

package com.zhou.config;

import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

/**
 * @Name LoginInterceptor
 * @Description 登录拦截器
 * @Author 88534
 * @Date 2021/9/23 20:53
 */
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session = request.getSession();
        //放行:判断什么情况下登录
        //前往登录页面也会放行;第一次登录,无session
        if (request.getRequestURI().contains("goLogin")||request.getRequestURI().contains("login")){
            return true;
        }

        //session中已经有userLoginInfo,重新访问
        if (session.getAttribute("userLoginInfo") != null){
            return true;
        }

        //判断什么情况下没有登录,重定向无法到WEB-INF内
        //不满足上述情况的拦截,前往登录页面
        request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request,response);
        return false;
    }
}

 

拦截器配置:applicationContext.xml插入

<mvc:interceptor>
    <mvc:mapping path="/user/**"/>
    <bean class="com.zhou.config.LoginInterceptor"/>
</mvc:interceptor>

 

12.2.5、测试

启动Tomcat,直接访问http://localhost:8080/来到初始页

  1. 单击“首页”、“登录页面”的超链接均会跳转到登录界面

  2. 输入密码后,正确则能进入首页,显示用户名;错误则重新返回登录界面

  3. 登录成功后重新回到初始页,单击“首页”、“登录页面”的超链接均会跳转到首页界面,无需重复登录

  4. 首页单击注销后,返回登录页面。如果再去直接访问首页会被拦截。

13、文件上传和下载

SpringMVC上下文中默认没有装配MultipartResolver,因此默认情况下其不能处理文件上传工作。如果想使用Spring的文件上传功能,则需要在上下文中配置MultipartResolver

前端表单要求:为了能上传文件,必须将表单的method设置为POST,并将enctype设置为multipart/form-data。

浏览器才会把用户选择的文件以二进制数据形式发送给服务器

13.1、表单的enctype属性

  • application/x-www=form-urlencoded:默认方式,只处理表单域中的 value 属性值,采用这种编码方式的表单会将表单域中的值处理成 URL 编码方式。

  • multipart/form-data:这种编码方式会以二进制流的方式来处理表单数据,这种编码方式会把文件域指定文件的内容也封装到请求参数中,不会对字符编码。

  • text/plain:除了把空格转换为 "+" 号外,其他字符都不做编码处理,这种方式适用直接通过表单发送邮件。

13.2、开源的Commons FileUpload组件

  • Servlet3.0规范已经提供方法来处理文件上传,但这种上传需要在Servlet中完成。

  • 而Spring MVC则提供了更简单的封装。

  • Spring MVC为文件上传提供了直接的支持,这种支持是用即插即用的MultipartResolver实现的。

  • Spring MVC使用Apache Commons FileUpload技术实现了一个MultipartResolver实现类:

  • CommonsMultipartResolver。因此,SpringMVC的文件上传还需要依赖Apache Commons FileUpload的组件。

导入文件上传的jar包,commons-fileupload

<!--文件上传-->
<dependency>
   <groupId>commons-fileupload</groupId>
   <artifactId>commons-fileupload</artifactId>
   <version>1.3.3</version>
</dependency>
<!--servlet-api导入高版本的-->
<dependency>
   <groupId>javax.servlet</groupId>
   <artifactId>javax.servlet-api</artifactId>
   <version>4.0.1</version>
</dependency>

 

配置bean:multipartResolver

<!--文件上传配置-->
<bean id="multipartResolver"  class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
   <!-- 请求的编码格式,必须和jSP的pageEncoding属性一致,以便正确读取表单的内容,默认为ISO-8859-1 -->
   <property name="defaultEncoding" value="utf-8"/>
   <!-- 上传文件大小上限,单位为字节(10485760=10M) -->
   <property name="maxUploadSize" value="10485760"/>
   <property name="maxInMemorySize" value="40960"/>
</bean>

 

CommonsMultipartFile 的 常用方法:

  • String getOriginalFilename():获取上传文件的原名

  • InputStream getInputStream():获取文件流

  • void transferTo(File dest):将上传文件保存到一个目录文件中

文件下载步骤:

1、设置 response 响应头

2、读取文件 -- InputStream

3、写出文件 -- OutputStream

4、执行操作

5、关闭流 (先开后关)

13.3、实际测试

上传和下载

前端页面

上传

<form action="/upload" enctype="multipart/form-data" method="post">
 <input type="file" name="file"/>
 <input type="submit" value="upload">
</form>

 

下载

<a href="/download">点击下载</a>

 

或href直接接下载地址即可下载(文件放在web目录下)

controller

package com.zhou.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.commons.CommonsMultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;

/**
 * @Name FileController
 * @Description
 * @Author 88534
 * @Date 2021/9/23 22:17
 */
@RestController
public class FileController {
    @RequestMapping("/upload")
    //@RequestParam("file") 将name=file控件得到的文件封装成CommonsMultipartFile 对象
    //批量上传CommonsMultipartFile则为数组即可
    public String fileUpload(@RequestParam("file") CommonsMultipartFile file , HttpServletRequest request) throws IOException {

        //获取文件名 : file.getOriginalFilename();
        String uploadFileName = file.getOriginalFilename();

        //如果文件名为空,直接回到首页!
        if ("".equals(uploadFileName)){
            return "redirect:/index.jsp";
        }
        System.out.println("上传文件名 : "+uploadFileName);

        //上传路径保存设置
        String path = request.getServletContext().getRealPath("/upload");
        //如果路径不存在,创建一个
        File realPath = new File(path);
        if (!realPath.exists()){
            realPath.mkdir();
        }
        System.out.println("上传文件保存地址:"+realPath);

        //文件输入流
        InputStream is = file.getInputStream();
        //文件输出流
        OutputStream os = new FileOutputStream(new File(realPath,uploadFileName));

        //读取写出
        int len=0;
        byte[] buffer = new byte[1024];
        while ((len=is.read(buffer))!=-1){
            os.write(buffer,0,len);
            os.flush();
        }
        os.close();
        is.close();
        return "redirect:/index.jsp";
    }

    /**
     * 采用file.TransTo 来保存上传的文件
     * @param file
     * @param request
     * @return
     * @throws IOException
     */
    @RequestMapping("/upload2")
    public String fileUpload2(@RequestParam("file") CommonsMultipartFile file, HttpServletRequest request) throws IOException {

        //上传路径保存设置
        String path = request.getServletContext().getRealPath("/upload");
        File realPath = new File(path);
        if (!realPath.exists()){
            realPath.mkdir();
        }
        //上传文件地址
        System.out.println("上传文件保存地址:"+realPath);

        //通过CommonsMultipartFile的方法直接写文件(注意这个时候)
        file.transferTo(new File(realPath +"/"+ file.getOriginalFilename()));

        return "redirect:/index.jsp";
    }

    @RequestMapping(value="/download")
    public String downloads(HttpServletResponse response , HttpServletRequest request) throws Exception{
        //要下载的图片地址
        String path = request.getServletContext().getRealPath("/upload");
        String  fileName = "Java.png";

        //1、设置response 响应头
        // 设置页面不缓存,清空buffer
        response.reset();
        //字符编码
        response.setCharacterEncoding("UTF-8");
        //二进制传输数据
        response.setContentType("multipart/form-data");
        //设置响应头
        response.setHeader("Content-Disposition",
                "attachment;fileName="+ URLEncoder.encode(fileName, "UTF-8"));

        File file = new File(path,fileName);
        //2、 读取文件--输入流
        InputStream input=new FileInputStream(file);
        //3、 写出文件--输出流
        OutputStream out = response.getOutputStream();

        byte[] buff =new byte[1024];
        int index=0;
        //4、执行 写出操作
        while((index= input.read(buff))!= -1){
            out.write(buff, 0, index);
            out.flush();
        }
        out.close();
        input.close();
        return "ok";
    }
}

 

前端表单提交地址修改

文件上传到out文件夹对应的目录上

下载采用download访问会直接弹出下载窗,采用超链接会直接打开该文件

 

posted on 2021-09-24 14:13  zrm0612  阅读(44)  评论(0)    收藏  举报