SpringMVC
ssm:Mybatis+Spring+SpringMVC
SpringMVC+Vue+SpringBoot+SpringCloud+Linux
SSM:JavaWeb做项目
Spring:IOC+AOP
SpringMVC:SpringMVC的执行流程(理论,面试要点)
SpringMVC:SSM框架整合(实践)
1 回顾MVC
1.1 回顾MVC
MVC:模型(dao,service) 视图(jsp)控制器(Servlet)
dao层:连接数据库
service层:调用dao层,执行一些具体的业务代码
servlet:接收前端数据,把数据和业务请求交给service层去处理,接收返回的数据并进行页面跳转(注意转发和重定向)
jsp/html
前端 数据传输 实体类(
【注意】前端传输的字段不一定包含pojo的所有属性,但是面向对象打交道是一般是使用一个pojo
实体类:用户名,密码,生日,爱好…约20个
前端:用户名, 密码
pojo:User
vo(视图层对象):UserVo 相当于为前端再封装了一个对象,还是实体类
- MVC是模型(Model)、视图(View)、控制器(Controller)的简写,是一种软件设计规范
- 是将业务逻辑、数据、显示分离的方法来组织代码。
- MVC主要作用是降低了视图与业务逻辑间的双向耦合。
- MVC不是一种设计模式,MVC是一种架构模式。当然不同的MVC存在差异。
Model(模型):数据模型,提供要在展示的数据,包含数据和行为,现在分为dao层数据库连接和sql操作,service层负责具体业务逻辑的处理。模型提供了“模型数据查询”和“模型数据的状态更新”,包括数据与业务。
**View(视图):**负责模型数据的展示,界面展示
**Controller(控制器):**接收用户请求,将请求委托给model层进行处理;处理完毕后把返回的模型数据飞View层进行渲染展示(即调整到view层)。也即是说controller做了一个调度员的工作。
最典型的MVC模型就是:JSP+Servlet+JavaBean模式
JSP:本质就是一个Servlet
假设一个面试问题:你的项目的架构,是设计好的,还是演进的?
回答:演进的
Alibaba,刚开始PHP
用户量过高,开始用Java
王坚 去IOE MySQL
MySQL:MySQL==>ALiSQL、AliRedis
All in one==>微服务
1.2 Model1时代
在web早期的开发中,通常采用的都是Model1
Model1中,主要分为两层,视图层和模型层(将controller层和model层混在一起了)
Model1优点:架构简单,比较适合小型项目开发
Model1缺点:JSP职责不单一,职责过重,不便于维护
1.3 Model2时代
Model2把项目分为三部分,包括view,controller、model
- 用户发送请求
- servlet接收请求数据,并调用对应的业务逻辑方法
- 业务处理完毕,返回更新后的数据给Servlet
- Servlet转向JSP,由JSP来渲染页面
- 响应给前端更新后的页面
职责分析:
Controller:
- 取得表单数据
- 调用业务逻辑
- 转向指定的页面
Model:
- 业务逻辑
- 保存数据的状态
View:
- 显示页面
Model2这样不仅提高了代码的复用率与项目的扩展性,且大大降低了项目的维护成本。
Model1实现模式比较简单,适用于快速开发小规模项目,Model1中JSP页面身兼View和controller两种角色,将控制逻辑和表现逻辑混杂在一起,从而导致代码的重用性非常低,增加了应用的扩展性和维护的难度。
Model2消除了Model1的缺点。
1.4 回顾Servlet
结构:
1.创建一个普通的maven项目,一个父工程可以完成所有的事情
删除src、提前导入公共pom依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.kuang</groupId>
<artifactId>springMVC</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>springmvc-01-servlet</module>
</modules>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet/servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet.jsp/jsp-api -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet/jstl -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
</project>
2.创建子模块Moudle:依旧创建普通项目springmvc-01-servlet,添加Web-app框架支持
3.子模块的pom文件中,导入servlet和jsp的jar依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springMVC</artifactId>
<groupId>com.kuang</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springmvc-01-servlet</artifactId>
<dependencies>
<!-- https://mvnrepository.com/artifact/javax.servlet/servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet.jsp/jsp-api -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
</dependency>
</dependencies>
</project>
4.编写一个Servlet类,用来处理用户的请求
package com.kuang.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
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);
}
}
5.编写text.jsp,在WEB-INF目录下新建一个jsp的文件夹,新建text.jsp
<%--
Created by IntelliJ IDEA.
User: liujie
Date: 2021/9/6
Time: 14:55
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>处理结果</title>
</head>
<body>
${msg}
</body>
</html>
6.在web.xml中注册Servlet
<?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">
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.kuang.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<!-- <session-config>-->
<!-- <session-timeout>15</session-timeout>-->
<!-- </session-config>-->
<!-- -->
<!-- <welcome-file-list>-->
<!-- <welcome-file>index.jsp</welcome-file>-->
<!-- </welcome-file-list>-->
</web-app>
7.配置Tomcat,并启动测试
localhost:8080/user?method=add
localhost:8080/user?method=delete
MVC框架要做的事情
- 将url映射到java类或者java类的方法
- 封装用户提交的数据
- 处理请求-调用相关的业务逻辑-封装响应数据
- 将响应的数据进行渲染,jsp/html等表示层数据
说明:
- 常见的服务器端MVC框架有:Struts、SpringMVC、ASP.NET MVC、Zend framework、JSP;
- 常见前端MVC框架:vue、angularjs、react、backbone
- 由MVC演化出的另外一些模式如:MVP、MVVM等等(MVVM:M V VM==>ViewModel双向绑定)
2 什么是SpringMVC
2.1 概述
SpringMVC是Spring Framework的一部分,是基于Java实现的MVC轻量级Web框架。
底层还是Servlet
SpringMVC官网地址:
https://docs.spring.io/spring-framework/docs/current/reference/html/web.html
Spring MVC的特点
-
轻量级,简单易学
-
高效,基于请求响应的MVC框架
-
与Spring的兼容性好,无缝结合
Spring:大杂烩,我们可以将SpringMVC中所有要用到的bean,注册到Spring中;
-
约定大于配置
-
功能强大:RESTful(就是网址中没有用问号传,用的是斜线)、数据验证、格式化、本地化、主题等
-
简洁灵活
Spring的web框架围绕DispatcherServlet(调度Servlet)设计
DispatcherServlet的作用是将请求分发到不同的处理器。从Spring2.5开始,使用java5或者以上版本的用户可以采用基于注解形式进行开发,十分简洁;
正因为SpringMVC好,简单,便捷,医学,天生和Spring无缝集成(使用其IOC和AOP),使用约定大于配置,能够进行简单的junit测试,支持Restful风格,异常处理,本地化,国际化,数据验证,类型转换,拦截器等等……所以我们要学习
最重要的是用的人多,使用的公司多
2.2 中心控制器
-
Spring的web框架围绕DispatcherServlet【调度Servlet】设计。
DispatcherServlet的作用是将请求分发到不同的的处理器(Servlet类及其方法)。
从Spring2.5开始,使用Java5或者以上版本的用户可以采用基于注解的controller声明方式。
-
SpringMVC框架向许多其他MVC框架一样,以请求为驱动,围绕一个中心Servlet分派请求及提供其他功能,DispatcherServlet实际上也是一个Servlet(它继承自HttpServlet父类)
SpringMVC的原理如下图所示:
-
当发起请求时被前置控制器拦截到请求,根据请求参数生成代理请求,找到请求对应的实际控制器;
-
控制器处理请求,创建ModelAndView(访问数据库,选择视图),将ModelAndView返回给中心控制器;
-
中心控制器使用视图解析器,选择视图+渲染数据,将结果返回给中心控制器
-
中心控制器将结果返回给请求者
2.3 SpringMVC执行原理
图为SpringMVC的一个较为完整的流程图,实线表示SpringMVC框架提供的技术,不需要开发者实现;
虚线表示需要开发者实现:
- Controller业务逻辑+数据库访问+返回ModelAndView
- 具体前端页面书写
简单分析执行流程:
-
DispatcherServlet表示前置控制器,中心控制器,是整个SpringMVC的控制中心。用户发出请求,DispatcherServlet接收请求并拦截请求。
我们假设请求的url为:http://localhost:8080/SpringMVC/hello
如上url拆分为三部分:
http://localhost:8080 :服务器域名
SpringMVC:部署在服务器上的web站点,可以Tomcat中配置
hello:表示控制器
通过分析,如上url表示为:请求 位于服务器localhost:8080上的,SpringMVC站点上的,hello控制器。
-
HandlerMapping为处理器映射。
DispatcherServlet调用HandlerMapping;HanderMapping根据请求的url查找HanderExecution;HanderExecution根据url寻找controller;如上url查找到的controller为:hello;
将解析到的controller信息返回给DispatcherServlet
-
HandlerAdapter表示处理器适配器,将按照特定的规则去执行Handler。
Handler让具体的controller执行。
controller执行业务逻辑+访问数据库,返回ModelAndView给HandlerAdapter,再返回给中心控制器;
-
中心控制器将ModelAndView给视图解析器ViewResolver,选择视图+渲染数据;将最终视图返回给中心控制器,中期控制器再返回给请求者。
3 HelloSpringMVC
3.1 配置版
项目结构:
-
新建一个Moudle,springmvc-02-hello,添加web支持
-
确定导入一个SpringMVC的依赖!
-
配置veb.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"> <!--配置DispatcherServlet:这个类是SpringMVC的核心;请求分发器,前端控制器--> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!--DispatcherServlet必须要绑定Spring的配置文件,关联一个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>
-
在Resources目录下,编写SpringMVC配置文件,springmvc-servlet.xml:【servletname】-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 https://www.springframework.org/schema/beans/spring-beans.xsd"> <!--1.处理器映射器:DispatcherController根据请求url找Controller名称--> <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/> <!--2.处理器适配器:适配连接具体的Controller,进行业务处理--> <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/> <!--3.视图解析器:DispatcherServlet返回的ModelAndView都需要经过视图解析器 1.获取了ModelAndView的数据 2.解析了ModelAndView的视图名字 3.拼接视图名字,找到了对应的视图 /WEB-INF/jsp/hello.jsp 4.将数据渲染到这个视图上 后面可以用模板引擎:ThymeLeaf Freemarker...--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="InternalResourceViewResolver"> <!--前缀--> <property name="prefix" value="/WEB-INF/jsp/"/> <!--前缀--> <property name="suffix" value=".jsp"/> </bean> <!--BeanNameUrlHandlerMapping处理器映射器,需要配置Controller的bean--> <bean id="/hello" class="com.kuang.controller.HelloController"/> </beans>
-
在java目录下新建com.kuang.controller包,新建HelloController类;
编写我们要操作业务的Controller,要么实现Controller接口,要么增加注解;
需要返回一个ModelAndView,装数据,封视图
package com.kuang.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:专心写业务 //注意:这里我们先导入Controller接口 //Controll public class HelloController implements Controller { public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { //ModelAndView 模型和视图 ModelAndView mv = new ModelAndView(); //1.业务代码 //封装对象,放在ModelAndView中。Model mv.addObject("msg","HelloSpringMVC!"); //2.跳转视图,设置视图的名字就可以了 //封装要跳转的视图,放在ModelAndView中 mv.setViewName("hello"); //: /WEB-INF/jsp/hello.jsp return mv; } }
-
写要跳转的jsp页面,显示ModelAndView存放的数据,以及我们的正常页面
hello.jsp
--%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> ${msg} </body> </html>
-
配置Tomcat,启动测试
可能遇到的问题,访问出现404,排查步骤:
-
查看控制台输出,看一下是不是缺少什么jar包
-
如果jar包存在,显示无法输出,就在idea的项目发布中,添加lib依赖
-
重启Tomcat即可解决
3.2 注解版
结构:
-
新建一个Moudle,springmvc-03-annotation,添加web支持
-
由于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框架核心库、Spring MVC、servlet、jstl等,我们在父依赖中已经引入了!
-
配置web.xml——注册DispatcherServlet,一次配置无需改变
注意点:
- 注意web.xml版本问题,要最新版
- 注意DispatcherServlet
- 关联SpringMVC的配置文件
- 启动级别为1
- 映射路径为/【不要用/*,会404】
<?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"> <!--配置DispatcherServlet:这个类是SpringMVC的核心;请求分发器,前端控制器--> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!--DispatcherServlet必须要绑定Spring的配置文件,关联一个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>
-
在resources目录下添加SpringMVC的配置文件:springmvc-servlet.xml,一次配置,无需改变
- 让IOC的注解生效
- 静态资源过滤:HTML,JS,CSS,图片,视频
- MVC的注解驱动,自动装配
- 配置视图解析器
在resources目录下添加springmvc-servlet.xml配置文件,配置的形式与Spring容器配置基本相似,为了支持基于注解的IOC,设置了自动扫描包的功能。
<?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"> <!--1.自动扫描包,让指定包下的注解生效,由IOC容器统一管理,实现了自动装配,无需再手动注册具体controller--> <context:component-scan base-package="com.kuang.controller"/> <!--让Spring MVC不处理静态资源--> <mvc:default-servlet-handler/> <!--支持mvc注解驱动,取代HandlerMapping和HandlerAdapter 在spring中一般采用@RequestMapping注解来完成映射关系 要想使@RequestMapping注解生效 必须向上下文中注册DefaultAnnotationHandlerMapping 和AnnotationMethodHandlerAdapter实例 这两个实例分别在类级别和方法级别处理 而annotation-driven配置帮助我们自动完成上述两个实例的注入--> <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>
不适用注解的情况下,需要配置:
- HandlerMapping
- HanderAdapter
- 视图解析器
- 具体的controller需要显示注册
使用注解情况下:
-
采用了“自动扫描包”,无需在手动注册 具体controller
-
采用“注解驱动”,取代了HandlerMapping和HandlerAdapter
-
(注意静态资源的处理)
-
同样“视图解析器”不可或缺
在视图解析器中我们把所有的视图都存放在/WEB-INF/jsp目录下,这样可以保证视图安全,因为这个目录下的文件,客户端不能直接访问。
-
创建具体controller,使用注解
编写一个java控制类,com.kuang.controller.HelloController,注意编码规范
package com.kuang.controller; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; @Controller @RequestMapping("/HelloController") //外层可以添加一层路径,一般不加 public class HelloController { //真实访问地址:项目名/helloController/hello @RequestMapping("/hello") public String hello(Model model){ //向模型中添加属性msg和值,可以在jsp页面取出并渲染 model.addAttribute("msg","Hello,SpringMVCAnnotation"); return "hello"; //会被视图解析器处理 /WEB-INF/jsp/hello.jsp } }
-
@Controller是为了让SpringIOC容器初始化自动扫描到
-
@RequestMapping是为了映射请求路径,这里因为类与方法上都有映射,所以访问时应该是
/HelloController/hello
-
方法中声明Model类型的参数是为了把Action中的数据带到视图中
-
方法返回的结果是视图的名称hello,加上配置文件中的前后缀变成
/WEB-INF/jsp/hello.jsp
-
-
创建视图层
在WEB-INF/jsp目录中创建hello.jsp,视图可以直接取出并展示从Controller带回的信息;
可以通过EL表达式去除Model中存放的值或者对象;
<%-- Created by IntelliJ IDEA. User: liujie Date: 2021/9/6 Time: 21:12 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> ${msg} </body> </html>
-
配置Tomcat运行
3.3 小结
无论是配置版还是注解版,实现步骤都差不多:
新建一个web项目
导入相关的jar包
1.编写web.xml,注册DispatcherServlet
2.编写springmvc配置文件:springmvc-servlet.xml
3.接下来就是去创建对应的控制类,controller
4.最后就是完善前端视图和controller之间的对应
测试运行调试
使用SpringMVC必须配置三大件:
配置版:处理器映射器HandlerMapping、处理器适配器HandlerAdapter、视图解析器ViewResolver
注解版:自动扫描+注解驱动,视图解析器
通常,我们只需要手动配置视图解析器,而HandlerMapping和HandlerAdapter只需要开启注解驱动即可,可以省去大段的xml配置
关注重点:
Controller及RestFul
结果跳转方式
数据处理
4 Controller及RestFul
4.1 控制器Controller
- 控制器 负责提供访问应用程序的行为,通常通过接口定义或者注解定义两种实现方法实现。
- 控制器负责解析用户的请求并将其转换为一个模型
- 在SpringMVC中,采用接口定义的controller只能有一个方法,采用注解定义的controller可以有多个方法
- 在SpringMVC中,对于Controller的配置方式有很多种
4.2 实现Controller接口
Controller是一个接口,在org.springframework.web.servlet.mvc包下,接口中只有一个方法:
//实现该接口的类获得控制器功能
public interface Controller{
//处理请求并返回一个模型与视图对象
ModelAndView handleRequest(HttpServletRequest var1,HttpServletResponse var2)throws Exception;
}
测试:
-
新建一个Moudle,springmvc-04-controller,添加web框架
重点:去项目框架中Artificial中添加lib目录
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"> <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-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
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 https://www.springframework.org/schema/beans/spring-beans.xsd"> <!--1.处理器映射器:DispatcherController根据请求url找Controller名称--> <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/> <!--2.处理器适配器:适配连接具体的Controller,进行业务处理--> <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/> <!--3.视图解析器:DispatcherServlet返回的ModelAndView都需要经过视图解析器 1.获取了ModelAndView的数据 2.解析了ModelAndView的视图名字 3.拼接视图名字,找到了对应的视图 /WEB-INF/jsp/hello.jsp 4.将数据渲染到这个视图上 后面可以用模板引擎:ThymeLeaf Freemarker...--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="InternalResourceViewResolver"> <!--前缀--> <property name="prefix" value="/WEB-INF/jsp/"/> <!--前缀--> <property name="suffix" value=".jsp"/> </bean> <!--BeanNameUrlHandlerMapping处理器映射器,需要配置Controller的bean--> <bean id="/hello" class="com.kuang.controller.ContollerTest01"/></beans>
-
com.kuang.controller目录下编写Controller类,ControllerTest01
package com.kuang.controller; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.Controller; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class ContollerTest01 implements Controller { @Override public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception { ModelAndView mv=new ModelAndView(); mv.addObject("msg","ControllerTest01"); mv.setViewName("hello"); return mv; } }
-
编写完毕后,去Spring配置文件中注册请求的bean;name对应请求的路径,class对应处理请求的类
<!--BeanNameUrlHandlerMapping处理器映射器,需要配置Controller的bean--> <bean id="/t1" class="com.kuang.controller.ContollerTest01"/>
-
编写前端test.jsp,注意在WEB-INF/jsp目录下编写,对应我们的视图解析器
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Kuangshen</title> </head> <body> ${msg} </body> </html>
-
配置Tomcat运行测试,我们没有项目发布名,配置的就是一个/,所以请求不用加项目名
说明:
- 实现接口Controller定义控制器是较老的办法。
- 缺点是:一个控制器里只有一个方法,如果要多个方法则需要定义多个Controller;定义的方式比较麻烦,每定义一个controller,都需要到springIOC容器配置文件中注册。
4.3 使用注解@Controller
- @Controller注解类型,用于声明Spring类的实例是一个控制器(在讲IOC时还提到另外3个注解
@Component 组件
@Service service
@Controller controller
@Repository dao
-
Spring可以使用扫描机制来找到应用程序中所有基于注解的控制器类
为了保证Spring能够找到你的控制器,需要在配置文件中声明组件扫描。
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" 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"> <!--1.自动扫描包--> <context:component-scan base-package="com.kuang.controller"/> <!--静态资源--> <mvc:default-servlet-handler/> <!--注解驱动,自动装配--> <mvc:annotation-driven/> <!--2.视图解析器--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/"/> <property name="suffix" value=".jsp"/> </bean> </beans>
-
增加一个ControllerTest2类,使用注解实现;
package com.kuang.controller; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; @Controller //这个类会被Spring接管,被这个注解的类中所有方法,如果返回值是String,并且有具体页面可以跳转,那么就会被这个视图解析器解析 public class ControllerTest2 { @RequestMapping("/t2") public String test1(Model model){ model.addAttribute("msg"," ControllerTest2"); return "hello"; } }
-
运行tomcat测试
可以发现,我们的两个请求都可以指向一个视图,但页面的结果是不一样的(因为数据渲染不同),从这里看出视图是可以被复用的,而控制器与视图之间是弱耦合关系。
4.4 RequestMapping
@RequestMapping
-
@RequestMapping注释用于映射url到控制器类或者一个特定的处理程序方法。
可用于类或者方法上,用于类上,表示类中所有的响应请求都是以该地址作为父路径
-
只注解在方法上面
@Controller public class TestController { @RequestMapping("/h1") public String test(){ return "test"; } }
访问路径:http://localhost:8080 / 项目名 / h1
-
同事注解类和方法
@Controller @RequestMapping("/admin") public class TestController { @RequestMapping("/h1") public String test(){ return "test"; } }
访问路径:http://localhost:8080 / 项目名/ admin /h1 , 需要先指定类的路径再指定方法的路径;
一般来说,我们不在类上面使用,只在方法上面使用。
4.5 RestFul风格
同一个链接,不同的请求方式,能产生不一样的结果!
概念
-
RestFul就是一个资源定位及资源操作的风格。不是标准也不是协议,只是一种风格。
-
这种风格设计的软件可以更简洁,更有层次,更易于实现缓存机制。
-
它是以斜线访问资源的。比如:localhost:8080/method/1/2
功能
- 资源:互联网上所有的事务都可以被抽象为资源
- 资源操作:get,post,delete,put
- 分别对应:查询,添加,删除,修改
传统方式操作资源:通过不同的参数来实现不同的效果,方法单一,post和get
@Controller
public class ControllerTest2 {
@RequestMapping("/add")
public String test1(int a,int b,Model model){
int ret=a+b;
model.addAttribute("msg","结果为:"+ret);
return "hello";
}
}
请求url:http://localhost:8080/add?a=1&b=2
使用RestFul风格方式操作资源:可以通过不同的请求方法来实现不同的效果
- 在
@RequestMapping
中映射路径中添加形式参数,和方法形参中用@PathVariable
注解,让方法参数的值对应绑定到一个URL模板变量上
请求url:http://localhost:8080/add2/1/2
使用路径变量的好处?
-
使路径变得更简洁
-
获得参数更方便,框架会自动进行类型转换
-
通过路径变量的类型可以约束访问参数,如果类型不一样,则访问不到对应的请求方法、如int,却提供String,则路径与方法不匹配,而不会是参数在转换失败
-
使用@RequestMapping的method参数,可以先定http请求的类型。指定请求类型为get,post,put,delete,patch,options,trace等
我们使用浏览器地址栏进行访问默认为Get请求,此时需要post请求,会报错405
-
方法级别的注解变体有如下几个:组合注解
@GetMapping @PostMapping @PutMapping @DeleteMapping @PatchMapping
@GetMapping是一个组合注解
相当于@RequestMapping(method =RequestMethod.GET) ,平时用的比较多
5 结果跳转
5.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>
对应的controller类
public class ControllerTest1 implements Controller {
public ModelAndView handleRequest(HttpServletRequest httpServletRequest,HttpServletResponse httpServletResponse) throws Exception {
//返回一个模型视图对象
ModelAndView mv = new ModelAndView();
mv.addObject("msg","ControllerTest1");
mv.setViewName("test");
return mv;
}
}
5.2 ServletAPI 实现跳转
关闭视图解析器,通过设置ServletAPI进行跳转
- 通过HttpServletResponse进行输出
- 通过HttpServletResponse实现重定向
- 通过HttpServletResponse实现转发
在controller类的方法中加上HttpServletRequest和HttpServletResponse请求和响应参数
5.3 SpringMVC方式 跳转
-
不借助视图解析器,直接跳转
测试前注释掉视图解析器
-
借助视图解析器(常用)
6 数据处理
6.1 处理前端提交的数据
1.提交的数据名称和处理方法的参数名一直
提交数据 : http://localhost:8080/hello?name=kuangshen
处理方法:
@RequestMapping("/hello")
public String test1(String name){
System.out.println(name);
return "hello";
}
后台输出:kuangshen
2.提交的数据名称和处理方法的参数名不一致
提交数据 : http://localhost:8080/hello2?username=kuangshen
处理方法:采用@RequestParam(“username”)注解
@RequestMapping("/hello2")
public String test2(@RequestParam("username") String name){
System.out.println(name);
return "hello";
}
后台输出:kuangshen
3.提交的是一个对象
要求提交的表单域和对象的属性名一致,参数使用对象即可
实体类:
@Data
public class User {
int id;
String name;
int age;
}
提交数据: http://localhost:8080/hello3?name=kuangshen&id=1&age=15
处理方法:
@RequestMapping("/hello3")
public String test3(User user){
System.out.println(user);
return "hello";
}
后台输出:User{id=1,name=“kuangshen”,age=15}
说明:如果使用对象的话,前端传递的参数名和对象名必须一致,否则就是null
6.2 数据显示到前端
视图页面可取到,并不是给请求方
-
通过ModelAndView
public class ControllerTest1 implements Controller { public ModelAndView handleRequest(HttpServletRequest httpServletRequest,HttpServletResponse httpServletResponse) throws Exception { //返回一个模型视图对象 ModelAndView mv = new ModelAndView(); mv.addObject("msg","ControllerTest1"); mv.setViewName("test"); return mv; } }
-
通过ModelMap
@RequestMapping("/hello") public String hello(@RequestParam("username") String name, ModelMap model){ //封装要显示到视图中的数据 //相当于req.setAttribute("name",name); model.addAttribute("name",name); System.out.println(name); return "hello"; }
-
通过Model
@RequestMapping("/ct2/hello") public String hello(@RequestParam("username") String name, Model model){ //封装要显示到视图中的数据 //相当于req.setAttribute("name",name); model.addAttribute("msg",name); System.out.println(name); return "test"; }
对比:
Model 只有寥寥几个方法,只适合用于存储数据,简化了新手对于Model对象的操作和理解;
ModelMap 继承了LinkedMap,除了实现了自身的一些方法,同样的继承了LinkedMap的方法和特性;
ModelAndView 可以在储存数据的同时,可以进行设置返回的逻辑视图,进行控制展示层的跳转;
6.4 乱码问题
- 环境构造
1.form.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="/e/t1" method="get">
<input type="text" name="name">
<input type="submit">
</form>
</body>
</html>
2.controller
package com.kuang.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
public class EncodingController {
@RequestMapping("/e/t1")
public String test01(@RequestParam("name") String name, Model model){
System.out.println(name);
model.addAttribute("msg",name);
return "result";
}
}
3.result.jsp
<%--
Created by IntelliJ IDEA.
User: liujie
Date: 2021/9/7
Time: 15:53
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
${msg}
</body>
</html>
显示乱码
-
解决办法一:自定义过滤器
-
新建filter目录,并书写过滤器EncodingFilter
package com.kuang.filter; import javax.servlet.*; import java.io.IOException; public class EncodingFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("================"); servletRequest.setCharacterEncoding("utf-8"); servletResponse.setCharacterEncoding("utf-8"); filterChain.doFilter(servletRequest, servletResponse); } @Override public void destroy() { } }
-
去web.xml中注册过滤器
<!--过滤器注册--> <filter> <filter-name>encoding</filter-name> <filter-class>com.kuang.filter.EncodingFilter</filter-class> </filter> <filter-mapping> <filter-name>encoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
-
-
解决办法二:采用SpringMVC框架内置提供的过滤器(一般情况下够用)
-
直接在web.xml中注册过滤器
<!--配置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>
-
-
解决办法三:自定义过滤器,但是是大神写的过滤器类
GenericEncodingFilter.java
package com.kuang.filter; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.Map; /** * 解决get和post请求 全部乱码的过滤器 */ public class GenericEncodingFilter implements Filter { @Override public void destroy() { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("======================================"); //处理response的字符编码 HttpServletResponse myResponse=(HttpServletResponse) response; myResponse.setContentType("text/html;charset=UTF-8"); // 转型为与协议相关对象 HttpServletRequest httpServletRequest = (HttpServletRequest) request; // 对request包装增强 HttpServletRequest myrequest = new MyRequest(httpServletRequest); chain.doFilter(myrequest, response); } @Override public void init(FilterConfig filterConfig) throws ServletException { } } //自定义request对象,HttpServletRequest的包装类 class MyRequest extends HttpServletRequestWrapper { private HttpServletRequest request; //是否编码的标记 private boolean hasEncode; //定义一个可以传入HttpServletRequest对象的构造函数,以便对其进行装饰 public MyRequest(HttpServletRequest request) { super(request);// super必须写 this.request = request; } // 对需要增强方法 进行覆盖 @Override public Map getParameterMap() { // 先获得请求方式 String method = request.getMethod(); if (method.equalsIgnoreCase("post")) { // post请求 try { // 处理post乱码 request.setCharacterEncoding("utf-8"); return request.getParameterMap(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } else if (method.equalsIgnoreCase("get")) { // get请求 Map<String, String[]> parameterMap = request.getParameterMap(); if (!hasEncode) { // 确保get手动编码逻辑只运行一次 for (String parameterName : parameterMap.keySet()) { String[] values = parameterMap.get(parameterName); if (values != null) { for (int i = 0; i < values.length; i++) { try { // 处理get乱码 values[i] = new String(values[i] .getBytes("ISO-8859-1"), "utf-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } } } hasEncode = true; } return parameterMap; } return super.getParameterMap(); } //取一个值 @Override public String getParameter(String name) { Map<String, String[]> parameterMap = getParameterMap(); String[] values = parameterMap.get(name); if (values == null) { return null; } return values[0]; // 取回参数的第一个值 } //取所有值 @Override public String[] getParameterValues(String name) { Map<String, String[]> parameterMap = getParameterMap(); String[] values = parameterMap.get(name); return values; } }
web.xml
<filter> <filter-name>encoding</filter-name> <filter-class>com.kuang.filter.GenericEncodingFilter</filter-class> </filter> <filter-mapping> <filter-name>encoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
7 JSON
前后端分离时代:
后端部署后端,提供接口,提供数据
json(数据格式,纯文本格式,对java而言就是一个字符串)
json字符串<===>JavaScript对象
前端独立部署,负责渲染后端的数据
7.1 什么是JSON?
- JSON(JavaScript Object,Notation,JS对象标记)是一种轻量级的数据交换格式,目前使用特别广泛。
- 采用完全独立于编程语言的文本格式来存储和表示数据
- 简洁清晰的层次结构使得JSON成为理想的数据交换语言
- 易于人阅读和编写,同时也易于机器解析和生成,并有效地替身网络传输效率
在js中,一起都是对象;任何js支持的类型都可以通过json来表示,如字符串、数字、对象、数组。
-
对象表示为键值对,数据由逗号分割
-
花括号保存对象
-
方括号保存数组
JSON键值对是用来保存JavaScript对象的一种方式,和JavaScript对象的写法也大同小异,键值对组合中的键名写在前面并用双引号包裹,使用冒号:分隔,然后紧接着值:
{"name": "QinJiang"}
{"age": "3"}
{"sex": "男"}
很多人搞不清楚JSON和JavaScript对象的关系,可以这样理解:
-
JSONJavaScript对象的字符串表示法,它使用文本表示一个JS对象的信息,本质是一个字符串
var obj={a:'hello',b:'world'}; //这是一个对象,注意键名也是可以使用引号包裹 var json="{'a':'hello','b','world'}"; //这是一个JSON字符串,本质还是一个字符串
-
JSON字符串和JavaScript对象互转
-
JSON字符串==>JavaScript对象
var obj=JSON.parse('{"a": "Hello", "b": "World"}'); //结果是 {a: 'Hello', b: 'World'}
-
JavaScript对象==>JSON字符串
var json=JSON.stringify({a: 'Hello', b: 'World'}); //结果是 '{"a": "Hello", "b": "World"}'
-
代码测试:
1.新建一个module,springmvc-05-json,添加web支持
2.在web目录下新建一个JSON-1.html,编写测试内容
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JSON_秦疆</title>
</head>
<body>
<script type="text/javascript">
//编写一个js的对象
var user = {
name:"秦疆",
age:3,
sex:"男"
};
//将js对象转换成json字符串
var str = JSON.stringify(user);
console.log(str);
//将json字符串转换为js对象
var user2 = JSON.parse(str);
console.log(user2.age,user2.name,user2.sex);
</script>
</body>
</html>
3.在idea中使用浏览器打开,查看控制台输出
7.2 Controller返回JSON数据给前端——Jackson的使用
- Jackson应该是目前比较好的json解析工具了
- 当然工具不只这一个,比如还有阿里巴巴的fastjson等等
-
使用Jackson,需要导入它的包;注意去更新lib下的包
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jacksoncore --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.8</version> </dependency>
-
Controller中直接返回json字符串浏览器
@ResponseBody
可以让Controller类下所有的返回请求不经过视图解析器- Jackson使用ObjectMapper类的方法将对象转化为json字符串
package com.kuang.controller; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.kuang.dao.User; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; @Controller public class JsonController { @RequestMapping("/json1") @ResponseBody //它就不会走视图解析器,会直接返回一个字符串;会返回你真实返回的东西,一般都是返回字符串,就实现了前后端分离 public String json1() throws JsonProcessingException { //1.创建一个Jackson对象映射器new ObjectMapper(),用来解析数据 //2.创建一个对象 User user=new User(1,"狂神",24); //3.将对象解析为json字符串并返回 return new ObjectMapper().writeValueAsString(user); } }
-
返回字符串统一解决
在类上直接使用
@RestController
,这样子,里面所有的方法都只会返回json字符串了,不用再每一个都添加@ResponseBody,我们在前后端分离开发中,一般都使用@RestController,十分便捷。@RestController public class UserController { //produces:指定响应体返回类型和编码 @RequestMapping(value = "/json1") public String json1() throws JsonProcessingException { //创建一个jackson的对象映射器,用来解析数据 ObjectMapper mapper = new ObjectMapper(); //创建一个对象 User user = new User("秦疆1号", 3, "男"); //将我们的对象解析成为json格式 String str = mapper.writeValueAsString(user); //由于@ResponseBody注解,这里会将str转成json格式返回;十分方便 return str; } }
-
返回的json字符串会存在乱码问题
-
解决办法一:通过
@RequestMapping
的produces属性来解决//produces:指定响应体返回类型和编码 @RequestMapping(path="/json1",produces="application/json;charset=utf-8")
-
解决办法二:在springmvc-servlet.xml配置文件中统一指定乱码解决,在注解驱动中配置
<!--注解驱动,自动装配--> <mvc:annotation-driven> <mvc:message-converters register-defaults="true"> <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>
-
-
测试集合对象输出为json字符串
@RequestMapping("/json2") public String json2() throws JsonProcessingException { //创建一个jackson的对象映射器,用来解析数据 ObjectMapper mapper = new ObjectMapper(); //创建一个对象 User user1 = new User("秦疆1号", 3, "男"); User user2 = new User("秦疆2号", 3, "男"); User user3 = new User("秦疆3号", 3, "男"); User user4 = new User("秦疆4号", 3, "男"); List<User> list = new ArrayList<User>(); list.add(user1); list.add(user2); list.add(user3); list.add(user4); //将我们的对象解析成为json格式 String str = mapper.writeValueAsString(list); return str; }
-
输出时间对象
@RequestMapping("/json3") public String json3() throws JsonProcessingException { //创建一个时间对象,java.util.Date Date date=new Date(); //将时间对象解析为json格式输出,默认输出为时间戳格式 return new ObjectMapper().writeValueAsString(date); }
运行结果:
-
默认日期格式会变成一个数字,是从1970年1月1日到当前日期的毫秒数
-
Jackson默认会把时间转化为timestamps形式
解决方案:自定义时间格式
@RequestMapping("/json4") public String json4() throws JsonProcessingException { ObjectMapper mapper = new ObjectMapper(); //1.不使用时间戳的方式 mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); //2.自定义日期格式对象 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //指定日期格式 mapper.setDateFormat(sdf); //3.再次解析,输出指定格式的日期 Date date = new Date(); return mapper.writeValueAsString(date); }
如果解析时间经常使用,我们可以封装工具类:
-
新建utils目录下JsonUtils工具类
package com.kuang.utils; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import java.text.SimpleDateFormat; public class JsonUtils { //函数重载2 public static String getJson(Object object) throws JsonProcessingException { return getJson(object,"yyyy-MM-dd HH:mm:ss"); } //函数重载1 public static String getJson(Object object,String dateFormat) throws JsonProcessingException { ObjectMapper mapper = new ObjectMapper(); //1不使用时间戳的方式 mapper.configure(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS,false); //2自定义日期格式对象 SimpleDateFormat sdf=new SimpleDateFormat(dateFormat); //指定日期格式 mapper.setDateFormat(sdf); //返回指定格式的date的json字符串 return mapper.writeValueAsString(object); } }
-
controller使用工具类
@RequestMapping("/json3") public String json3() throws JsonProcessingException { Date date = new Date(); return JsonUtils.getJson(date); }
-
7.3 FastJson
FastJson.jar是阿里开发的一款专门用于Java开发的包,可以方便地实现:
- Json对象与JavaBean对象的转换
- json字符串与JavaBean对象的转换
- json字符串与Json对象的转换
-
添加pom依赖
<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.60</version> </dependency>
-
fastjson的三个主要的类
- JSONObject:代表json对象
- JSONObject实现了Map接口
- JSONObject对应json对象,通过各种形式的get()方法可以获取json对象中的数据,也可利用诸如size(),isEmpty()等方法获取键值对的个数和判空。其本质是通过实现Map接口并调用接口中的方法完成的。
- JSONArray:代表json对象数组
- 内部是有List接口的方法来完成操作的。
- JSON:代表JSONObject和JSONArray的转化
- JSON类源码分析与使用
- 主要实现了json对象,json对象数组,javabean对象,json字符串之间的相互转化。
代码测试:
- JSONObject:代表json对象
8 整合SSM
8.1 环境要求
环境:
- idea
- MySQL
- Tomcat
- Maven
要求:
- 需要
8.2 数据库环境
创建一个存放书籍数据的数据库表
8.3 基本环境搭建
-
新建一个Maven项目,ssmbuild,添加web支持
-
导入相关pom依赖
-
Maven资源过滤设置
-
建立基本结构和配置框架
java:
- com.kuang.pojo
- com.kuang.dao
- com.kuang.service
- com.kuang.controller
resources:
- mybatis-config.xml
- applicationConext.xml
8.4 Mybatis层编写
-
数据库配置文件database.properties
MySQL8.0以上的版本url连接,还需要增加一个时区的设置
-
IDEA关联数据库
-
编写MyBatis核心配置文件mybatis-config.xml
MyBatis其他配置给Spring去做,只留“typeAliases”和“mappers”
-
编写数据库对应的实体类com.kuang.pojo.Books
尽量让实体类的属性和数据库字段相同,使用lombok插件
-
编写Dao层的Mapper接口
-
编写Dao层接口对应的Mapper.xml文件。需要导入MyBatis的包
-
编写Service层的接口和实现类
接口:
实现类:
OK,到此,底层需求操作编写完毕!
8.5 Spring层
-
配置Spring整合MyBatis,我们这里数据源使用c3p0连接池;
-
我们去编写Spring整合Mybatis的相关配置文件:spring-dao.xml
注意所有的.xml文件都连接在项目框架结构下,Project Structure,Modules
-
spring整合service层,扫描service相关bean,配置事务管理器:spring-service.xml
8.6 SpringMVC层
- web.xml中配置DispatcherServlet encodingFilter Session过期时间
- spring-mvc.xml配置
- Spring配置整合文件,applicationcontext.xml
配置文件,暂时结束!Controller和视图层编写
- BookController类的书写,方法一:查询全部书籍
- 编写首页 index.jsp
- 书籍列表页面 allbook.jsp
- BookController类的编写,方法二:添加书籍
- 添加书籍页面:addBook.jsp
- BookController类的编写,方法三:修改书籍
- 修改书籍页面: updateBook.jsp
- BookController类编写,方法四:删除书籍
配置Tomcat,进行运行!
到目前为止,这个SSM项目整合已经完全OK了,可以直接运行进行测试!这个练习十分重要,大家需要保证,不看任何东西,自己也可以完整的实现出来!
项目结构图
小结与展望
这个是同学们的第一个SSM整合案例,一定要烂熟于心!
SSM框架的重要程度是不言而喻的,学到这里,大家已经可以进行基本网站的单独开发。但是这只是增删改查的基本操作。可以说学到这里,大家才算是真正的步入了后台开发的门。也就是能找个后台相关工作的底线。
或许很多人,工作就做这些事情,但是对于个人的提高来说,还远远不够!
我们后面还有学习的一些SpringMVC的知识!
-
Ajax和json
-
文件上传和下载
-
拦截器
SpringBoot、SpringCloud开发!
8.7 增加查询书籍功能
-
前端页面增加一个输入框和查询按钮
-
编写查询的Controller
-
由于底层没有实现,所以我们要将底层代码先搞定
-
Mapper接口
-
Mapper.xml
-
Service接口
-
Service实现类
-
完善Controller
-
测试,查询功能OK!
-
优化!当我们发现查询的东西不存在的时候,查出来的页面是空的,我们可以提高用户的体验性!
- 在前端添加一个展示全部书籍的按钮
- 并在后台增加一些判断性代码
- 将错误信息展示在前台页面。完整的查询栏代码
9 Ajax
9.1 简介
-
Asynchoronous JavaScript and XML
-
异步通过xhr请求与后端交互,将返回的数据在前端页面上部分更新;
让web页面交互性更强。
-
使用ajax技术的网页,通过在后台服务器进行少量的数据交换,就可以实现异步局部更新。
简单应用:
- 注册时,输入用户名自动检测用户名是否已经存在
- 登录时,提示用户名密码错误
- 删除数据行时,将行ID发送到后台,后台在数据库中删除,数据库删除成功后,在页面DOM中将数据行也删除
- ……等等
9.2 伪造Ajax
我们可以使用前端的一个标签来伪造ajax的样子。iframe标签
- 新建一个module:springmvc-06-ajax,导入web支持,添加lib依赖
- 编写一个ajax-frame.html使用iframe测试,感受下效果
9.3 JQuery.ajax
- 纯原生实现的Ajax我们不去讲解这里,直接使用jquery提供的,方便学习和使用,避免重复造轮子,有兴趣的同学可以去了解下js原生的XMLHttpRequest!
- Ajax的核心是XMLHttpRequest对象(XHR)。XHR为向服务器发送请求和解析服务器响应提供了接口,能够以异步方式从服务器获取新数据。
- 通过jQuery AJAX方法,你能够使用HTTP Get和HTTP Post从远程服务器上请求文本、HTML、XML或JSON,同时你可以把这些外部数据直接载入网页的被选元素中。
- jQuery Ajax本质是XMLHttpRequest,对它进行了封装,方便调用!
测试1:简单的测试:
前端input失去焦点,调用js发送一个ajax请求到后端;后端处理完返回数据,前端局部刷新。
ajax三要素(url、method、data、success是一个callback函数)
后端重定向或者转发(jsp页面)必须重新刷新页面,视图更新控制权在后端
Ajax请求后端只返回数据,视图更新控制权交给前端
Ajax是前后端分离里面很重要的东西,前端向接口发送请求,获取数据,自己进行视图更新或跳转,后端提供接口进行操作并返回数据。(前后端交互,异步刷新)
测试2:SpringMVC后端返回实体类json对象
由于采用@RestController
注解,自动将对象转成json格式返回;纯天然不用经过视图解析器;
前端通过Ajax请求,获取对象的json数据,再进行渲染,局部刷新。
9.4 测试3:注册提示效果
我们再测试一个小Demo,思考一下我们平时注册的时候,输入框后面的实时提示是怎么做到的;如何优化;
输入用户名input,失去焦点,触发js函数,发送1个ajax请求
输入密码input,失去焦点,触发js函数,发送1个ajax请求
9.5 测试4:获取baidu接口Demo
10 拦截器
10.1 概述
SpringMVC的处理器拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。开发者可以自己定义一些拦截器来实现特定的功能。
过滤器与拦截器的区别:拦截器是AOP思想的具体应用。
过滤器:
- servlet规范中的一部分,任何java web工程都可以使用
- 在url-pattern中配置了/*之后,可以对所有要访问的资源进行过滤
拦截器:
- 拦截器是SpringMVC框架自己的,只有使用了SpringMVC框架的工程才能使用
- 拦截器只会拦截访问控制器方法的请求,如果访问的是静态资源jsp/html/css/image/js是不会进行拦截的,效率会更高
10.2 自定义拦截器
那如何实现拦截器呢?
想要自定义拦截器,必须实现HandlerInterceptor接口
- 新建一个Module,springmvc-07-interceptor,添加web支持,添加lib包
- 配置web.xml和springmvc-servlet.xml文件
- 新建目录config,编写一个拦截器MyInterceptor.java
- 在springmvc-servlet.xml配置文件中配置拦截器
- 编写一个controller,接收请求
- 前端index.jsp
- 启动tomcat测试
一般只实现preHandle类,处理前,return true
并放行
10.3 测试:验证用户是否登录(认证用户)
在WEB-INF下的所有页面只能通过controller进行访问
实现思路:
- 有一个登录页面,需要写一个controller访问页面。
- 登录页面有一个提交表单的动作。需要在controller中处理,判断用户名密码是否正确;如果正确,向session中写入用户信息。返回登录成功。
- 拦截用户请求,判断用户是否登录。如果用户已经登录,放行;如果用户未登录,跳转到登录页面。
11 文件上传和下载
11.1 准备工作
文件上传是项目开发中最常见的功能之一,SpringMVC可以很好地支持文件上传,但是SpringMVC上下文中默认没有装配MultipartResolver,因此默认情况下其不能处理文件上传工作。如果想使用Spring的文件上传功能,则需要在上下文中配置MultipartResover。
前端表单要求:为了能上传文件,必须将表单的method设置为post,并将enctype设置为multipart/form-data。只有在这样的情况下,浏览器才会把用户选择的文件,以二进制数据发送给服务器。
对表单中的enctype属性做个详细的说明:
- application/x-www==form-urlencoded:默认方式,只处理表单域中的value属性值,采用这种编码方式的表单会将表单域中的值处理成URL编码方式。
- multipart/form-data:这种编码方式会以二进制流方式来处理表单数据,这种编码方式会把文件域指定文件的内容也封装到请求参数中,不会对字符编码。
- text/plain:除了把空格转换为“+”以外,其他字符都不做编码处理,这种方式适合直接通过表单发送邮件。
<form action="" enctype="multipart/form-data" method="post">
<input type="file" name="file"/>
<input type="submit">
</form>
一旦设置了enctype为multipart/form-data,浏览器即会采用二进制流的方式来处理表单数据,而对 于文件上传的处理则涉及在服务器端解析原始的HTTP响应。在2003年,Apache Software Foundation 发布了开源的Commons FileUpload组件,其很快成为Servlet/JSP程序员上传文件的最佳选择。
- Servlet3.0规范已经提供方法来处理文件上传,但这种上传需要在Servlet中完成。
- 而Spring MVC则提供了更简单的封装。
- Spring MVC为文件上传提供了直接的支持,这种支持是用即插即用的MultipartResolver实现的。
- Spring MVC使用Apache Commons FileUpload技术实现了一个MultipartResolver实现类: CommonsMultipartResolver。因此,SpringMVC的文件上传还需要依赖Apache Commons FileUpload的组件。
11.2 文件上传
-
pom.xml中导入文件上传的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
【注意!!!这个bena的id必须为:multipartResolver , 否则上传文件会报400的错误!在这里栽过坑,教训!】
<!--文件上传配置--> <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):将上传文件保存到一个目录文件中
-
-
编写前端页面
-
FileController
获取文件名,创建目录;
方式一:缓冲保存:getInputStream():获取文件流 ,将上传文件保存到一个目录中。
方式二:新的保存:transferTo(File dest):将上传文件保存到一个目录文件中
11.3 文件下载
文件下载步骤:
- 设置response响应头
- 读取文件–InputStream
- 写出文件–Outputstream
- 执行操作
- 关闭流(先开后关)
注意文件的存放路径
小结
前端四要素
前端化工程:SSM+(Vue+BootStrap)
Java 全栈工程师
后台开发:主打
前端:html、css、js、jQuery、Vue、ps
运维:项目发布,服务器如何运行一个项目?