第一个SpringMVC程序
配置文件
springmvc.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"
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">
<!-- 配置扫描器 -->
<context:component-scan base-package="cn.test.handler"/>
<!-- 配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/views/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
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">
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
Handler.java
@Controller
public class Handler {
@RequestMapping("welcome")
public String Welcome(){
return "success";
}
}
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<a href="${pageContext.request.contextPath}/welcome">I'm welcome</a><br>
</body>
</html>
success.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>I'm success.jsp</h1>
</body>
</html>
HiddenHttpMethodFilter过滤器
给 普通浏览器 增加 put|delete请求方式
HiddenHttpMethodFilter过滤器拦截请求的要求:
1.<input type=“hidden” name="_method" value=“delete/put”/>
2.请求方式为post
传值方式
url传值@PathVariable
//通过url传值
@RequestMapping("welcome2/{name}")
public String Welcome2(@PathVariable("name") String name){
System.out.println(name);
return "success";
}
普通表单传值@RequestParam(等价于request.getParameter())
//普通表单传值(可接受多个参数)
@RequestMapping("testDelete")
public String TestDelete(@RequestParam("uname") String name, @RequestParam(value = "uid", required = false, defaultValue = "18") int id){
System.out.println("删: "+id+" "+name);
return "success";
}
实体类对象自动传值(表单中的参数名要和实体类的属性名相同)
Handler.java
@RequestMapping("testObjectProperties")
public String TestObjectProperties(Student student){
System.out.println(student);
return "success";
}
index.java
<form action="${pageContext.request.contextPath}/testObjectProperties">
sid:<input type="text" name="sid"><br>
sname:<input type="text" name="sname"><br>
homeAddress:<input type="text" name="address.homeAddress"><br>
schoolAddress:<input type="text" name="address.schoolAddress">
<input type="submit" value="提交">
</form>
Student.java
public class Student {
private int sid;
private String sname;
private Address address;//Address包含homeAddress、schoolAddress两属性
getter、setter方法省略
}
处理模型数据(跳转时携带数据)
将数据放在request 域
Handler.java
//ModelAndView既有数据又有视图
@RequestMapping("testModelAndView")
public ModelAndView TestModelAndView(){
ModelAndView mv = new ModelAndView("success"); //会自动加上前后缀
Student stu = new Student();
stu.setSid(1);
stu.setSname("zs");
mv.addObject("student", stu);//相当于request.setAttribute("student", stu);
return mv;
}
//传入模型返回视图
@RequestMapping("testModelAndView2")
public String TestModelAndView2(ModelMap model){
Student stu = new Student();
stu.setSid(1);
stu.setSname("zs");
model.put("student2", stu);//相当于request.setAttribute("student", stu);
return "success";
}
jsp页面获取值
${requestScope.student.sid}
${requestScope.student.sname}
将数据放在 session 域
Handler.java
@SessionAttributes(value = {"student", "student2"})
或者
@SessionAttributes(types = Student.class)
jap获取值
${sessionScope.student.sid}
${sessionScope.student.sname}
数值更新 ModelAttribute //先查询再修改
//模拟查询过程
@ModelAttribute//慎用! 任意一次请求执行前,都会执行@ModelAttribute修饰的方法
//@ModelAttribute 在请求该类的各个方法前均被执行的设计给予一个思想:一个控制器只做一个功能
public void queryStudent(Map<String, Object> map){
Student student = new Student();
student.setSid(1);
student.setSname("zs");
map.put("student", student);//约定:map中key是下面方法参数类型的首字母小写
student-Student
} //不过不一致也可以map.put("stu", student) 下面方法参数@ModelAttribute("stu")
@RequestMapping("testModelAttribute")
public String TestModelAttribute(Student stu){
stu.setSname(stu.getSname());
System.out.println(stu);
return "success";
}
视图、视图解析器
视图的顶级接口:View
视图解析器:ViewResolver
常见的视图和解析器:
InternalResourceView、InternalResourceViewResolver
SpringMVC解析jsp时,会默认使用InternalResourceView,但如果发现Jsp中包含了jstl语言,会自动转为JstlView,JstlView可以解析jstl 实现国际化操作(国际化:针对不同地区、不同国家,进行不同的显示)
public class JstlView extends InternalResourceView(){}
实现国际化具体步骤:
1.创建资源文件
基名_语言_地区.properties or 基名_语言.properties
i18n_en.US.properties
resource.welcome=WELCOME
resource.exit=EXIT
i18n_en.US.properties
resource.welcome=欢迎
resource.exit=退出
2.配置springmvc.jsp文件
<!-- 加载国际化资源文件(id名必须为messageSource) -->
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="i18n"/>
</bean>
3.通过jstl使用国际化
导入jstl.jar、standard.jar包
InternalResourceViewResolver其他功能
1.实现不通过Controller直接index.jsp→success.jsp
前面index.jsp→Controller(@RequestMapping)→success.jsp
springmvc.xml
<mvc:view-controller path="testMvcViewController" view-name="success"/>
但是该配置会让所有请求转入该配置中的映射地址,而忽略掉@RequestMapping(),若要两者共存,则需
<mvc:annotation-driven/>
2.指定请求方式
SpringMVC默认的跳转方式为请求转发,若需改为重定向则需:
@RequestMapping("testRedirect")
public String TestRedirect(){
return "redirect:/views/success.jsp";
//注意 此方式不会被视图解析器加上前缀和后缀,需手动添加
}
3.处理静态资源
静态资源:html、css、js、图片、视频等等
动态资源:可以与用户交互、因为时间、地点的不同而结果不同的内容,如百度天气不同地方的结果不同
在SpringMVC中若直接访问静态资源:404,原因:之前将所有的请求通过通配符“/”拦截,进而交给SpringMVC的入口DispatcherServlet去处理:去找请求映射对应的@RequestMapping或配置文件中的<mvc:path…/>
解决方法:
若请求不需要SpringMVC处理,则使用tomcat默认的servlet去处理:若有对应的拦截请求,则交给对应的Servlet去处理,如果没有默认的servlet,则直接访问。tomcat默认servlet位置:cong\web.xml
<!-- 让SpringMVC在接到没有对应@RequestMapping的请求时时, 将请求交给服务器默认的servlet去处理 -->
<mvc:annotation-driven/>
<mvc:default-servlet-handler/>
<mvc:resources mapping="/img/**" location="/img/"/>//图片路径:img\joey.jpg
4.类型转换
a.SpringMVC内置了一些常见的类型转换器
b.自定义类型转换器(下面以String→Student转换为例)
i.编写自定义类型转换器的类(实现Converter接口)
public class MyConverter implements Converter<String, Student> {
@Override
public Student convert(String source) {
//source: 2-zs
String[] arr = source.split("-");
Student stu = new Student();
stu.setSid(Integer.parseInt(arr[0]));
stu.setSname(arr[1]);
return stu;
}
}
ii.配置自定义转换器(三大步)
<!-- 1.将自定义转换器纳入SpringIOC容器 -->
<bean id="myConverter" class="cn.test.converter.MyConverter"/>
<!-- 2.将自定义转换器myConverter纳入SpringMVC提供的转换器Bean中 -->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set><ref bean="myConverter"/> </set>
</property>
</bean>
<!-- 3.将conversionService注册到annotation-driven中 -->
<mvc:annotation-driven conversion-service="conversionService"/>
iii.测试
Handler.java
@RequestMapping("testConverter")
public String TestConverter(@RequestParam("studentInfo") Student student){
System.out.println(student);
return "success";
}
index.jsp
<form action="${pageContext.request.contextPath}/testConverter" method="post">
学生信息:<input name="studentInfo" type="text">
<input type="submit" value="转换">
</form>
5.数据格式化
以前:SimpleDateFormat sdf = new SimpleDateFormat (“yyyy-MM-dd hh:mm:ss”);
SpringMVC提供了很多注解,方便实现数据格式化
日期格式化实现步骤
a.在springmvc.xml中配置
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionService"/>
b.在Student.java中对属性增加注解
//格式化前端传来的数据
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date birthDay;
c.Handler.java
@RequestMapping("testDateFormat")
public String TestDateFormat(Student student, BindingResult result){
//约定 第二个参数BindingResult可直接异常报告,相当于try/catch
if(result.getErrorCount()>0){
for (ObjectError error: result.getAllErrors()){
System.out.println(error);
}
}
System.out.println(student);
System.out.println(student.getBirthDay());
return "success";
}
d.index.jsp
<form action="${pageContext.request.contextPath}/testDateFormat" method="post">
sid:<input type="text" name="sid"><br>
sname:<input type="text" name="sname"><br>
birthDay:<input type="text" name="birthDay"><br>
<input type="submit" value="提交">
</form>
数字格式化(其他与日期格式化相同,除了在Student.java页面增加的注解不一样):
@NumberFormat(pattern = "###,#")
private int sid;
其中上面配置springmvc.xml用到的FormattingConversionServiceFactoryBean既可以实现格式化、也可以实现类型转下,以后实现类型转换可以直接在FormattingConversionServiceFactoryBean里面写
前端展示错误信息(将错误消息放入request域)
Handler.java
@RequestMapping("testDateFormat")
public String TestDateFormat(Student student, BindingResult result, Map<String, Object> map){
//约定 第二个参数BindingResult可直接异常报告,相当于try/catch
//参数位置不可随便更换,因为这是框架规定好的
if(result.getErrorCount()>0){
for (ObjectError error: result.getFieldErrors()){
System.out.println(error.getDefaultMessage());
map.put("errors", error.getDefaultMessage());
}
}
System.out.println(student);
System.out.println(student.getBirthDay());
return "success";
}
success.jsp
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<c:forEach items="${errors}" var="error">
${error}
</c:forEach>
数据检验
JSP303、Hibernate Validator
使用Hibernate Validator步骤:
1.导入jar包(注意jar包之间的版本兼容问题)
hibernate validator.jar
classmate.jar
jboss-logging.jar
validation-api.jar
hibernate-validator-annotation-processor.jar
2.配置springmvc.xml
要实现JSP303、Hibernate Validator或其他校验,必须实现SpringMVC提供的一个接口:ValidatorFactory
而LocalValidatorFactoryBean是ValidatorFactory的一个实现类,<mvc:annotation-driven/>会自动加载LocalValidatorFactoryBean类,因此可以直接实现数据校验。
<mvc:annotation-driven/>
3.使用
Student.java
@Past//需要一个过去的时间
@DateTimeFormat(pattern = "yyyy-MM-dd")//前端输入日期需符合该格式
private Date birthDay;
Handler.java(在被检验的参数Student前加@Valid)
@RequestMapping("testDateFormat")
public String TestDateFormat(@Valid Student student, BindingResult result, Map<String, Object> map){
//约定 第二个参数BindingResult可直接异常报告,相当于try/catch
System.out.println(student);
System.out.println(student.getBirthDay());
if(result.getErrorCount()>0){
for (ObjectError error: result.getFieldErrors()){
System.out.println(error.getDefaultMessage());
map.put("errors", error.getDefaultMessage());
}
}
return "success";
}
Ajax请求SpringMVC,并且返回JSON数据
1.导入jar包(注意版本要一致)
jackson-annotations.jar
jackson-core.jar
jackson-databind.jar
2.用@ResponseBody修饰方法
@ResponseBody修饰的方法,会将该方法的返回值以一个json数组的形式返回给前端
@ResponseBody
@RequestMapping("testJson")
public List<Student> testJson(){
//Controller -> Service -> Dao
//但是现在省略这些步骤,假装已经直接拿到了对象数据
Student stu1 = new Student(1, "zs");
Student stu2 = new Student(2, "ls");
Student stu3 = new Student(3, "ww");
List<Student> students = new ArrayList<>();
students.add(stu1);
students.add(stu2);
students.add(stu3);
return students;
}
3.前端将返回值结果以json数组的形式传给了result
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Index</title>
<%-- 引入jquery --%>
<script type="text/javascript" src="js/jquery-3.2.1.js"></script>
<script type="text/javascript">
$(document).ready(function () {
$("#testJson").click(function () {
//通过ajax请求springmvc
$.post(
"testJson",//服务器地址
// {
// "sid" : 5,
// "sname" : "zs",
// },
function (result) { //服务端处理完毕后的回调函数
for(var i=0; i<result.length; i++){
alert(result[i].sid+"-"+result[i].sname);
}
}
)
})
})
</script>
</head>
<body>
<%--按钮--%>
<input type="button" value="testJson" id="testJson">
</body>
SpringMVC实现文件上传
和Servlet本质一样,都是通过commons-fileupload.jar和commons-io.jar
SpringMVC可以简化文件上传的代码,但是必须满足条件:实现MultipartResolver接口,SpringMVC也提供了该类的实现类CommonsMultipartResolver
实现步骤:
1.导入jar包(commons-fileupload.jar、commons-io.jar)
2.配置,将CommonsMultipartResolver加入SpringIOC容器中(可直接在springmvc.xml中配置)
<!-- 配置CommonsMultipartResolver,用于实现文件上传,将其加入SpringIOC容器,id值唯一,不能改变 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 文件编码 -->
<property name="defaultEncoding" value="UTF-8"/>
<!-- 上传单个文件的最大值, 单位Byte, 如果值为-1, 表示无限制-->
<property name="maxUploadSize" value="102400"/>
</bean>
3.处理方法
Handler.java
@RequestMapping("testUpload")
public String testUpload(@RequestParam("describe") String describe,@RequestParam("file") MultipartFile file) throws IOException {
System.out.println(describe);
//jsp中上传的文件:file
InputStream input = file.getInputStream();
String fileName = file.getOriginalFilename();
//将file上传到服务器中的某一个硬盘文件中
OutputStream output = new FileOutputStream("C:\\Users\\yaoweungyu\\Desktop\\"+fileName);
byte[] bs = new byte[1024];
int len = -1;
while ((len = input.read(bs)) != -1){
output.write(bs, 0 ,len);
}
output.close();
input.close();
System.out.println("上传成功");
return "success";
}
index.jsp
<form action="${pageContext.request.contextPath}/testUpload" method="post" enctype="multipart/form-data">
<input type="file" name="file">
文件描述信息:<input type="text" name="describe">
<input type="submit" value="上传">
</form>
拦截器
拦截器的原理和过滤器相同。
在SpringMVC要想实现拦截器,需要实现一接口HandlerInterceptor
实现步骤:
1.编写拦截器implements HandlerInterceptor
2.配置:将自己写的拦截器配置到springmvc.xml中(spring)
<!-- 配置拦截器 默认拦截全部请求 -->
<mvc:interceptors>
<!-- 第一个拦截器 -->
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/testObjectProperties"/>
<bean class="cn.test.interceptor.MyInterceptor"/>
</mvc:interceptor>
<!-- 第二个拦截器 -->
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/testObjectProperties"/>
<bean class="cn.test.interceptor.MySecondInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
3.测试
Handler.java
@RequestMapping("testInterceptor")
public String testInterceptor(){
System.out.println("----我是请求处理方法----");
return "success";
}
控制台输出
第一个拦截器 拦截请求
第二个拦截器 拦截请求
----我是请求处理方法----
第二个拦截器 拦截响应
第一个拦截器 拦截响应
第二个拦截器 视图渲染完毕
第一个拦截器 视图被渲染完毕
异常处理
HandlerExceptionResolver接口
HandlerExceptionResolver接口的每个实现类都是处理异常的一种方式
如:
1.ExceptionHandlerExceptionResolver类:
主要提供了注解@ExceptionHandler,并通过该注解处理异常
//该方法可以自动捕获 本类中 抛出的以下参数中的类型的异常(处理路径:最短优先)
@ExceptionHandler({ArithmeticException.class, ArrayIndexOutOfBoundsException.class})
public ModelAndView testArithmeticException(Exception e){
ModelAndView mv = new ModelAndView("error");
System.out.println(e);
mv.addObject("error", e);
return mv;
}
处理 不在同一个类中 的异常,需给处理异常的类加@ControllerAdvice,并实现HandlerExceptionResolver接口
@ControllerAdvice
public class MyExceptionHandler implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
ModelAndView mv = new ModelAndView("error");
mv.addObject("error", ex);
System.out.println(ex);
return mv;
}
}
2.ResponseStatusExceptionResolver类:
提供注解@ResponseStatus,实现自定义异常显示页面
在类前加注解@ResponseStatus实现自定义异常显示页面还需要继承Exception类
@ResponseStatus(value = HttpStatus.FORBIDDEN, reason = "你的数组越界了啦!~")
public class MyArrayIndexOutOfBoundsException extends Exception{
}
也可以在方法前加注解@ResponseStatus实现自定义异常显示页面
@RequestMapping("testMyArrayIndexOutOfBoundsException")
public String testMyArrayIndexOutOfBoundsException(@RequestParam("i") int i){
if(i==3){
return "redirect:MyArrayIndexOutOfBoundsException";
}
return "success";
}
@ResponseStatus(value = HttpStatus.FORBIDDEN, reason = "你的数组越界了啦!~")
@RequestMapping("MyArrayIndexOutOfBoundsException")
public void MyArrayIndexOutOfBoundsException(){
}
3.DefaultHandlerExceptionResolver类:
SpingMVC在一些常见的异常基础上(300、500、404),新增了一些异常,例如:
* @see #handleNoSuchRequestHandlingMethod
* @see #handleHttpRequestMethodNotSupported
* @see #handleHttpMediaTypeNotSupported
* @see #handleMissingServletRequestParameter
* @see #handleServletRequestBindingException
* @see #handleTypeMismatch
* @see #handleHttpMessageNotReadable
* @see #handleHttpMessageNotWritable
* @see #handleMethodArgumentNotValidException
* @see #handleMissingServletRequestParameter
* @see #handleMissingServletRequestPartException
* @see #handleBindException
4.SimpleMappingExceptionResolver类:
通过配置实现异常的处理
<!-- 通过配置处理异常 -->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<!-- 如果发生异常,异常对象会被保存在exceptionAttribute的value值中,默认值为exception -->
<property name="exceptionAttribute" value="ex"/>
<property name="exceptionMappings">
<props>
<!-- 相当于catch(ArithmeticException e)然后跳转到error页面 -->
<prop key="java.lang.ArithmeticException">
error
</prop>
</props>
</property>
</bean>