1. 一个简单的springMVC程序
jar:
spring-aop.jar
spring-bean.jar
spring-context.jar
spring-core.jar
spring-web.jar
spring-webmvc.jar
commons-logging.jar
报错NoClassDefFoundError:缺少jar
springmvc的作用相当于servlet
实现效果:利用springMVC从主页跳转到另一个页面
index.jsp文件:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
Insert title here
welcome
这个界面只有一个超链接
web.xml文件
SpringMVCProject3
index.html
index.htm
index.jsp
default.html
default.htm
default.jsp
springDispatcherServlet
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
classpath:springmvc.xml
1
springDispatcherServlet
/
其中:
/:一切请求 ,注意不是 /*
/user:拦截以 /user开头的请求
/user/abc.do :只拦截该请求
.action:只拦截 .action结尾的请求
SpringMVCHandler.java(其作用类似于servlet类)
package com.handler;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class SpringMVCHandler {
@RequestMapping("welcome")
public String welcome() {
return "success";
}
}
springmvc.xml文件:
该类添加了扫描标签以及SpringMVCHandler.java
中返回的success加上前后缀,成为要定向到的新界面。
success.jsp:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
Insert title here
welcome to springmvc
注意:通过
contextConfigLocation
classpath:springmvc.xml
指定springmvc配置文件的路径,如果要省略,必须放到 默认路径:
/WEB-INF/springDispatcherServlet-servlet.xml
小技巧:ctrl+shift+t打开open type 查找类文件
2.RequestMapping映射及个属性
映射是 去匹配@RequestMapping注解,可以和方法名、类名不一致
通过method指定 请求方式(get post delete put) @RequestMapping(value="welcome",method=RequestMethod.POST)//映射
params= {"name2=zs","age!=23"}
name2:必须有name="name2"参数
age!=23 : a.如果有name="age",则age值不能是23
b.没有age
!name2 :不能name="name2"的属性
ant风格的请求路径
? 单字符
* 任意个字符(0或多个)
** 任意目录
例如:
@RequestMapping(value="welcome3/**/test")
接受示例:
a href="welcome3/abc/xyz/abccc/test"
通过@PathVariable获取动态参数
前端通过/将路径与参数分离,然后在后台获得参数方法如下:3.利用IDE快速配置web.xml文件
alt+/快捷键然后选中dispatcherservlet
4.REST风格:软件编程风格
Springmvc:
GET :查
POST :增
DELETE :删
PUT :改
普通浏览器 只支持get post方式 ;其他请求方式 如 delelte|put请求是通过 过滤器新加入的支持。
springmvc实现 :put|post请求方式的步骤:
a.增加过滤器
HiddenHttpMethodFilte
org.springframework.web.filter.HiddenHttpMethodFilter
HiddenHttpMethodFilte
/*
b.表单
i:必须是post方式
ii:通过隐藏域 的value值 设置实际的请求方式 DELETE|PUT
c.控制器
@RequestMapping(value="testRest/{id}",method=RequestMethod.DELETE)
public String testDelete(@PathVariable("id") Integer id) {
System.out.println("delete:删 " +id);
//Service层实现 真正的增
return "success" ;// views/success.jsp,默认使用了 请求转发的 跳转方式
}
通过 method=RequestMethod.DELETE 匹配具体的请求方式
此外,可以发现 ,当映射名相同时@RequestMapping(value="testRest),可以通过method处理不同的请求。
当运行时报错 JSPs only permit GET, POST or HEAD. Jasper also permits OPTIONS时,可以在要跳转的页面
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" isErrorPage="true"%>
多加一句话:isErrorPage设置为true,默认为false
5.过滤器部分源码
过滤器中 处理put|delete请求的部分源码:
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
HttpServletRequest requestToUse = request;
if ("POST".equals(request.getMethod()) && request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) == null) {
String paramValue = request.getParameter(this.methodParam);
if (StringUtils.hasLength(paramValue)) {
requestToUse = new HttpMethodRequestWrapper(request, paramValue);
}
}
filterChain.doFilter(requestToUse, response);
}
原始请求:request,改请求默认只支持get post header
但是如果 是"POST" 并且有隐藏域
则,过滤器 将原始的请求 request加入新的请求方式DELETE,并将原始请求 转为 requestToUse 请求(request+Delete请求)
最后将requestToUse 放入 请求链中, 后续再事情request时 实际就使用改造后的 requestToUse
6.mvc获取一些其它信息
接收前端传值的另一种方式:
@RequestParam("uname") String name,@RequestParam(value="uage",required=false,defaultValue="23")
@RequestParam("uname"):接受前台传递的值,等价于request.getParameter("uname");
required=false:该属性 不是必须的。
defaultValue="23":默认值23
获取请求头信息 @RequestHeader
public String testRequestHeader(@RequestHeader("Accept-Language") String al ) {
}
通过@RequestHeader("Accept-Language") String al 获取请求头中的Accept-Language值,并将值保存再al变量中
通过mvc获取cookie值(JSESSIONID)
@CookieValue
(前置知识: 服务端在接受客户端第一次请求时,会给该客户端分配一个session (该session包含一个sessionId)),并且服务端会在第一次响应客户端时 ,请该sessionId赋值给JSESSIONID 并传递给客户端的cookie中
小结:
SpringMVC处理各种参数的流程/逻辑:
请求: 前端发请求a-> @RequestMappting("a")
处理请求中的参数xyz:
@RequestMappting("a")
public String aa(@Xxx注解("xyz") xyz)
{
}
使用对象(实体类Student)接受请求参数
前端页面:
后台界面:
@RequestMapping(value="testObjectProperties")
public String testObjectProperties(Student student) {//student属性 必须 和 form表单中的属性Name值一致(支持级联属性)
/*
String name = request.getParameter("name");
int age= Integer.parseInt(request.getParameter("age")s) ;
String haddrss = request.getParameter("homeaddress");
String saddress = request.getParameter("schooladdress");
Address address = new Address();
address.setHomeAddress(haddrss);
address.setSchoolAddress(saddress);
Student student = new Student();
student.setName(name);
student.setAddress(address);
*/
System.out.println(student.getId()+","+student.getName()+","+student.getAddress().getHomeAddress()+","+student.getAddress().getSchoolAddress());
return "success" ;
}
在SpringMVC中使用原生态的Servlet API :HttpServletRequest :直接将 servlet-api中的类、接口等 写在springMVC所映射的方法参数中即可:
@RequestMapping(value="testServletAPI")
public String testServletAPI(HttpServletRequest request,HttpServletResponse response) {
// request.getParameter("uname") ;
System.out.println(request);
return "success" ;
}
7.处理模型数据
如果跳转时需要带数据:V、M,则可以使用以下方式:
ModelAndView、ModelMap 、Map、Model -数据放在了request作用域
@SessionAttributes、@ModelAttribute
示例:
public String testModel(Model model| Map
m.put(x,".."); 就会将x对象 放入request域中
如何将上述数据放入session中?@SessionAttributes(..)
例如(ModelAndView):
@RequestMapping("testModelAndView")
public ModelAndView testModelAndView() {
ModelAndView mv = new ModelAndView("success");
Student student = new Student();
student.setId(1);
student.setName("zs");
mv.addObject("student",student);
return mv;
}
例如(Model、Map、ModelMap):
@RequestMapping("testModel")
public String testModel(Model model) {
Student student = new Student();
student.setId(1);
student.setName("zs");
model.addAttribute("student1", student);
return "success";
}
@ModelAttribute
i.经常在 更新时使用
ii.在不改变原有代码的基础之上,插入一个新方法。
通过@ModelAttribute修饰的方法 ,会在每次请求前先执行;
并且该方法的参数map.put()可以将 对象 放入 即将查询的参数中;
必须满足的约定:
map.put(k,v) 其中的k 必须是即将查询的方法参数 的首字母小写
testModelAttribute(Student xxx) ,即student;
如果不一致,需要通过@ModelAttribute声明。如下:
@ModelAttribute//在任何一次请求前,都会先执行@ModelAttribute修饰的方法
public void queryStudentById(Map map) {
//StuentService stuService = new StudentServiceImpl();
//Student student = stuService.queryStudentById(31);
//模拟调用三层查询数据库的操作
Student student = new Student();
student.setId(31);
student.setName("zs");
student.setAge(23);
map.put("stu", student) ;//约定:map的key 就是方法参数 类型的首字母小写
}
//修改:Zs-ls
@RequestMapping(value="testModelAttribute")
public String testModelAttribute(@ModelAttribute("stu")Student student) {
student.setName(student.getName());//将名字修改为ls
System.out.println(student.getId()+","+student.getName()+","+student.getAge());
return "success";
}
一个Servlet 对应一个功能:
增删改查 对应于 4个Servlet
更新: Servlet - SpringMVC的Controller
查询
@ModelAttribute
public void query()
{
}
修改
public String update()
{
}
@ModelAttribute会在 该类的每个方法执行前 均被执行一次,因为使用时需要注意。
8.视图、视图解析器
视图的顶级接口:View
视图解析器:ViewResolver
常见的视图和解析器:
InternalResourceView、InternalResourceViewResolver
public class JstlView extends InternalResourceView:
springMVC解析jsp时 会默认使用InternalResourceView,
如果发现Jsp中包含了jstl语言相关的内容,则自动转为JstlView。
JstlView 可以解析jstl\实现国际化操作
国际化: 针对不同地区、不同国家 ,进行不同的显示
中国:(大陆、香港) 欢迎
美国: welcome
i18n_zh_CH.properties
resource.welcome=你好
resource.exist=退出
i18n.properties
具体实现国际化步骤:
a.创建资源文件
基名语言地区.properties
基名_语言.properties
b.配置springmvc.xml,加载资源文件
ResourceBundleMessageSource会在springmvc响应程序时 介入(解析国际化资源文件)
c.通过jstl使用国际化
jstl.jar standar.jar
springmvc在启动时,会自动查找一个id="messageSource"的bean,如果有 则自动加载
9.InternalResourceViewResolver其他功能:
之前实现:index.jsp -> Controller(@RequsetMapping("a")) ->succes.jsp
要用SpringMVC实现:index.jsp -> succes.jsp :
以上注解 ,会让所有的请求 转入中匹配映射地址,而会忽略调@RequsetMapping();
如果想让 @RequsetMapping("a") 和
指定请求方式
指定跳转方式:return "forward:/views/success.jsp";
forward: redirect: ,需要注意 此种方式,不会被视图解析器加上前缀(/views)、后缀(.jsp)
处理静态资源:html css js 图片 视频
在SpringMVC中,如果直接访问静态资源:404 。原因:之前将所有的请求 通过通配符“\” 拦截,进而交给 SPringMVC的入口DispatcherServlet去处理:找该请求映射对应的 @requestMapping
http://localhost:8888/SpringMVCProject/img.png
@RequsetMapping("img.png")
return sucess
解决:如果是 需要mvc处理的,则交给@RequsetMapping("img.png")处理;如果不需要springmvc处理,则使用 tomcat默认的Servlet去处理。
tomcat默认的Servlet去处理:如果有 对应的请求拦截,则交给相应的Servlet去处理;如果没有对应的servlet,则直接访问。
tomcat默认的Servlet在哪里?在tomcat配置文件\conf\web.xml中
abc
xxx.xxx.xx.ABCServlet
abc
/abc
解决静态资源方案:如果有springmvc对应的@requestMapping则交给spring处理;如果没有对应@requestMapping,则交给服务器tomcat默认的servlet去处理 :实现方法,只需要增加2个注解即可 springmvc.xml:
总结:要让springmvc访问静态资源,只需要加入以下2个注解:
类型转换
a.Spring自带一些 常见的类型转换器:
public String testDelete(@PathVariable("id") String id) ,即可以接受int类型数据id 也可以接受String类型的id
b.可以自定义类型转换器
i.编写 自定义类型转器的类 (实现Converter接口)
public class MyConverter implements Converter{
@Override
public Student convert(String source) {//source:2-zs-23
//source接受前端传来的String:2-zs-23
String[] studentStrArr = source.split("-") ;
Student student = new Student();
student.setId( Integer.parseInt( studentStrArr[0]) );
student.setName(studentStrArr[1]);
student.setAge(Integer.parseInt(studentStrArr[2] ));
return student;
}
}
ii.配置:将MyConverter加入到springmvc中
测试转换器:
@RequestMapping(value="testConverter")
public String testConverter(@RequestParam("studentInfo") Student student) {// 前端:2-zs-23
System.out.println(student.getId()+","+student.getName()+","+student.getAge());
return "success";
}
其中@RequestParam("studentInfo")是触发转换器的桥梁:
@RequestParam("studentInfo")接受的数据 是前端传递过来的:2-zs-23 ,但是 需要将该数据 复制给 修饰的目的对象Student;因此SPringMVC可以发现 接收的数据 和目标数据不一致,并且 这两种数据分别是 String、Student,正好符合public Student convert(String source)转换器。
数据格式化
SimpleDateForamt sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
SPringMVC提供了很多注解,方便我们数据格式化
实现步骤:
a.配置
b.通过注解使用
@DateTimeFormat(pattern="yyyy-MM-dd")
@NumberFormat(parttern="###,#")
错误消息
public String testDateTimeFormat(Student student, BindingResult result ,Map
需要验证的数据是 Student中的birthday, SPringMVC要求 如果校验失败 则将错误信息 自动放入 该对象之后紧挨着的 BindingResult中。
即Student student, BindingResult result之间 不能有其他参数。
如果要将控制台的错误消息 传到jsp中显示,则可以将 错误消息对象放入request域中,然后 在jsp中 从request中获取。
数据校验
JSR303
Hibernate Validator
使用Hibernate Validator步骤:
a.jar(注意各个jar之间可能存在版本不兼容)
hibernate-validator-5.0.0.CR2.jar classmate-0.8.0.jar jboss-logging-3.1.1.GA.jar
validation-api-1.1.0.CR1.jar hibernate-validator-annotation-processor-5.0.0.CR2.jar
b配置
此时mvc:annotation-driven的作用:要实现Hibernate Validator/JSR303 校验(或者其他各种校验),必须实现SpringMVC提供的一个接口:ValidatorFactory
LocalValidatorFactoryBean是ValidatorFactory的一个实现类。
c.直接使用注解
public class Student {
@Past//当前时间以前
private Date birthday ;
}
在校验的Controller中 ,给校验的对象前增加 @Valid
public String testDateTimeFormat(@Valid Student student, BindingResult result ,Map
{...}
10.Ajax请求SpringMVC,并且json格式的数据
a.jar
jackson-annotations-2.8.9.jar
jackson-core-2.8.9.jar
jackson-databind-2.8.9.jar
b。
@ResponseBod修饰的方法,会将该方法的返回值 以一个json数组的形式返回给前台
@ResponseBody//告诉SpringMVC,此时的返回 不是一个 View页面,而是一个 ajax调用的返回值(Json数组)
@RequestMapping(value="testJson")
public List testJson() {
//Controller-Service-dao
//StudentService studentService = new StudentServiceImp();
// List students = studentService.qeuryAllStudent();
//模拟调用service的查询操作
...
List students = new ArrayList<>();
students.add(stu1) ;
students.add(stu2) ;
students.add(stu3) ;
return students;
}
前台:服务端将返回值结果 以json数组的形式 传给了result。
$("#testJson").click(function(){
//通过ajax请求springmvc
$.post(
"handler/testJson",//服务器地址
//{"name":"zs","age":23}
function(result){//服务端处理完毕后的回调函数 List students, 加上@ResponseBody后, students实质是一个json数组的格式
for(var i=0;i
11.SpringMVC实现文件上传
和Servlet方式的本质一样,都是通过commons-fileupload.jar和commons-io.jar
SpringMVC可以简化文件上传的代码,但是必须满足条件:实现MultipartResolver接口 ;而该接口的实现类SpringMVC也已经提供了CommonsMultipartResolver
具体步骤:(直接使用CommonsMultipartResolver实现上传)
a.jar包
commons-fileupload.jar、commons-io.jar
b.配置CommonsMultipartResolver
将其加入SpringIOC容器
c.处理方法
//文件上传处理方法
@RequestMapping(value="testUpload") //abc.png
public String testUpload(@RequestParam("desc") String desc , @RequestParam("file") MultipartFile file ) throws IOException {
System.out.println("文件描述信息:"+desc);
//jsp中上传的文件:file
InputStream input = file.getInputStream() ;//IO
String fileName = file.getOriginalFilename() ;
OutputStream out = new FileOutputStream("d:\\"+fileName) ;
byte[] bs = new byte[1024];
int len = -1;
while(( len = input.read(bs)) !=-1 ) {
out.write(bs, 0, len);
}
out.close();
input.close();
//将file上传到服务器中的 某一个硬盘文件中
System.out.println("上传成功!");
return "success";
}
框架: 将原来自己写的1000行代码,变成:框架帮你写900行,剩下100行自己写
控制器:handler servlet controller action
12.拦截器
拦截器的原理和过滤器相同。
SpringMVC:要想实现拦截器,必须实现一个接口HandlerInterceptor
ctrl+shift+r :自己编写的代码.java .jsp .html
ctrl+shift+t :jar中的代码
a.编写拦截器implements HandlerInterceptor
package com.Interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
public class MyInterceptor implements HandlerInterceptor{
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// TODO Auto-generated method stub
System.out.println("拦截请求");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// TODO Auto-generated method stub
System.out.println("拦截响应");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
// TODO Auto-generated method stub
System.out.println("after渲染");
}
}
b.配置:将自己写的拦截器 配置到springmvc中(spring)
拦截器1拦截请求- 拦截器2拦截请求 - 请求方法 - 拦截器2处理相应-拦截器1处理相应- 只会被 最后一个拦截器的afterCompletion()拦截
如果有多个拦截器,则每个拦截器的preHandle postHandle 都会在相应时机各被触发一次;但是afterCompletion, 只会执行最后一个拦截器的该方法。
13.异常处理
SpringMVC: HandlerExceptionResolver接口,该接口的每个实现类 都是异常的一种处理方式:
a.
ExceptionHandlerExceptionResolver: 主要提供了@ExceptionHandler注解,并通过该注解处理异常
//该方法 可以捕获本类中 抛出的ArithmeticException异常
@ExceptionHandler({ArithmeticException.class,ArrayIndexOutOfBoundsException.class })
public String handlerArithmeticException(Exception e) {
System.out.println(e +"============");
return "error" ;
}
@ExceptionHandler标识的方法的参数 必须在异常类型(Throwable或其子类) ,不能包含其他类型的参数
异常处理路径:最短优先
如果有方法抛出一个ArithmeticException异常,而该类中 有2个对应的异常处理法你发:
@ExceptionHandler({Exception.class })
public ModelAndView handlerArithmeticException2(Exception e) {}
@ExceptionHandler({ArithmeticException.class })
public ModelAndView handlerArithmeticException1(Exception e) {}
则优先级: 最短优先。
@ExceptionHandler默认只能捕获 当前类中的异常方法。如果发生异常的方法 和处理异常的方法 不在同一个类中:@ControllerAdvice
总结:如果一个方法用于处理异常,并且只处理当前类中的异常:@ExceptionHandler. 如果一个方法用于处理异常,并且处理所有类中的异常: 类前加@ControllerAdvice、 处理异常的方法前加@ExceptionHandler
b.
ResponseStatusExceptionResolver:自定义异常显示页面 @ResponseStatus
@ResponseStatus(value=HttpStatus.FORBIDDEN,reason="数组越界222!!!")
public class MyArrayIndexOutofBoundsException extends Exception {//自定义异常
}
@ResponseStatus也可以标志在方法前:
@RequestMapping("testMyException")
public String testMyException(@RequestParam("i") Integer i) throws MyArrayIndexOutofBoundsException {
if(i == 3) {
throw new MyArrayIndexOutofBoundsException();//抛出异常
}
return "success" ;
}
@RequestMapping("testMyException2")
public String testMyException2(@RequestParam("i") Integer i) {
if(i == 3) {
return "redirect:testResponseStatus" ;//跳转到某一个 异常处理方法里
}
return "success" ;
}
c.异常处理的实现类:
DefaultHandlerExceptionResolver:SPringMVC在一些常见异常的基础上(300 500 405),新增了一些异常,例如:
- @see org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler
- @see #handleNoSuchRequestHandlingMethod
- @see #handleHttpRequestMethodNotSupported :如果springmvc的处理方法限制为post方式,如果实际请求为get,则会触发此异常显示的页面
- @see #handleHttpMediaTypeNotSupported
- @see #handleMissingServletRequestParameter
- @see #handleServletRequestBindingException
- @see #handleTypeMismatch
- @see #handleHttpMessageNotReadable
- @see #handleHttpMessageNotWritable
- @see #handleMethodArgumentNotValidException
- @see #handleMissingServletRequestParameter
- @see #handleMissingServletRequestPartException
- @see #handleBindException
d.
SimpleMappingExceptionResolver:通过配置来实现异常的处理
error
error