MVC三层架构
MVC
- Model:模型
- View:视图
- Controller:控制器
1.初识MVC
springMVC官方文档:https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html
SpringMVC框架技术:https://blog.csdn.net/weixin_44900198/article/details/105710177
项目:springMVC_study
导入依赖
<?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>org.example</groupId>
<artifactId>springMVC_study</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.12</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.4.RELEASE</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>
需要回顾servlet
- Spring Web MVC是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想,将web层进行职责解耦,基于请求驱动指的就是使用请求-响应模型,框架的目的就是帮助我们简化开发,Spring Web MVC也是要简化我们日常Web开发的。
2.HelloMVC
模块:springMVC_02_helloMVC
结构
步骤
- 配置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>
<!--DispatcherServlet需要关联一个spring配置文件-->
<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>
<!--springMVC中 / /*
/ : 匹配所有的请求,不匹配jsp页面
/* : 匹配所有请求,包括jsp页面
-->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
- 配置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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--添加处理器映射器-->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<!--添加处理器适配器-->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
<!--添加视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
id="internalResourceViewResolver">
<!--前缀 也就是工程路径到jsp页面前面的一部分-->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!--后缀 也就是jsp文件的后缀名-->
<property name="suffix" value=".jsp"/>
</bean>
<!--将自己得类交给IOC容器 注册bean
处理器映射器根据id去映射 相当于url-pattern -->
<bean id="/hello" class="com.it.controller.HelloController"/>
</beans>
编写HelloController.java
package com.it.controller;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author shkstart
* @create 2020-04-19 11:49
*/
//注意:我们这的到的包是是接口包不是注释包
public class HelloController implements Controller {
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
//ModelAndView 模型和视图
ModelAndView mv = new ModelAndView();
//业务代码
String result = "HelloSpringMVC";
//封装对象 放在ModelAndView中
mv.addObject("msg","result");
//封装要跳转的视图 放在ModelAndView中
mv.setViewName("hello");// /WEB-INF/jsp/hello.jsp
//返回模型视图对象
return mv;
}
}
WEB-INF / jsp / hello.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
${msg}
</body>
</html>
执行步骤
- 用户访问页面
- 调用DispatcherServlet 前端控制器
- 请求映射器 HandlerMappeing
- 请求适配器 HandlerAdapter
- 控制器
- 视图解析器
注意
- 如果我们访问tomcat 网页出现 404 错误
我们先检查代码,如果代码没问题就可能是环境问题
可能是jar包没有导入进来 解决方法
3.使用注解开发SpringMVC
模块 springMVC_03_annotation
注意:maven项目我们需要右击模块,点Add Frameworks Support ,勾选java web ,在点击file,选择project structure ,选择Artifacts ,选中模块,在web-inf 目录下创建lib文件夹,添加jar包
web.xml
<?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-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--配置springmvc配置文件 进行关联-->
<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>
<!--所有请求都被springmvc拦截-->
<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"
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.it.controller"/>
<!--让Spring不处理静态资源 例如 .css .js .html .mp3-->
<mvc:default-servlet-handler/>
<!--支持MVC注解
在springMVC中一般使用@RequestMapping完成映射
想要使@RequestMapping生效
必须在上下文中注册DefaultAnnotationMapping
和一个AnnotationMethodHandlerAdapter实例
这两个实例分别在类级别和方法级别处理
而annotation-driven配置帮助我们自动完成上述两个实例的注入-->
<mvc:annotation-driven/>
<!--配置视图解析器-->
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--前缀-->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!--后缀-->
<property name="suffix" value=".jsp"/>
</bean>
</beans>
根据springmvc中的视图解析器前缀后缀自动找到包
web-inf / jsp / hello.jsp
<%--
Created by IntelliJ IDEA.
User: 刘浩
Date: 2020/4/19
Time: 16:16
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>
创建controller 包 HelloController类
package com.it.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author shkstart
* @create 2020-04-19 16:18
*/
@Controller
public class HelloController {
//真实访问地址:项目名/hello
@RequestMapping("/hello")
public String hello(Model model){
//向模型中添加msg属性 可以在jsp页面中取
model.addAttribute("msg","Hello,SpringMVCAnnotation");
//被解析器解析处理 对应跳转的jsp名称
//web-inf/jsp/hello.jsp
return "hello";
}
}
4.Controller 和 RestFul风格
模块:springMVC_04_controller
控制器 Controller
- 控制器复杂提供访问应用程序的行为,通常通过接口定义或注解定义
- 控制器负责解析用户的请求并将其转换为Model模型
- 在SpringMVC中一个控制器可以包含多个方法
- 在SpringMVC中,对Controller的配置有很多种
实现Controller接口的操作步骤(不建议使用)
1.配置Web.xml配置文件
- 注册DispatcherServlet
- initParam标签配置spring配置文件关联
- load-on.startup 启动顺序
- 配置关联的spring配置文件
- 注册视图解析器对象
3.创建Controller类 实现Controller接口
- 重写HandleRequest 方法 返回ModelAndView对象
- 将Controller类注册到IOC容器中
- bean name 为访问路径
- class 为 对应处理请求的类 也就是Controller
5.编写前端jsp页面,注意要对应视图解析器的前缀后缀
代码在模块中
使用注解实现Controller 操作步骤(建议使用)
1.配置Web.xml配置文件
- 注册DispatcherServlet
- initParam标签配置spring配置文件关联
- load-on.startup 启动顺序
- 配置关联的spring配置文件
- 配置自动扫描包,确保spring能扫描到控制器
- 配置spring不处理静态资源
- 配置mvc:annotation-driven 支持MVC注解
<!--自动扫描包下的类 使类下注解生效-->
<context:component-scan base-package="com.it.controller"/>
<!--springmvc 不处理静态资源-->
<mvc:default-servlet-handler/>
<!--支持MVC注解 这个注解会帮助我们自动生成 映射器 和 适配器-->
<mvc:annotation-driven/>
3.创建Controller类 类名上加注解@Controller
- 写方法 方法名上@RequestMapping(“访问地址”)
- 返回值为视图位置
@Controller//代表这个类会被spring接管,别注解的类下的方法如果返回值为string
//并且有具体页面可以跳转,就会被视图解析器解析
public class HelloControllerAnnotation {
@RequestMapping("/hello")//配置访问地址
public String hello(Model model){
//处理业务层代码
String result = "Service层业务逻辑";
//封装对象到Model
model.addAttribute("msg",result);
//返回的值通过视图解析器前缀后缀拼接成一个url
return "hello";
}
4.编写前端jsp页面,注意要对应视图解析器的前缀后缀
RequestMapping()
@requestMapping 注解用于映射url到控制器类或特定的一个处理程序方法,可用于类上 或 方法上 类上表示所有请求都是以该地址作为父路径
运用在类上 访问地址就变成了 parent/url
@Controller
@RequestMapping("parent")
public class controller{
@RequestMapping("url")
public String test1(){
return "";
}
}
运用在方法上 访问地址 url
@Controller
public class Controller(){
@RequestMapping("url")
public String test1(){
}
}
RestFul风格
RestFul就是一个资源定位及资源操作风格,不是标准也不是协议,只是一种风格,基于这个风格设计软件更简洁,更有层次,更易实现缓存机制
@PathVariable 路径变量 将参数绑定到url模板上
@Controller
public class HelloControllerRestFul {
//原来的访问路径:http://localhost:8080/add?a=2&b=3
//RestFul的访问路径:http://localhost:8080/add/1/2
//两种都是传递参数
//value属性 就是请求的url地址+参数
//method属性 就是限制用什么请求方式访问
@RequestMapping(value="/add/{a}/{b}",method = RequestMethod.GET)
public String add(@PathVariable int a, @PathVariable String b, Model model){
//@PathVariable 路径变量 将参数绑定到url模板上
String result = a + b;
model.addAttribute("msg","结果:" + result);
return "hello";
}
}
还可以通过不同的注解限定请求方式
@GetMapping("add/a/b")
@PostMapping("add/a/b")
@DeleteMapping("add/a/b")
@PutMapping("add/a/b")
@PatchMapping("add/a/b")
5. 请求转发 和 请求重定向
ModelAndView
设置ModelAndView对象,根据view名称和视图解析器跳转到指定页面
页面: 视图解析器前缀 + viewName + 视图解析器后缀
- SpringMVC 不配置视图解析器
显式配置转发 forward
@Controller
public class ModelTest1 {
@RequestMapping("/test")
public String test1(Model model){
model.addAttribute("msg","不配置视图解析器");
//返回值 : forward: / + 项目路径
return "forward:/WEB-INF/jsp/hello.jsp";
}
}
显式配置重定向 redirect
@Controller
public class ModelTest1 {
@RequestMapping("/test")
public String test1(Model model){
model.addAttribute("msg","不配置视图解析器");
//返回值 : forward: / + 项目路径
return "redirect: /index.jsp";
}
}
- SpringMVC配置视图解析器
- springMVC 默认式转发
- 如果需要转发,就直接return 视图解析拼接的中间部分 因为视图解析器会根据 前缀 + return的值 + 后缀 拼接一个完整的url
- 如果需要重定向,就需要return redirect:重定向地址 需要写重定完全地址
重定向
@Controller
public class ModelTest1 {
@RequestMapping("/test")
public String test1(Model model){
model.addAttribute("msg","不配置视图解析器");
//重定向:redirect后跟全地址
return "redirect: /index.jsp";
}
}
转发
@Controller
public class ModelTest1 {
@RequestMapping("/test")
public String test1(Model model){
model.addAttribute("msg","不配置视图解析器");
//转发: return 需要拼接的
return "hello";
}
}
return "forward:hello" : WEB-INF/jsp/ 下
return "forward:/hello" : WEB-INF/jsp 下
6.接收请求参数以及回显数据
接收普通类型前端参数
@Controller //注册到IOC容器 交给Spring管理
@RequestMapping("user") //将该地址作为父路径
public class UserController {
//方法参数 --- 就是客户端传递过来的参数
@RequestMapping("/t1") //@RequestParam() 如果参数是前端的就必须加上
public String test1(@RequestParam("username") String name, Model model){
//1.接收前端参数
System.out.println(name);
//将返回结果传递给前端
model.addAttribute("name",name);
//视图跳转
return "forward:/hello";
}
}
接收对象前端参数
//前端传递一个对象
/*
前端传递的参数名和User类的属性名去比较 如果一样就赋值 不一样为null
要求就是提交的表单域和对象的属性名一致,参数使用对象即可
*/
@GetMapping("/t2")
public String test2(User user){
System.out.println(user);
return "forward:/hello";
}
}
7.解决乱码
Tomcat配置文件 serve.xml
<Connector URIEncoding="utf-8" port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
Spring的filter过滤器
<!-- spring的编码过滤器-->
<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>
如果spring的filter过滤器都无法解决 就用下面这个
package com.it.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;
public class EncodingFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
//处理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);
}
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>encodingFilter</filter-name>
<filter-class>com.it.filter.EncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
8.JSON
模块springMVC_05_json
导入依赖
<dependencies>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.3</version>
</dependency>
</dependencies>
写一个JsonUtils工具类
package com.it.utils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import java.text.SimpleDateFormat;
/**
* @author shkstart
* @create 2020-04-21 10:08
*/
public class JsonUtils {
/**
*
* @param object 返回object对应的json字符串
* @return
*/
public static String getJson(Object object){
//不需要重复造轮子 直接调用下面的方法,给一个默认值
return getJson(object,"yyyy-MM-dd HH:mm:ss");
}
/**
*
* @param object 日期对象 最后转换为json字符串
* @param dateFormat 自定义日期格式,不适用json默认时间戳
* @return 返回json字符串
*/
public static String getJson(Object object,String dateFormat){
ObjectMapper mapper = new ObjectMapper();
//设置mapper不适用默认时间戳
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,false);
//设置自定义时间戳
mapper.setDateFormat(new SimpleDateFormat(dateFormat));
try {
//返回json字符串
return mapper.writeValueAsString(object);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return null;
}
}
在springmvc-servlet.xml中配置防止json乱码
<!--mvc注解-->
<mvc:annotation-driven>
<!-- json乱码解决-->
<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>
测试
package com.it.controller;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.it.pojo.User;
import com.it.utils.JsonUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* @author shkstart
* @create 2020-04-21 9:21
*/
//@Controller 会走视图解析器
@RestController //标注这个类下的所有都不走视图解析器,都返回字符串
public class UserController {
//produces 解决中文乱码
//@RequestMapping(value="/j1",produces = "application/json;charset=utf-8")
@RequestMapping(value="/j1")
// @ResponseBody 表示这个方法不会走视图解析器,返回一个字符串,配合Controller使用
public String json1() throws JsonProcessingException {
//jackson ,ObjectMapper
ObjectMapper mapper = new ObjectMapper();
//创建一个对象
User user = new User(1,"黄沙","123123");
//将对象转化为Json字符串形式
String userString = mapper.writeValueAsString(user);
//JSON字符串形式 {键:值,键:值...}
return userString;
}
@RequestMapping("j2")
public String json2() throws JsonProcessingException {
User user1 = new User(1,"流1","11");
User user2 = new User(2,"流2","22");
User user3 = new User(3,"流3","33");
User user4 = new User(4,"流4","44");
List<User> list = new ArrayList<User>();
list.add(user1);
list.add(user2);
list.add(user3);
list.add(user4);
//将list集合转化为json字符串
return JsonUtils.getJson(list);
}
@RequestMapping("j3")
public String json3() throws JsonProcessingException {
return JsonUtils.getJson(new Date());
//纯java方式
/* Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//将时间转换为json
return new ObjectMapper().writeValueAsString(sdf.format(date));*/
}
}
@RestController 标注这个类下的所有都不走视图解析器,都返回字符串
@@ResponseBody 表示这个方法不会走视图解析器,返回一个字符串,
配合@Controller使用
9. Ajax
模块:spinrgMVC_06_ajax
AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)。
-
AJAX 是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术。
-
Ajax 不是一种新的编程语言,而是一种用于创建更好更快以及交互性更强的Web应用程序的技术。
使用jQuery提供的ajax支持
jQuery.ajax(...)
部分参数:
url:请求地址
type:请求方式,GET、POST(1.9.0之后用method)
headers:请求头
data:要发送的数据
contentType:即将发送信息至服务器的内容编码类型(默认: "application/x-www-form-urlencoded; charset=UTF-8")
async:是否异步
timeout:设置请求超时时间(毫秒)
beforeSend:发送请求前执行的函数(全局)
complete:完成之后执行的回调函数(全局)
success:成功之后执行的回调函数(全局)
error:失败之后执行的回调函数(全局)
accepts:通过请求头发送给服务器,告诉服务器当前客户端可接受的数据类型
dataType:将服务器端返回的数据转换成指定类型
"xml": 将服务器端返回的内容转换成xml格式
"text": 将服务器端返回的内容转换成普通文本格式
"html": 将服务器端返回的内容转换成普通文本格式,在插入DOM中时,如果包含JavaScript标签,则会尝试去执行。
"script": 尝试将返回值当作JavaScript去执行,然后再将服务器端返回的内容转换成普通文本格式
"json": 将服务器端返回的内容转换成相应的JavaScript对象
"jsonp": JSONP 格式使用 JSONP 形式调用函数时,如 "myurl?callback=?" jQuery 将自动替换 ? 为正确的函数名,以执行回调函数
使用SpringMVC实现Ajax异步请求
实体类User.java
public class User {
private int id;
private String name;
private String password;
有参构造 无参构造 get/set方法 toString方法
}
web.xml配置
<?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-->
<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:applicationContext.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>
<!--编码过滤器-->
<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>
</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"
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
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--自动扫描包-->
<context:component-scan base-package="com.it.controller"/>
<!--支持mvc注解-->
<mvc:annotation-driven>
<!-- json乱码解决-->
<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>
<!--spring不处理静态资源-->
<mvc:default-servlet-handler/>
<!--视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
上面的springMVC环境基本搭建完成
我们来创建一个AjaxController类
AjaxController.java
package com.it.controller;
import com.it.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* @author shkstart
* @create 2020-04-22 16:20
*/
@Controller
@RestController //返回字符串 不走视图解析器
public class AjaxController {
@RequestMapping("/t1")
public String test(){
return "hello";
}
@RequestMapping("/a1")
public void a1(String name, HttpServletResponse response) throws IOException {
if("kasha".equals(name)){
response.getWriter().write("true");
}else{
response.getWriter().write("false");
}
}
//返回list集合
@RequestMapping("/a2")
public List<User> a2(){
List<User> list = new ArrayList<User>();
list.add(new User(1,"卡莎","123"));
list.add(new User(2,"塞恩","456"));
list.add(new User(3,"薇恩","789"));
return list;
}
@RequestMapping("/a3")
public String a3(String name,String pwd){
String msg = "";
if(name != null){
if("admin".equals(name)){
msg = "用户名正确";
}else{
msg = "用户名错误";
}
}
if(pwd != null){
if("123456".equals(pwd)){
msg = "密码正确";
}else{
msg = "密码错误";
}
}
return msg;
}
}
上面AjaxController类中的方法对应的jsp页面↓
a1()方法对应的jsp页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
<script src="statics/js/jquery-3.5.0.js"></script>
<script type="text/javascript">
function a1(){
$.get({
url:"${pageContext.request.contextPath}/a1", <%--ajax请求到的地址--%>
data:{"name":$("#username").val()}, <%--ajax请求携带的参数--%>
success:function(data,status){ <%--请求成功后回调函数 data就是后端返回的参数也可以是json--%>
console.log("data:" +data);
console.log("status:" + status)
}
})
}
</script>
</head>
<body>
<%-- 失去焦点发起请求--%>
用户名:<input type="text" id="username" οnblur="a1()">
</body>
</html>
a2()方法对应的jsp页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<script src="statics/js/jquery-3.5.0.js"></script>
<script type="text/javascript">
$(function(){
$("#btn_1").click(function(){
$.ajax({
url:"${pageContext.request.contextPath}/a2",
success:function(data){
var html = "";
for (let i = 0; i < data.length; i++){
html += "<tr>" +
"<td>" + data[i]?.id + "</td>" +
"<td>" + data[i]?.name + "</td>" +
"<td>" + data[i]?.password + "</td>" +
"</tr>"
}
$("#content").html(html)
}
})
});
})
</script>
</head>
<body>
<input type="button" value="加载数据" id="btn_1">
<table>
<thead>
<tr>
<th>用户ID</th>
<th>用户名</th>
<th>密码</th>
</tr>
</thead>
<tbody id="content">
</tbody>
</table>
</body>
</html>
Ajax请求后,前端接收到后台返回的list集合 将集合循环遍历到页面
a3()对应的jsp页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<script src="statics/js/jquery-3.5.0.js"></script>
<script type="text/javascript">
function f1(){
$.post({
url:"${pageContext.request.contextPath}/a3",
data:{"name":$("#name").val()},
success:function(data){
if(data == "用户名正确"){
$("#userInfo").css("color","green");
}else{
$("#userInfo").css("color","red");
}
$("#userInfo").html(data);
}
})
}
function f2(){
$.post({
url: "${pageContext.request.contextPath}/a3",
data:{"pwd":$("#pwd").val()},
success:function(data){
if(data == "密码正确"){
$("#pwdInfo").css("color","green");
}else{
$("#pwdInfo").css("color","red");
}
$("#pwdInfo").html(data);
}
})
}
</script>
</head>
<body>
<p>
用户名:<input type="text" id="name" οnblur="f1()">
<span id="userInfo" style="color: red;width: 20px"></span>
</p>
<p>
密码:<input type="password" id="pwd" οnblur="f2()">
<span id="pwdInfo" style="color: red;width: 20px"></span>
</p>
</body>
</html>
Ajax 请求 判断用户名或密码是否正确 异步刷新
10.拦截器+文件上传
拦截器
模块:springMVC_07_interceptor
SpringMVC的处理器拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。开发者可以自己定义一些拦截器来实现特定的功能。
过滤器与拦截器的区别:拦截器是AOP思想的具体应用。
过滤器 filter
-
servlet规范中的一部分,任何java web工程都可以使用
-
在url-pattern中配置了/*之后,可以对所有要访问的资源进行拦截
拦截器 interceptor
-
拦截器是SpringMVC框架自己的,只有使用了SpringMVC框架的工程才能使用
-
拦截器只会拦截访问的控制器方法, 如果访问的是jsp/html/css/image/js是不会进行拦截的
自定义拦截器
- 创建模板springMVC_07_interceptor 支持web
- 配置web.xml 和 applicationContext.xml 或 springMVC-servlet.xml
- 编写一个类(拦截器) 实现HandlerInterceptor接口 重写方法
package com.it.MyInterceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author shkstart
* @create 2020-04-23 10:18
*/
//实现HandlerInterceptor 接口 就是一个拦截器了
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("=========处理后==========");
}
//dispatcherservlet处理后执行,做清理工作
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("==============清理===========");
}
}
- 配置sringMVC-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
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.it.controller"/>
<mvc:default-servlet-handler/>
<mvc:annotation-driven/>
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!--配置SpringMVC拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<!--
/** 表示 包括路径及其子路径
/admin/* 表示 拦截admin/add这种,admin/add/user 就不会被拦截
/admin/** 表示 拦截admin/下所有
-->
<mvc:mapping path="/**"/>
<!--bean 就是配置的拦截器-->
<bean class="com.it.MyInterceptor.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
</beans>
- 编写一个Controller类测试拦截器
package com.it.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author shkstart
* @create 2020-04-23 10:15
*/
//测试拦截器
@RestController
public class MyController {
@RequestMapping("/t1")
public String test1(){
System.out.println("OK");
return "OK";
}
}
- 启动Tomcat 访问t1 控制台输出
利用拦截器验证用户是否登录
- 创建三个页面 WEB-INF/jsp/login.jsp , WEB-INF/jsp/main.jsp 还有默认index.jsp
login.jsp登录页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登录</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/user/login" method="post">
用户名:<input type="text" name="username"> <br/>
密码: <input type="password" name="password">
<input type="submit" value="登录">
</form>
</body>
</html>
- 创建LoginController类
package com.it.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpSession;
/**
* @author shkstart
* @create 2020-04-23 11:15
*/
@Controller
@RequestMapping("user")
public class LoginController {
//跳转到登录页面
@RequestMapping("/goLogin")
public String goLogin(){
return "login";
}
//跳转到首页
@RequestMapping("/goMain")
public String main(){
return "main";
}
//登录提交
@RequestMapping("/login")
public String login(HttpSession session,String username){
System.out.println("接收前端=====");
//向session记录登录信息
session.setAttribute("user",username);
return "main";
}
//注销操作
@RequestMapping("/goOut")
public String goOut(HttpSession session){
session.removeAttribute("user");
return "redirect:/index.jsp";
}
}
- 编写main.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>首页</title>
</head>
<body>
<h1>首页</h1>
<h4>用户:${sessionScope.get("user")}</h4>
<h3><a href="${pageContext.request.contextPath}/user/goOut">注销</a></h3>
</body>
</html>
- 编写Index.jsp页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title></title>
</head>
<body>
<h1>
<a href="${pageContext.request.contextPath}/user/goLogin">登录</a>
</h1>
<h1>
<a href="${pageContext.request.contextPath}/user/goMain">首页</a>
</h1>
</body>
</html>
- 编写LoginInterceptor拦截器
package com.it.MyInterceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
* @author shkstart
* @create 2020-04-23 11:27
*/
public class LoginInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession();
//判断如果当前页面是login页面就放行
if(request.getRequestURI().contains("login")){
return true;
}
//如果已经登陆 Session中有数据 也放行
if(session.getAttribute("user") != null){
return true;
}
//用户没有登录 跳转到登录页面
request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request,response);
return false;
}
}
- 将拦截器配置到spirngmvc-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
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.it.controller"/>
<mvc:default-servlet-handler/>
<mvc:annotation-driven/>
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!--配置SpringMVC拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<!--
/** 表示 包括路径及其子路径
/admin/* 表示 拦截admin/add这种,admin/add/user 就不会被拦截
/admin/** 表示 拦截admin/下所有
-->
<mvc:mapping path="/**"/>
<!--bean 就是配置的拦截器-->
<bean class="com.it.MyInterceptor.MyInterceptor"/>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/user/**"/>
<bean class="com.it.MyInterceptor.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
</beans>
文件上传和下载
模块:springMVC_08_file
- 如果想使用Spring的文件上传功能,则需要在上下文中配置MultipartResolver。
- 前端表单要求:为了能上传文件,必须将表单的method设置为POST,并将enctype设置为multipart/form-data。只有在这样的情况下,浏览器才会把用户选择的文件以二进制数据发送给服务器;
文件上传
1.导入依赖
<!--文件上传-->
<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>
编写web.xml 配置dispatcher和filter编码过滤器
略
编写applicationContext.xml 配置MVC注解、过滤静态资源,自动扫描表、视图解析器、文件上传bean
<?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
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--自动扫描包-->
<context:component-scan base-package="com.it.controller"/>
<!--过滤静态资源-->
<mvc:default-servlet-handler/>
<!--MVC注解支持-->
<mvc:annotation-driven/>
<!--视图解析器-->
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!--文件上传配置-->
<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>
</beans>
CommonsMultipartFile 的 常用方法:
String getOriginalFilename():获取上传文件的原名
InputStream getInputStream():获取文件流
void transferTo(File dest):将上传文件保存到一个目录文件中
编写jsp页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>文件上传和下载</title>
</head>
<body>
<%--必须将 method设置为post请求 enctype设置为multipart/form-data --%>
<form action="${pageContext.request.contextPath}/upload1" method="post" enctype="multipart/form-data">
<input type="file" name="file"><br/>
<input type="submit" value="upload">
</form>
</body>
</html>
编写Controller层
方式一:
package com.it.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 java.io.*;
/**
* @author shkstart
* @create 2020-04-24 10:05
*/
//@RestController 不走视图解析器
@Controller
public class MyController {
@RequestMapping("/upload1")
//@RequestParam("file") 将name=file的控件得到到文件封装成CommonsMultipartFile对象
//批量上传 CommonsMultipartFile为数组即可
public String fileUpload1(@RequestParam("file")CommonsMultipartFile file,
HttpServletRequest request) throws IOException {
//获取文件名
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 来保存上传的文件
@RequestMapping("/upload2")
public String fileUpload2(@RequestParam("file") CommonsMultipartFile file,
HttpServletRequest request) throws IOException {
//上传路径保存设置
String path = request.getServletContext().getRealPath("/upload");
//获取到File对象
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";
}
文件下载
1.设置文件响应头 response
2.读写操作
@RequestMapping("/download")
public String fileDownload(HttpServletRequest request, HttpServletResponse response) throws IOException {
//要下载的图片地址
String path = request.getServletContext().getRealPath("/upload");
//图片名
String fileName = "这是要闹哪样.jpg";
//1.设置response响应头
response.reset();//设置页面不缓存,清空buffer
response.setCharacterEncoding("utf-8");//字符编码
response.setContentType("multipart/form-data");//二进制传输数据
//设置响应头
response.setHeader("Content-Disposition",
"attachement;fileName="+ URLEncoder.encode(fileName,"utf-8"));
File file = new File(path,fileName);
//2.读取文件 输入流
InputStream input = new FileInputStream(file);
//3.读取文件 输出流
OutputStream output = response.getOutputStream();
byte[] buffer = new byte[1024];
int index = 0;
//4.执行写操作
while((index = input.read(buffer)) != -1) {
output.write(buffer,0,index);
output.flush();
}
output.close();
input.close();
return null;
}