1.什么是SpringMVC
1.1、什么是MVC
- MVC是模型(Model)、视图(View)、控制器(Controller)的简写,是一种软件设计规范。
- 是将业务逻辑、数据、显示分离的方法来组织代码。
- MVC主要作用是降低了视图与业务逻辑间的双向偶合。
- MVC不是一种设计模式,MVC是一种架构模式。当然不同的MVC存在差异。
Model(模型):数据模型,提供要展示的数据,因此包含数据和行为,可以认为是领域模型或JavaBean组件(包含数据和行为),不过现在一般都分离开来:Value Object(数据Dao) 和 服务层(行为Service)。也就是模型提供了模型数据查询和模型数据的状态更新等功能,包括数据和业务。
View(视图):负责进行模型的展示,一般就是我们见到的用户界面,客户想看到的东西。
Controller(控制器):接收用户请求,委托给模型进行处理(状态改变),处理完毕后把返回的模型数据返回给视图,由视图负责展示。也就是说控制器做了个调度员的工作。
最典型的MVC就是JSP + servlet + javabean的模式。
1.2、Spring概述
Spring MVC是Spring Framework的一部分,是基于Java实现MVC的轻量级Web框架。
查看官方文档:https://docs.spring.io/spring/docs/5.2.0.RELEASE/spring-framework-reference/web.html#spring-web
我们为什么要学习SpringMVC呢?
Spring MVC的特点:
- 轻量级,简单易学
- 高效 , 基于请求响应的MVC框架
- 与Spring兼容性好,无缝结合
- 约定优于配置
- 功能强大:RESTful、数据验证、格式化、本地化、主题等
- 简洁灵活
Spring的web框架围绕DispatcherServlet [ 调度Servlet ] 设计。
DispatcherServlet的作用是将请求分发到不同的处理器。从Spring 2.5开始,使用Java 5或者以上版本的用户可以采用基于注解形式进行开发,十分简洁;
正因为SpringMVC好 , 简单 , 便捷 , 易学 , 天生和Spring无缝集成(使用SpringIoC和Aop) , 使用约定优于配置 . 能够进行简单的junit测试 . 支持Restful风格 .异常处理 , 本地化 , 国际化 , 数据验证 , 类型转换 , 拦截器 等等......所以我们要学习 .
最重要的一点还是用的人多 , 使用的公司多 .
1.3、中心控制器
Spring的web框架围绕DispatcherServlet设计。DispatcherServlet的作用是将请求分发到不同的处理器。从Spring 2.5开始,使用Java 5或者以上版本的用户可以采用基于注解的controller声明方式。
Spring MVC框架像许多其他MVC框架一样, 以请求为驱动 , 围绕一个中心Servlet分派请求及提供其他功能,DispatcherServlet是一个实际的Servlet (它继承自HttpServlet 基类)。
SpringMVC的原理如下图所示:
当发起请求时被前置的控制器拦截到请求,根据请求参数生成代理请求,找到请求对应的实际控制器,控制器处理请求,创建数据模型,访问数据库,将模型响应给中心控制器,控制器使用模型与视图渲染视图结果,将结果返回给中心控制器,再将结果返回给请求者。
1.4、SpringMVC执行原理
图为SpringMVC的一个较完整的流程图,实线表示SpringMVC框架提供的技术,不需要开发者实现,虚线表示需要开发者实现。
简要分析执行流程
DispatcherServlet表示前置控制器,是整个SpringMVC的控制中心。用户发出请求,DispatcherServlet接收请求并拦截请求。
我们假设请求的url为 : http://localhost:8080/SpringMVC/hello
如上url拆分成三部分:
http://localhost:8080服务器域名
SpringMVC部署在服务器上的web站点
hello表示控制器
通过分析,如上url表示为:请求位于服务器localhost:8080上的SpringMVC站点的hello控制器。
HandlerMapping为处理器映射。DispatcherServlet调用HandlerMapping,HandlerMapping根据请求url查找Handler。
HandlerExecution表示具体的Handler,其主要作用是根据url查找控制器,如上url被查找控制器为:hello。
HandlerExecution将解析后的信息传递给DispatcherServlet,如解析控制器映射等。
HandlerAdapter表示处理器适配器,其按照特定的规则去执行Handler。
Handler让具体的Controller执行。
Controller将具体的执行信息返回给HandlerAdapter,如ModelAndView。
HandlerAdapter将视图逻辑名或模型传递给DispatcherServlet。
DispatcherServlet调用视图解析器(ViewResolver)来解析HandlerAdapter传递的逻辑视图名。
视图解析器将解析的逻辑视图名传给DispatcherServlet。
DispatcherServlet根据视图解析器解析的视图结果,调用具体的视图。
最终视图呈现给用户。
在这里先听一遍原理,不理解没有关系,我们马上来写一个对应的代码实现大家就明白了,如果不明白,那就写10遍,没有笨人,只有懒人!
2.实现SpringMVC(配置版)
- 新建一个Moudle,添加Web支持。
- 确定导入了SpringMVC的依赖
- 配置web.xml,注册DispatcherServlet前端控制器,这是SpringMVC核心。
<?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>
<!-- 也可不配置参数,默认加载 /WEB-INF/springmvc-servlet.xml -->
<!-- DispatcherServlet要绑定一个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>
- 配置SpringMVC的配置文件:springmvc-servlet.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--1.添加处理映射器-->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<!--2.添加处理器适配器-->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
<!--3.添加视图解析器:DispatcherServlet给它的ModelAndView
1.获取了ModelAndView的数据
2.解析ModelAndView的视图名字
3.拼接视图名字,找到对应的视图 /WEB-INF/jsp/hello.jsp
4.将数据渲染到这个视图上
-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
<!--前缀-->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!--后缀-->
<property name="suffix" value=".jsp"/>
</bean>
<!--4.Handler-->
<bean id="/hello" class="com.dzj.controller.HelloController" />
</beans>
- 创建Controller类
package com.dzj.controller;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
//注意:我们这里先导入Controller接口
public class HelloController implements Controller {
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception{
//模型和视图
ModelAndView mv = new ModelAndView();
//业务代码,假设这里处理了一个业务,返回的结果为result
String result = "HelloSpringMVC!";
//封装对象,放在ModelAanView中,Model
mv.addObject("msg",result);
//视图跳转封装要跳转的视图,放在ModelAndView中,view
mv.setViewName("hello"); // 自动拼接成:/WEB-INF/jsp/hello.jsp
return mv;
}
}
3.实现SpringMVC(注解版)
第一步:新建一个Moudle,添加web支持,建立包结构 com.dzj.controller
第二步:由于Maven可能存在资源过滤问题,我们将配置完善!
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
第三步:在pom.xml文件引入相关的依赖:主要有Spring框架核心库、SpringMVC、servlet、JSTL等。如果在父依赖中已经引入了,就不需要再引入。注意:在项目结构下添加 lib文件夹,添加 Library Files
<!--导入依赖-->
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.9</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet/servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet.jsp/jsp-api -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2.1-b03</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet/jstl -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
<!--资源过滤-->
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
第四步:配置web.xml文件
- 注册DispatcherServlet
- 关联SpringMVC配置文件
- 启动级别为1
- 映射路劲为 “ / ” ,【不要用“ /* ” ,会404】
<!--1.注册前端控制器:DispatcherServlet,这是SpringMVC的核心-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 也可不配置参数,默认加载 /WEB-INF/springmvc-servlet.xml -->
<!-- DispatcherServlet要绑定一个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>
第五步:添加Spring MVC配置文件
<?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
http://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.dzj.controller"/>
<!-- 让Spring MVC 不处理静态资源 .css .js .html .mp3 .mp4-->
<mvc:default-servlet-handler />
<!--
支持mvc注解驱动
在spring中一般采用@RequestMapping注解来完成映射关系;
要想使@RequestMapping注解生效;
必须想上下文中注册DefaultAnnotationHandlerMapping和一个AnnotationMethodHandlerAdapter实例,
这两个实体分别在类级别和方法级别处理;
而annotation-driver配置帮助我们自动完成上述两个实例的注入。
-->
<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,HelloController.java
@Controller
public class HelloController {
@RequestMapping("/hello")
public String hello(Model model){
//在模型中添加 msg 和值,可以在jsp中取出并渲染
model.addAttribute("msg","Hello,SpringMVCAnnotation!");
return "hello"; // hello 会被视图解析器处理
}
}
第七步:创建视图层,hello.jsp
第八步:配置Tomcat运行
4.注解使用说明
@Controller
//被@Controller注解的类,代表这个类被Spring托管,此类中的方法如果返回值是String类型,
// 并且有具体的页面可以跳转,那么就会被视图解析器解析
@Controller
public class ControllerTest2 {
@RequestMapping("/test2")
public String test2(Model model){
model.addAttribute("msg","ControllerTest2");
return "test";
}
}
@RequestMapping
@Controller
@RequestMapping("/c3") //标注在类上,可理解为第一层级目录
public class ControllerTest3 {
@RequestMapping("/test2") //标注在方法上,可理解为第二层级目录
public String test3(Model model){
model.addAttribute("msg","ControllerTest3");
return "test";
}
}
@Controller
public class ControllerTest3 {
@RequestMapping("/c3/test2") //等价于上面的写法
public String test3(Model model){
model.addAttribute("msg","ControllerTest3");
return "test";
}
}
5.RestFul风格
@Controller
public class RestFulController {
// 原来的方式 ---> http://localhost:8080/add?a=3&b=7
// RestFul方式 ---> http://localhost:8080/add/1/2
// @RequestMapping(value = "/add/{a}/{b}",method = RequestMethod.DELETE)
@PostMapping("/add/{a}/{b}")
public String test1(@PathVariable int a,@PathVariable String b, Model model){
//a,b为前端传递过来的参数
String res = a + b;
model.addAttribute("msg","结果为1:" + res);
return "test";
}
@GetMapping("/add/{a}/{b}")
public String test2(@PathVariable int a,@PathVariable String b, Model model){
//a,b为前端传递过来的参数
String res = a + b;
model.addAttribute("msg","结果为2:" + res);
return "test";
}
}
6.SpringMVC的结果跳转方式
1.ModelAndView
设置ModelAndView对象,根据view的名称,和视图解析器跳转到指定的页面
页面 = {视图解析器前缀} + viewName + {视图解析器后缀}
<!--视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--前缀-->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!--后缀-->
<property name="suffix" value=".jsp"/>
</bean>
对应的Controller类
public class HelloController implements Controller {
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
//模型和视图
ModelAndView mv = new ModelAndView();
//业务代码,假设这里处理了一个业务,返回的结果为result
String result = "HelloSpringMVC!";
//封装对象,放在ModelAanView中,Model
mv.addObject("msg",result);
//视图跳转封装要跳转的视图,放在ModelAndView中,view
mv.setViewName("hello"); // 自动拼接成:/WEB-INF/jsp/hello.jsp
return mv;
}
}
2.ServletAPI
@Controller
public class ModelTest1 {
@RequestMapping("/m1")
public String test1(HttpServletRequest req, HttpServletResponse resp){
HttpSession session = req.getSession();
System.out.println(session.getId());
return "test";
}
}
3.SpringMVC
不需要视图解析器
@Controller
public class ModelTest1 {
@RequestMapping("/m1")
public String test1(Model model){
model.addAttribute("msg","ModleTest1");
//转发,地址栏没有发生变化
return "/WEB-INF/jsp/test.jsp"; //注意使用全路径
// 也可写成这样 return "forward:/WEB-INF/jsp/test.jsp";
}
}
@Controller
public class ModelTest1 {
@RequestMapping("/m1")
public String test1(Model model){
model.addAttribute("msg","ModelTest1");
//重定向,地址栏会发生变化
return "redirect:/index.jsp";
}
}
有视图解析器
默认情况下为转发,在前面加上redirect为重定向
@Controller
public class ModelTest1 {
@RequestMapping("/m1")
public String test1(Model model){
model.addAttribute("msg","ModelTest1");
//转发
return "test";
}
@RequestMapping("/m2")
public String test2(Model model){
model.addAttribute("msg","ModelTest1");
//重定向
return "redirect:/index.jsp";
}
}
7.SpringMVC数据处理
1.处理提交数据
提交的域名称和处理方法的参数一致的情况
@Controller
@RequestMapping("/user")
public class UserController {
// http://localhost:8080/user/t1?name=dengzhijiang
@GetMapping("t1")
public String Test(String name, Model model){
//1.接受前端数据
System.out.println("接受前端的数据为:" + name);
//2.将业务处理后的结果返回给前端,Model
model.addAttribute("msg",name);
//3.视图跳转
return "test";
}
}
提交的域名称和处理方法的参数不一致的情况
只需在参数前加上 @RequestParma,建议以后不管一不一样都加上
@Controller
@RequestMapping("/user")
public class UserController {
// http://localhost:8080/user/t1?name=dengzhijiang
@GetMapping("t1")
public String Test(@RequestParam("username") String name, Model model){
//1.接受前端数据
System.out.println("接受前端的数据为:" + name);
//2.将业务处理后的结果返回给前端,Model
model.addAttribute("msg",name);
//3.视图跳转
return "test";
}
}
前端接受的是一个对象
1.接受前端用户传递的参数,判断参数的名字,假设名字直接在方法上,可以直接使用
2.假设传递的是一个对象User,匹配对象User中的字段,如果名字一致则ok,否则匹配不到
3.如果使用对象的话,前端传递的参数名,和对象名属性必须一致,否则就是null
@GetMapping("t2")
public String Test2(User user, Model model){
// http://localhost:8080/user/t2?id=11&name=dengzhijiang&age=22
System.out.println(user);
model.addAttribute("msg",user);
//3.视图跳转
return "test";
}
2.数据显示到前端
第一种:通过ModelAndView
public class HelloController implements Controller {
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
//模型和视图
ModelAndView mv = new ModelAndView();
//业务代码,假设这里处理了一个业务,返回的结果为result
String result = "HelloSpringMVC!";
//封装对象,放在ModelAanView中,Model
mv.addObject("msg",result);
//视图跳转封装要跳转的视图,放在ModelAndView中,view
mv.setViewName("hello"); // 自动拼接成:/WEB-INF/jsp/hello.jsp
return mv;
}
}
第二种:通过Model
@Controller
public class HelloController {
@RequestMapping("/hello")
public String hello(Model model){
//在模型中添加 msg 和值,可以在jsp中取出并渲染
model.addAttribute("msg","Hello,SpringMVCAnnotation!");
return "hello"; // hello 会被视图解析器处理
}
}
第三种:通过ModelMap
3.乱码问题
1.自定义过滤器处理乱码
创建一个过滤器
public class EncodingFilter implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {
}
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
servletRequest.setCharacterEncoding("utf-8");
servletResponse.setCharacterEncoding("utf-8");
filterChain.doFilter(servletRequest,servletResponse);
}
public void destroy() {
}
}
在web.xml中配置过滤器
<filter>
<filter-name>encoding</filter-name>
<filter-class>com.dzj.filter.EncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
2.可以把Post方法改成Get方法
3.配置SpringMVC的乱码过滤器
<!--2.配置SpringMVC的乱码过滤器-->
<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>
8.JSON
- JSON是JavaScript对象的字符串表示法,它使用文本表示表示一个JS对象的信息,本质是一个字符串。
var json = {"name":"邓志江","age":3,"sex":"男"} //这是一个JSON字符串
var obj = {name: '邓志江', age: 3, sex: '男'} //这是一个Javas对象
- JSON 和 JavaScript对象的互转
//JavaScript对象转换JSON对象,使用 JSON.stringfy()方法
var json = JSON.stringify(user);
//JSON转换成JavaScript对象,使用 JSON.parse()方法
var obj = JSON.parse(json);
9.Controller返回JSON数据
1.使用 jackson 方式
1.导入依赖
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.3</version>
</dependency>
2.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>
3.实现对象转换为 JSON 字符串
//@Controller
@RestController //如果在类上标注了@RestController,就不会走视图解析器
public class UserController {
@RequestMapping("/j1")
//@ResponseBody //加了这个注解,就不会走仕途解析器,会直接返回一个字符串,配合@Controller使用
public String json1() throws JsonProcessingException {
//jackson里有一个ObjectMapper对象,可以把user对象转换成json字符串
ObjectMapper mapper = new ObjectMapper();
User user = new User("邓志江", 22, "男");
String str = mapper.writeValueAsString(user);// 把user对象转换为JSON字符串
return str;
}
}
4.实现集合类型转换为 JSON 字符串
@RequestMapping("/j2")
public String json2() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
List<User> userList = new ArrayList<User>();
User user1 = new User("邓志江1号", 22, "男");
User user2 = new User("邓志江2号", 22, "男");
User user3 = new User("邓志江3号", 22, "男");
User user4 = new User("邓志江4号", 22, "男");
User user5 = new User("邓志江5号", 22, "男");
userList.add(user1);
userList.add(user2);
userList.add(user3);
userList.add(user4);
userList.add(user5);
String str = mapper.writeValueAsString(userList);// 把user对象转换为JSON字符串
return str;
}
5.实现日期类型转换为 JSON 字符串
@RequestMapping("/j3")
public String json3() throws JsonProcessingException {
Date date = new Date();
//把时间按照一定的格式显示
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//ObjectMapper将时间解析后的格式为:timestamp,时间戳
return new ObjectMapper().writeValueAsString(sdf.format(date));
}
6.源码思想
方法的重载,方法名相同,根据参数的不同调用不同的方法
public static String getJson(Object object){
return getJson(object,"yyyy-MM-dd HH:mm:ss");
}
public static String getJson(Object object, String dateFormat){
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,false);
//把时间按照一定的格式显示
SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
mapper.setDateFormat(sdf);
try {
String str = mapper.writeValueAsString(object);
return str;
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return null;
}
2.使用fastjson方式
1.添加 fastjson 依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.78</version>
</dependency>
2.直接使用 JSON.toJSONString()实现转换
@RequestMapping("/j4")
public String json4(){
List<User> userList = new ArrayList<User>();
User user1 = new User("邓志江1号", 22, "男");
User user2 = new User("邓志江2号", 22, "男");
User user3 = new User("邓志江3号", 22, "男");
User user4 = new User("邓志江4号", 22, "男");
User user5 = new User("邓志江5号", 22, "男");
userList.add(user1);
userList.add(user2);
userList.add(user3);
userList.add(user4);
userList.add(user5);
return JSON.toJSONString(userList); //把集合转化为JSON字符串
}
10.SpringMVC:Ajax技术
10.1 简介
10.2 伪造Ajax
1. 新建一个Moudule
新建一个Moudule:springmvc-06-ajax,导入web支持
2. 编写一个test.html
编写一个test.html 使用 iframe 测试,感受页面无刷新效果
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>iframe测试体验页面无刷新</title>
<script type=text/javascript>
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="https://www.baidu.com/" />
<input type="button" value="提交" onclick="go()"/>
</p>
</div>
<div>
<iframe id="iframe1" style="width: 100%;height: 500px"></iframe>
</div>
</body>
</html>
10.3 实现Ajax
1. 下载并导入jQuery
2. 添加静态资源过滤
在applicationContext.xml中添加静态资源过滤
<!--静态资源过滤-->
<mvc:default-servlet-handler/>
3. 编写一个前端页面
实现:当失去焦点的时候,发起一个请求
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
<%--引入jQuery--%>
<script src="${pageContext.request.contextPath}/statics/js/jquery-3.6.0.js"></script>
<script>
function a() {
//通过post方式向后台发起请求
$.post({
url:"${pageContext.request.contextPath}/a1", //请求地址
data:{"name":$("#username").val()}, //传递的数据
success:function (data,status) { //请求之后的回调函数,返回数据,data接受返回的数据
console.log("data="+data);
console.log("status="+status);// status 表示请求的状态 200 300 400 500
})
}
</script>
</head>
<body>
<%--失去焦点的时候,发起一个请求--%>
用户名:<input type="text" id="username" onblur="a()">
</body>
</html>
4. 后台接受数据返回结果
注意:方法中的参数要和前端ajax中传递的参数名一致
data:{"name":$("#username").val()}
@RestController
public class AjaxController {
@RequestMapping("/a1")
public void a1(String name, HttpServletResponse response) throws IOException {
System.out.println("a=>"+name);
if("kuangshen".equals(name)){
response.getWriter().print("true");
}else {
response.getWriter().print("false");
}
}
}
小结:根据Ajax的地址,后台接收数据,并返回结果,并没有进行重定向或者转发的操作,因此就不会刷新页面,实现了前端页面的局部更新。
10.4 Ajax异步加载数据
1. 创建一个User类
注意:使用注解的话,记记得添加 lombok 依赖,并且把jar包添加到 lib 目录中
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
<scope>provided</scope>
</dependency>
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private String name;
private int age;
private String sex;
}
2. 创建前端页面
body>
<input id="btn" type="button" value="加载数据" />
<table>
<tr>
<td>名称</td>
<td>年龄</td>
<td>性别</td>
</tr>
<tbody id="content">
</tbody>
</table>
</body>
3. 通过Ajax想后台发起请求
<%-- 引入 jQuery --%>
<script src="${pageContext.request.contextPath}/statics/js/jquery-3.6.0.js"></script>
<script>
window.onload=function(){
$("#btn").click(function () {
console.log("成功了没??");
/*
简写的方式:$.post(url, param[可省略] , success)
*/
$.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>
4. 后台实现数据返回到前端
@RequestMapping("/a2")
public List<User> a2(){
// 创建几个对象,添加到集合中返回到前端
List<User> userList = new ArrayList<User>();
userList.add(new User("前端",1,"女"));
userList.add(new User("后端",2,"男"));
userList.add(new User("运维",3,"男"));
return userList;
}
小结:前端通过 Ajax 先向后端发起请求,后台处理请求,并把结果返回到前端,前端把接收到的数据进行处理,显示到页面中。
10.5 Ajax验证用户信息体验
1. 创建前端页面login.jsp
<body>
<p>
用户名:<input type="text" id="userName" onblur="a1()">
<span id="userId"></span>
</p>
<p>
密码:<input type="text" id="passWord" onblur="a2()">
<span id="passWordId"></span>
</p>
</body>
2. 通过Ajax想后台发起请求
<script src="${pageContext.request.contextPath}/statics/js/jquery-3.6.0.js"></script>
<script>
function a1() {
$.get({
url:"${pageContext.request.contextPath}/a3",
data:{"name":$("#userName").val()},
success:function (data) {
console.log(data);
if(data.toString()==='ok'){
$("#userId").css("color","green");
}else{
$("#userId").css("color","red");
}
$("#userId").html(data);
}
})
}
function a2() {
$.get({
url:"${pageContext.request.contextPath}/a3",
data:{"password":$("#passWord").val()},
success:function (data) {
console.log(data);
if(data.toString()==='ok'){
$("#passWordId").css("color","green");
}else{
$("#passWordId").css("color","red");
}
$("#passWordId").html(data);
}
})
}
</script>
3. 后台实现数据返回前端
@RequestMapping("/a3")
public String a3(String name,String password){
String msg = "";
if(name!=null){
if("admin".equals(name)){
msg = "ok";
}else{
msg = "用户名有误!";
}
}
if(password!=null){
if("123456".equals(password)){
msg = "ok";
}else{
msg = "密码有误!";
}
}
return msg;
}
11.拦截器
11.1 简单实用拦截器
1.创建一个控制器
@RestController
public class TestController {
@GetMapping("/t1")
public String test1(){
System.out.println("执行成功了");
return "ok";
}
}
创建一个拦截器
public class MyInterceptor implements HandlerInterceptor {
//return true ,执行下一个拦截器,放行
//return false ,不执行下一个拦截器
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("*************处理前*************");
return false;
}
//下面两个方法可写可不写,主要用于日志返回
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("*************处理后*************");
}
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("*************清理*************");
}
}
3. 配置拦截器
<!--拦截器配置-->
<mvc:interceptors>
<mvc:interceptor>
<!--包括这个请求下面的所有请求-->
<mvc:mapping path="/**"/>
<bean class="com.dzj.config.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
11.2 登录判断验证
1. 入口
<body>
<h1>
<a href="${pageContext.request.contextPath}/user/goLogin">登录页面</a>
<a href="${pageContext.request.contextPath}/user/main">首页</a>
</h1>
</body>
2. 登录页
<body>
<%-- 在web-inf下面所有的页面或者资源,只能通过controller或者servlet进行访问--%>
<h1>登录页面</h1>
<form action="${pageContext.request.contextPath}/user/login" method="post">
用户名:<input type="text" name="username">
密码:<input type="text" name="password">
<input type="submit" value="提交">
</form>
</body>
3. 首页
<body>
<h1>首页</h1>
<span>${username}</span>
<p>
<a href="${pageContext.request.contextPath}/user/goOut">注销</a>
</p>
</body>
4. 新建控制器
@Controller
@RequestMapping("/user")
public class LoginController {
@RequestMapping("/main")
public String main(){
return "main";
}
@RequestMapping("/goLogin")
public String goLogin(){
return "Login";
}
@RequestMapping("/login")
public String login(String username, String password, HttpSession session, Model model){
session.setAttribute("usernameInfo",username);
model.addAttribute("username",username);
return "main";
}
@RequestMapping("/goOut")
public String goOut(HttpSession session){
//移除节点
session.removeAttribute("usernameInfo");
return "main";
}
}
5. 创建拦截器
public class LoginInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession();
// 放行,判断什么情况下登录
//登录页面也会放行
if(request.getRequestURI().contains("goLogin")){
return true;
}
//说明我在提交登录
if(request.getRequestURI().contains("login")){
return true;
}
//注销登录
if(request.getRequestURI().contains("goOut")){
return true;
}
//第一次登录也是没有session的
if((session.getAttribute("userLoginInfo")!=null)&&(request.getRequestURI().contains("main"))){
return true;
}
//判断什么情况下没有登陆
request.getRequestDispatcher("/WEB-INF/jsp/Login.jsp").forward(request,response);
return false;
}
}
6. 配置拦截器
<mvc:interceptor>
<!--包括user这个请求下面的所有请求-->
<mvc:mapping path="/user/**"/>
<bean class="com.dzj.config.LoginInterceptor"/>
</mvc:interceptor>
12.SpringMVC:文件上传和下载
12.1 准备工作
12.2 文件上传
1. 导入文件上传的jar包
导入文件上传的jar包,commons-fileupload,Maven会自动帮我们导入他的依赖包commons-io
<!-- 文件上传-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
<!-- servlet-api导入高版本的 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
2. 配置bean:multipartResolver
注意:这个 bean 的 id 必须为:multipartResolver,否则上传文件会报404的错误!
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 请求的编码格式,必须和jSP的pageEncoding属性一致,以便正确读取表单的内容,默认为ISO-8859-1 -->
<property name="defaultEncoding" value="UTF-8"/>
<!-- 上传文件大小上限,单位为字节(10MB) -->
<property name="maxUploadSize" value="10485760"/>
<property name="maxInMemorySize" value="40960"/>
</bean>
3. 编写前端页面
<body>
<form action="${pageContext.request.contextPath}/upload" enctype="multipart/form-data" method="post">
<input type="file" name="file"/>
<input type="submit" value="upload"/>
</form>
<a href="${pageContext.request.contextPath}/statics/1.png">下载图片</a>
</body>
四、创建后台控制器
@RestController
public class FileController {
//@RequestParam("file") 将name=file控件得到的文件封装成ConmmonsMultipartFile对象
//批量上传CommonsMultipartFile则为数组即可
/*
采用file.Transto 来保存上传的文件
*/
@RequestMapping("/upload")
public String fileUpload(@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";
}
}
12.3 文件下载
文件下载的步骤:
- 设置 response 响应头
- 读取文件 -- InputStream
- 写出文件 -- OutputStream
- 执行操作
- 关闭流(先开后关)
@RequestMapping(value="/download")
public String downloads(HttpServletResponse response,HttpServletRequest request) throws Exception {
//要下载的图片地址
String path = request.getServletContext().getRealPath("/upload");
String fileName = "1.png";
//1.设置response相应头
response.reset();//设置页面不缓存,清空buffer
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 null;
}
直接前端这样也行
<a href="${pageContext.request.contextPath}/statics/1.png">下载图片</a>