SpringMVC学习笔记
SpringMVC
SSM:Mybatis+Spring+SpringMVC MVC三层架构
1.什么是MVC
M(Model),V(View),C(controller) DAO,Service,Jsp/Html,Servlet Jsp+javaBean+Servlet
2.复习Servlet
1.先配置实体类继承HttpServlet
2.在WEB-INF的xml文件中配置相关的servlet的注册
在下面的这张图中有个新的标签
3.HelloSpringMVC important 底层原理
提前注意,之前在写jsp的时候就出现的问题
在本文件夹中的project structure的war下,可能当前需要的依赖包没有导入,这个点需要检查
1.先配置相应的Servlet.xml文件
下面的这个包里面有内置的Servlet,所以可以直接使用
org.springframework.web.servlet.DispatcherServlet
在下面的
当值为0或者大于0时,表示容器在应用启动时就加载这个servlet;
当是一个负数时或者没有指定时,则指示容器在该servlet被选择时才加载。
正数的值越小,启动该servlet的优先级越高。
下面的这个
/ 代表的是匹配所有的请求(不包括.jsp) important
/* 代表的是匹配所有的请求(包括.jsp)
这上面的差别就是用户是从.jsp文件目录直接访问该网页呢,还是靠虚拟路径来实现页面的跳转
2.配置相应的Spring-Servlet.xml文件
这下面的三个class是十分重要的
1.org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping
这个代表的是给文件项目里注册了处理映射器
2.org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter
这个代表的是给文件项目里注册了处理适配器
3.org.springframework.web.servlet.view.InternalResourceViewResolver
这个代表的是给文件项目里注册了试图解析器,这其中的属性,是和下面的网页访问对应的jsp文件进行拼接的时候用的
以上的三个步骤是固定的,无需修改
下面要修改的是controller的注册
最下面的那个
注意
第一个property中的属性值
/jsp/ 和 ./jsp/的区别:
第一个的根基目录就是Web的目录
第二个是相对于当前网页目录的前一个目录下的jsp文件
所以第一个更加好,索引也更加的标准
<?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
http://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.feng.controller"/>
<mvc:annotation-driven/>
<mvc:default-servlet-handler/>
</beans>
3.配置对应的controller的文件
这个是实现了Controller接口的方法,这个ModelView是模型与视图
下面的方法是将Object对象封装起来放在了页面中
setViewName是和上面的Servlet.xml中的
在这个文件中可以实现业务的访问和工作
4.配置对应的jsp文件
在这个文件就实现了msg的读取
5.文件的配置
4.注解实现HelloSpringMVC
1.配置mvc和context在Servlet.xml的引入
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd"
在这些都引入的情况下,就可以进行下面的步骤了
先扫描注解需要生效的包,也就是< context:component-scan >的作用
<mvc:default-servlet-handler/>
<mvc:annotation-driven/>
上面的这些标签分别代表
- 让springmvc不处理静态资源
- 这个功能十分强大,他将上面需要配置的Mapper和Adapter都配置完了,所以下面直接接入Resolver
2.实体类的配置
@Controller这个声明这个类是一个Controller类,代表是网页控制类
@RequestMapping这个声明这是一个网页的处理方法,默认是post和get方法都能访问
这个方法返回的String字符串就是和上面的需要拼接的字符串的网页jsp文件
注意
在这种方法下可以实现jsp文件的重复利用性
在这种多次网页切换的情况下,就只需要一个jsp文件就可以实现重复使用性
在最上面加个@RequestMapping可以实现多级目录的效果
5.RestFul风格
1.基本原理
- 系统上的一切对象都要抽象为资源
- 每个资源对应唯一的资源标识
- 对资源的操作不能改变资源标识本身
- 所有的操作都是无状态的
可以通过不同的请求方式来实现不同的效果!
例子:百度百科就是典型的RestFul风格
2.传统网页的配置
下面需要访问该网页并实现该网页功能的网址是如下图,就像是get方法一样传入参数,这个网页默认的提取方式为get的方式
2.RestFul的配置
在如下的这张图中直接将a,b的变量变为该网页要访问的路径之一,从而实现该网页的功能,访问该网站的网址为
通过下面的方式可以实现相同的网址,但是结果不同,也就是因为访问的方式不同,结果也不同
HTML中的FORM是传统的网页路径的设置
下面的方法简化上述的@RequestMapping的设置方法的复杂
相应的POST有对应的@PostMapping的注解,其他的也有相应的注解
4.RestFul的好处
还有一点安全
6.重定向和转发
1.配置视图解析器
ModelAndView 的 setViewName 的方法来实现重定向
通过prefic和suffix的字符串拼接来实现相应页面的跳转
2.通过HttpServlet
上面的那个方法是通过HttpServletResponse的sendRedirect的方法,着个方法不会将当前的response和request中存在的东西传递下去,所以这里还新加了个HttpSession来存在数据在浏览器中,这要就可以达到重定向和传递数据的功能,当然还可以通过ModelAndView来进行存储
下面的那个方法是通过HttpServletRequest的getRequestDispatcher的方法来达到转发的功能,在加上forward的功能来转发数据
注意 第一种方法会改变当前页面的网页,第二种不会改变
3.通过Return语句
测试前,先将视图解析器去掉
这个方法默认是转发,这个方法就是在return语句中加上forward或者redirect的标签
4.存在视图解析器
直接通过return相关语句来重定向
7.接受请求参数及数据回显
1.接受请求参数
1.处理提交参数名相同的数据
访问方式为get的方法,访问地址为 localhost:7878/feng/test2/t1?name=feng
2.处理提交参数名不同的数据
通过给参数设置@RequestParam的属性设置域名访问的参数,通过这个方法参数就必须被设置
访问地址为 localhost:7878/feng/test2/t2?username=feng
3.传入参数是一个实体类
直接就是把实体类中的属性值设置一下就可以将参数传入了,这些参数不是必要的
访问地址为 localhost:7878/feng/test2/t3?id=1&name=feng&age=2
2.数据显示在前端上
0.前端页面
通过${msg}来访问该属性
1.通过ModelAndView
在视图上添加属性,之后在前端访问
2.通过ModelMap
3.通过Model 用的最多
8.乱码问题
插曲
注意:form表中的action=””的路径一定要设置为相对路径”LoginServlet”,绝对路径”/LoginServlet”是找不到的。
这里直接使用了Spring自带的Filter类,这个是固定的语句
注意url-pattern的 / 和 /* 的区别,前面有讲
下面是大神的过滤器的实现,这个是通用过滤器,注意要在web.xml中注册filter
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 {
//处理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;
}
}
9.JSON important
1.json的格式
json是一种键值对的行书存在的,键名用双引号""来包裹,使用冒号:来进行分隔,然后紧接着的就是值,这个本质就是一个字符串
实例
'{"a":"hello","b":"SpringMVC"}'
javaScript对象,这个本质就是一个对象
实例
{a:"hello",b:"SpringMVC"}
其中有两个方法:
JSON.stringify() 这个方法是将js对象转化为json字符串
JSON.parse() 这个方法是将json字符串转化为js对象
测试程序如图:
结果如下图:第一个是JSON字符串,第二个是javaScript对象
2.准备工作,引入jackSON
下面的是jackSon的依赖包,实际的效果和导入的maven依赖包有关,直接就下载下面的maven依赖的包
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.8</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.8</version>
</dependency>
3.Spring-servlet.xml和web.xml的配置
这一部分的配置和之前配置的是一样的,但是spring-servlet.xml中加入了处理json的乱码问题
下面的方法一:
<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>
方法二:
@RequestMapping(value = "/t4",produces = "application/json;charset=utf-8")
以上的两种方法都可以有效的解决乱码问题
4.测试类的配置
这里分别给出了两种方法
新的注解
@RestController
@RestController是@Controller和@ResponseBody的结合体,意味着这个标签可以实现两种共功能,这个是返回的是json对象,需要jackson的支持
@ReponseBody
使用@ReponseBody时,该方法不会走入视图解析器,所以return语句不会被拼接,就可以直接显示到页面上,这个也是返回json对象的,需要jackson的支持
方式一:
直接通过return语句来返回值,这里要控制返回值的类型,这种方法会直接调用jackson的json转换的方法
方式二: 首推
通过ObjectMapper的映射器来将对象转化为json对象
new ObjectMapper().writeValueAsString(java对象); 将java对象转换为json对象
new ObjectMapper().readValue(json字符串,java对象.class); json转java对象
json转类对象的参数为java对象.class
json转集合对象的参数为new TypeReference<List<java对象类>>(){}
这个TypeReference是jackson中的类,别导包错了
这个方法可以设置为工具类,参数为Object
fastjson
这个是阿里巴巴开发的json转换的依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.60</version>
</dependency>
他的效果是和前面讲到的jackson是一样的
10.SSM框架整合 important
1.Mybatis层
1.建立相应的数据库
2.先建立实体类
3.建立相应的接口类
4.配置.xml文件
1先配置对应的mybatis-config.xml文件
2.配置对应的mapper文件
对应的实现方法类的配置
现在第一步被Mybatis-spring给整合了
<context:property-placeholder location="classpath:properties/db.properties"/>
上面的这个是导入文件,导入对应的properties文件,也就是存有驱动的文件,链接数据库
下面这张图中有新的数据源,也就是c3p0的数据源,这个数据源有很多的属性可以设置
下面的org.mybatis.spring.mapper.MapperScannerConfigurer,它会将查找到类路径下的映射器并自动将他们创建成MapperFactoryBean,也就是自动注册了,可以直接使用了,这个点需要多加了解 important,还有这下面有个参数叫做autoCommitOnClose,连接池在回收数据库连接时是否自动提交事务。如果为false,则会回滚未提交的事务,如果为true,则会自动提交事务。这个参数不建议使用。要实现事务提交可以用AOP的事务提交
5.配置接口类的实现类
在这个类中可以实现别的业务,也就是加入别的功能,所以在service中配置,经过下面的xml文件的配置,就可以直接调用对应的方法来实现功能了
6.配置对应的service.xml文件
通过注册实现接口类的实体类,并将相应的属性给赋值,这个bookMapper就是之前自动生成的mapper,所以可以调用对应的方法
2.SpringMVC层
1.建立对应的controller包
2.建立对应的xml文件
这个文件和之前的没有区别
3.Spring层
建立一个全部整合的文件
4.整合SSM框架
jsp页面出现乱码,使用filter过滤器来实现字符转码,这个可以使用spring提供的Filter,还可以自己做一个Filter来实现该功能,记得要在web.xml文件中注册该filter
Filter文件中的Filter
web.xml
最后就是通过jsp文件,也就是前端的界面来整合mapper接口中的方法来实现增删改查的操作
注意
在这个项目中还有一个功能没实现,就是删除的时候,编号自动变化
11.Ajax
1.使用iframe实现Ajax的效果
iframe就是一个页面内嵌技术,可以达到一部分页面的重复使用的效果,这个只是一个伪Ajax
2.真正的Ajax
真正的Ajax可以实现异步刷新的功能
实例1:
1.配置对应的操作界面
这里用到的onblur的意思是失去鼠标焦点的时候就会去调用方法
这里的方法调用了Ajax的post的方法,但是这个方法的实质还是$.Ajax
在这个方法中有四个参数url,data(可选),callback(可选),type(可选)
-
url:是回调函数要实现的时候访问的url的地址
-
data:是要传递的参数
-
callback:是实现该方法的时候的回调函数,也就是接下来的操作
下面的success是回调函数,这个回调函数将data的数据传到了controller中,让其处理
2.配置对应当Controller文件
在这个controller中,加了一个@RestController,这个注解的含义是不会去调用视图解析器,也就不会去拼接路径
实例2:数据回显
1.配置对应的操作界面
2.配置对应的js的方法
这段是经典的jQuery的写法
回调函数将返回值放到这里,也就是data参数,这里在经过拼接技术,将界面恢复到了对应的标签下
3.对应的controller文件
这里有个实体类,就是User的实体类
实例3:账户验证
1.配置jsp文件
2.配置对应的jQuery的方法
3.配置对应的controller方法
这个和if的顺序没有关系,因为前端的时候就设置了onblur的属性,所以只有失去焦点的时候才会调用方法
12.拦截器
1.过滤器和拦截器的区别
过滤器:
-
过滤是servlet的一部分,在任何javaweb的工程都可以使用
-
在url-pattern中配置了/*之后,就要对所有的资源进行过滤(包括js,jsp等文件)
拦截器:
- 拦截器是SpringMVC框架自己的,只有使用了SpringMVC框架的工程才能使用
- 拦截器只会拦截访问的控制器方法,如果访问的是jsp/html/css等是不会拦截的
2.拦截器的实现
1.配置对应的实现类
通过继承HandlerInterceptor来实现拦截器的功能,但是这个类不需要重载方法,但是要实现拦截器的话,要重写这个类的第一个方法preHandle,这个类实现了AOP的方法
这个方法,当return true的时候,代表可以继续执行下去,当return false的时候,代表不会继续执行下去
2.配置spring-mvc.xml文件
这个配置,也就是注册这个拦截器
这里的mapping要注意,/**
代表的是包括包括路径及其子路径,而\*
代表的只是下一个路径
13.文件的上传和下载
1.设置要求
为了能上传文件,必须将表单的method设置为post,并将enctype设置为multipart/form-data。只有在这样的情况下,浏览器才会把用户选择的文件以二进制数据发送给服务器
2.对表单中的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响应。
3.文件上传
SpringMVC提供了直接的支持,是通过MultipartResolver的类接口来实现的
1.导入Apache Commons FileUpload依赖
<!--文件上传支持-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
<!--服务器支持-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
2.注册multipartResolver
- org.springframework.web.multipart.commons.CommonsMultipartResolver
这个文件名是upload的关键文件,通过注册这个文件,可以使用SpringMVC提供的文件下载的包,注意这个id必须是multipartResolver,要不然会报错
下面这段是固定的,可以直接使用
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="utf-8"/>
<property name="maxUploadSize" value="10485760"/>
<property name="maxInMemorySize" value="40960"/>
</bean>
3.写相关的controller类
下面的这段代码也是可以直接使用
package com.feng.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
@Controller
public class HelloController3 {
//@RequestParam("file") 将name=file控件得到的文件封装成CommonsMultipartFile 对象
//批量上传CommonsMultipartFile则为数组即可
@RequestMapping("/upload")
public String fileUpload(@RequestParam("file") CommonsMultipartFile file , HttpServletRequest request) throws IOException {
//获取文件名 : file.getOriginalFilename();
String uploadFileName = file.getOriginalFilename();
//如果文件名为空,直接回到首页!
if ("".equals(uploadFileName)){
return "redirect:/index.jsp";
}
System.out.println("上传文件名 : "+uploadFileName);
//上传路径保存设置
String path = request.getServletContext().getRealPath("/upload");
//如果路径不存在,创建一个
File realPath = new File(path);
if (!realPath.exists()){
realPath.mkdir();
}
System.out.println("上传文件保存地址:"+realPath);
InputStream is = file.getInputStream(); //文件输入流
OutputStream os = new FileOutputStream(new File(realPath,uploadFileName)); //文件输出流
//读取写出
int len=0;
byte[] buffer = new byte[1024];
while ((len=is.read(buffer))!=-1){
os.write(buffer,0,len);
os.flush();
}
os.close();
is.close();
return "redirect:/index.jsp";
}
}
4.相关jsp页面的配置
这里要注意enctype的类型还有method的方法类型
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/upload" enctype="multipart/form-data" method="post">
<input type="file" name="file"/>
<input type="submit" value="upload">
</form>
</body>
</html>
4.文件下载
方式一:
直接使用一个来进行资源定向,让浏览器来替我们做这个文件下载的事情
方式二:
配置一个controller类
代码可以直接使用,不过要改文件路径,因为这些都是静态资源所以可以固定下载路径
package com.feng.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
@Controller
public class HelloController4 {
@RequestMapping(value="/download")
public String downloads(HttpServletResponse response , HttpServletRequest request) throws Exception{
//要下载的图片地址
String path = request.getServletContext().getRealPath("/resources");
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;
}
}
文件结构
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
</dependency>