springmvc的配置
1.配置springmvc.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 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"> <!-- 配置spring创建容器时扫描的包 --> <context:component-scan base-package="cn.maoritian"></context:component-scan> <!-- 配置视图解析器,用于解析项目跳转到的文件的位置 --> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/pages/"></property> <property name="suffix" value=".jsp"></property> </bean> <!-- 配置spring开启注解mvc的支持 --> <mvc:annotation-driven></mvc:annotation-driven> </beans>
2.配置springmvc的核心核心控制器DispatcherServlet
<web-app> <!-- 配置核心控制器 --> <servlet> <servlet-name>dispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 使核心控制器初始化时读取bean.xml文件创建Spring核心容器 --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:bean.xml</param-value> </init-param> <!-- 设置该Servlet的优先级别未最高,使之最早创建 --> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
Springmvc的使用
@Controller public class HelloController { @RequestMapping(path="/hello") // 指定方法对应的URL public String helloHandler() { System.out.println("Hello SpringMVC!!"); return "success"; // 指定跳转的视图的地址,被ViewResolver解析为 /WEB-INF/pages/success.jsp } }
案例执行流程分析
案例的执行流程
- 启动Tomcat服务器时,由于配置了
<load-on-startup>
标签,所以首先创建DispatcherServlet
对象并加载bean.xml
配置文件 - 由于
bean.xml
中开启了注解扫描,HelloController
对象被创建并加入Spring容器中 - 浏览器请求
index.jsp
,请求会先到达DispatcherServlet
核心控制器,根据配置@RequestMapping
注解找到具体要执行的方法helloHandler
- 执行方法
helloHandler
,得到返回值. 的视图解析器解析返回值,查找到对应的JSP文件success.jsp
- Tomcat服务器渲染页面,做出响应
请求路径匹配
@RequestMappin注解用于建立请求url路径和处理器之间的对应关系
出现位置:可以出现在类上,也可以出现在方法上
当他出现在类上也出现杂在方法上是,类上注解值为请求URL的一级目录,方法上注解值为请求URL的二级目录
当它只出现在方法上时,该注解为请求URL的一级目录
其属性如下:
path:value属性的别名,指定请求的URL.
method:指定HTTP请求的方法
params:指定请求参数的限制,支持简单的表达式如:
@RequestMapping(params={“param1”}),表示请求参数中param1必须出现
@RequestMapping(params={‘’!param1'}),表示请求参数中param1不能出现
@RequestMapping(params={"param1=value1"}),表示请求参数中param1必须出现且为value1
@RequestMapping(params={"param1!value1"}),表示请求参数中param1必须出现且不为value1
@PathVariable注解的使用
<a href="account/findAccount/10">查询账户</a>
// 控制器类 @Controller @RequestMapping(path = "/account") public class HelloController { @RequestMapping("/findAccount/{id}") public void findAccount(@PathVariable(name = "id") Integer accountId) { // accountId = 10 // 方法体... } }
访问URLhttp://localhost:8080/myProject/account/findAccount/10
会将10
传给findAccount
方法的accountId
参数
请求参数的绑定
<a href="account/findAccount?accountId=10">查询账户</a>
// 控制器类 @Controller @RequestMapping(path = "/account") public class HelloController { @RequestMapping(path = "/findAccount") public void findAccount(Integer accountId) { // accountId = 10 // 方法体... } }
SpringMVC中会将10
传给findAccount
方法的accountID
参数传递给HandlerAdapter
执行.
@RequestParam注解:微处理器方法参数起别名
@RequestParam注解作用在方法参数上,把请求中指定名称的参数给处理器方法中的形参赋值
属性如下:
name:value属性的别名,指定请求参数的名称
required:指定该请求参数是否是必须的,默认为true
<a href="testRequestParam?param1=value">测试requestParam注解</a>
处理器方法中给对应参数加上@RequestParam(name="param1")
注解来接收参数
@RequestMapping("/testRequestParam") public String handlerMethod(@RequestParam("param1") String username) { // 方法体... }
各种类型请求参数的绑定
SpringMVC内置参数绑定类型
1.SpringMVC支持三种类型的参数绑定
2.JavaBean类型
3.集合类型
数据绑定要求请求参数明和方法中的参数明相同,或使用@RequestParam为方法参数起别名.
基本数据类型和String类型的参数绑定
对于基本数据类型,只需要以方法参数名作为请求参数名即可.示例如下:
<a href="account/findAccount?accountId=10&accountName=zhangsan">查询账户</a>
// 控制器类 @Controller @RequestMapping(path = "/account") public class HelloController { @RequestMapping("/findAccount") public String findAccount(Integer accountId, String accountName) { // accountId = 10, accountName = "zhangsan" // 方法体... } }
JavaBean类型的参数绑定
JavaBean类型的参数,要想实现绑定,就必须实现其空参构造函数和所有属性的get,set方法
1.若JavaBean参数的属性中只包含基本数据类型和String类型属性,以属性名作为请求参数名,则SpringMVC会自动将其封装成JavaBean对象.示例如下:
javaBean类的定义如下
// JavaBean类 public class Account implements Serializable { private String username; private Integer age; // 所有属性的getset方法... }
则其对应的请求参数名如下:
<form action="account/updateAccount" method="post"> <label>名称</label><input type="text" name="username"><br/> <label>年龄</label><input type="text" name="age"><br/> <input type="submit" value="保存"> </form>
2.若JavaBean参数的属性中包含其它JavaBean对象,则以外层类属性名.内层类属性名作为请求参数
public class Account implements Serializable { private String username; private Intger age; private User user; // 所有属性的getset方法... } public class User implements Serializable{ private String uname; private Double umoney; // 所有属性的getset方法... }
则其对应的请求参数名如下:
<form action="account/updateAccount" method="post"> <label>名称</label><input type="text" name="username"><br/> <label>年龄</label><input type="text" name="age"><br/> <label>用户名</label><input type="text" name="user.uname"><br/> <label>用户余额</label><input type="text" name="user.umoney"><br/> <input type="submit" value="保存"> </form>
自定义数据类型参数绑定
表单提交的任何数据类型都是字符串类型,SpringMVC定义了转换器,将字符串转化为我们方法参数的各种类型.我们也可以实现自定义的转换器以实现自定义的参数类型转换
自定义的类型转换器要实现Converter<String, T>
接口,并在Spring容器配置bean.xml
中配置该实现类. 示例如下:
// 自定义的类型转换器,完成从String类到Date类的转换 public class StringToDateConverter implements Converter<String, Date> { public Date convert(String source) { try { DateFormat df = new SimpleDateFormat("yyyy-MM-dd"); Date date = df.parse(source); return date; } catch (Exception e) { throw new RuntimeException("类型转换错误"); } } }
<!-- 配置的类型转换器工厂 --> <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"> <property name="converters"> <set> <!-- 诸如我们自定义的类型转换器 --> <bean class="cn.maoritian.utils.StringToDateConverter"/> </set> </property> </bean>
通过原始ServletAPI对象处理请求
SpringMVC支持使用原始ServletAPI作为控制器方法的参数,包括HttpServletRequest
,HttpServletResponse
,HttpSession
对象,他们都可以直接用做控制器方法的参数.示例如下:
@RequestMapping("/path") public void myHandler(HttpServletRequest request, HttpServletResponse response) throws IOException { System.out.println(request.getParameter("param1")); System.out.println(request.getParameter("param1")); response.getWriter().println("<h3>操作成功</h3>"); }
解决请求参数绑定中文乱码问题
在web.xml配置编码转换过滤器,即可解决请求参数中文乱码的问题.
<!-- 配置编码转换过滤器 --> <filter> <filter-name>characterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filterclass> <!-- 指定字符集 --> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <!-- 过滤所有请求 --> <filter-mapping> <filter-name>characterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
小知识点:/不会匹配到.jsp /*会匹配到.jsp
通过处理器方法返回值指定返回视图
SpringMVC中的处理器方法的返回值用来指定页面跳转到哪个视图,处理器的返回值可以为String
,void
,ModelAndView
对象.
处理器返回String对象:转发到字符串指定的URL
处理器方法返回字符串可以指定逻辑视图名,通过视图解析器解析为物理视图地址.
在本例中,因为我们在Spring容器配置文件bean.xml
中配置的视图解析器中注入prefix
和suffix
属性,所以视图解析器会把处理器返回的"字符串值"
解析为"/WEB-INF/pages/字符串值.jsp"
,再请求对应视图.这是一个请求转发过程,浏览器地址栏不会发生变化.
bean.xml中配置的视图解析器如下:
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/pages/"></property> <property name="suffix" value=".jsp"></property> </bean>
处理器方法如下:
@Controller @RequestMapping("/user") public class UserController { @RequestMapping("/testString") public String testString(Model model) { // 执行方法体...向隐式对象添加属性attribute_user,可以在jsp中通过 ${attribute_user} 获取到 model.addAttribute("attribute_user", new User("张三", "123")); // 经过视图解析器的处理,SpringMVC会将请求转发到/WEB-INF/pages/succeess.jsp,但浏览器地址栏显示的一直是 项目域名/user/testString return "success"; } }
处理器返回void:转发到当前URL
处理器返回ModelAndView对象:更灵活地添加属性和指定返回视图
ModelAndView为我们提供了一种更灵活地为页面添加属性和指定返回视图的方法,其主要方法如下:
1.public ModelMap getModelMap():返回当前页面的ModelMap对象.
2.public ModelAndView addObject(Object attributeValue)
: 向当前页面的ModelMap
对象中添加属性
3.public void setViewName(@Nullable String viewName)
: 指定返回视图,viewName
会先被视图解析器处理解析成对应视图.
@Controller @RequestMapping("/user") public class UserController { @RequestMapping("/testModelAndView") public ModelAndView testModelAndView() { // 创建ModelAndView对象 ModelAndView mv = new ModelAndView(); // 向model中存入属性attribute_user mv.addObject("attribute_user", new User("张三", "123")); // 指定返回视图,视图解析器将"success"解析为视图URL /WEB-INF/pages/succeess.jsp mv.setViewName("success"); return mv; } }
使用SpringMVC框架提供的请求转发
要使用SpringMVC框架提供的请求转发,只需要在处理器方法返回的viewName
字符串首加上forward:
即可,要注意的是,此时forward:
后的地址不能直接被视图解析器解析,因此要写完整的相对路径.示例如下:
@Controller @RequestMapping("/user") public class UserController { @RequestMapping("/testForward") public String testForward() { // 在forward:要写完整的相对路径 // return "forward:success" // 错误,会将请求转发到 /项目名/user/success return "forward:/WEB-INF/pages/success.jsp"; } }
使用SpringMVC框架提供的重定向
要使用SpringMVC框架提供的请求重定向,只需要在处理器方法返回的viewName
字符串首加上redirect:
即可,要注意的是,此时redirect:
后的地址要写相对于ContextPath
的地址.示例如下:
@Controller @RequestMapping("/user") public class UserController { @RequestMapping("/testRedirct") public String testRedirct() { // 在forward:要写完整的相对路径 // return "redirect:" + request.getContextPath() + "/index.jsp"; // 错误,会将请求转发到 /项目名/项目名/index.jsp return "redirect:/index.jsp"; } }
SpringMVC响应json数据
前期准备
1.jsp在页面上引入jQuery以发送json数据,因此需要向服务器发起一个对jQuery的请求.像这种对静态资源的请求,不应当经过具体的某个处理器处理,而应当直接返回对应的静态资源.
因此我们需要在Spring容器配置bean.xml
中使用<mvc:resources>
标签声明该资源为静态资源,否则请求该资源会报404
错误.该标签的属性如下:
1.location属性;表示该资源在服务器上所在的位置,必须是一个有效的目录
2.mapping属性:指定匹配的URL
我们在bean.xml中配置各静态文件的位置如下.
在jsp中编写代码发送json数据
在jsp页面中编写代码发送json请求如下:
<script> // 页面加载,绑定单击事件 $(function () { $("#btn").click(function () { // 发送ajax请求 $.ajax({ // 配置请求参数 url: "user/testAjax", contentType: "application/json;charset=UTF-8", dataType: "json", type: "post", // 请求的json数据 data: '{"username":"myname","password":"mypassowrd","age":30}', // 回调函数,处理服务器返回的数据returnData success: function (returnData) { // 我们假定服务器返回的是一个user对象,将其输出在控制台上 console.log(returnData); } }); }); }); </script>
在控制器中编写代码响应json数据
使用@RequestBody注解将请求绑定到控制器方法参数上,使用@ResponseBody注解表示将该方法的返回值直接写回到HTTP响应中,而不会存入Model解析为视图名
@RequestBody会将json对象封装为对应的javaBean对象
@Controller @RequestMapping("/user") public class UserController { @RequestMapping("/testAjax") @ResponseBody public User testAjax(@RequestBody User user) { System.out.println(user); // 将user对象返回给前端页面 return user; } }
异常处理器
当程序发生错误时,错误最终会传递给DispatcherServlet
,由DispatcherServlet
进行异常处理.
1.创建自定义异常类
package cn.maoritian.exception; public class SysException extends Exception { // 存储提示信息的 private String message; // 构造方法 public SysException(String message) {this.message = message; } // get,set方法 public String getMessage() {return message; } public void setMessage(String message) {this.message = message; } }
2.创建异常处理器,异常处理器必须实现HandlerExceptionResolver
接口,其resolveException()
方法执行异常处理.
package cn.maoritian.exception; // 自定义异常处理器 public class MyExceptionResolver implements HandlerExceptionResolver { public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { myException e = null; if (ex instanceof myException) { e = (myException) ex; } else { e = new myException("其他错误"); } ModelAndView mv = new ModelAndView(); mv.addObject("errorMsg", e.getMessage()); // 封装错误信息 mv.setViewName("error"); // 跳转页面 return mv; } }
3.向spring容器中注入异常处理器
<!--配置异常处理器--> <bean id="myExceptionResolver" class="cn.maoritian.exception.MyExceptionResolver"/>
拦截器
SpringMVC中的拦截器只能拦截controller中的方法.
1.自定义拦截器需要继承HandlerInterceptor接口,该接口定义了三个方法,都有其默认实现:
1.preHandle(...):该方法在处理器方法执行之前执行
2.postHandle(...):该方法在处理器方法实际执行完毕以后执行
3.afterCompletion(...):该方法在整个请求处理完成后执行
其中preHandle(..)
方法返回一个boolean
值,可以通过这个方法来决定是否继续执行处理链中的部件。当方法返回 true
时,处理器链会继续执行;若方法返回 false
, DispatcherServlet
即认为拦截器自身已经完成了对请求的处理(比如说,已经渲染了一个合适的视图),那么其余的拦截器以及执行链中的其他处理器就不会再被执行了。
public class MyIntercepter implements HandlerInterceptor{ @Override public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception { System.out.println("过滤器执行成功"); return true; }
2.向Spring容器中注入拦截器
<mvc:interceptors> <mvc:interceptor> <!-- 拦截的方法 --> <mvc:mapping path="/**" /> <!-- 具体的拦截器 --> <bean id="officeHoursInterceptor" class="cn.maoritian.interceptor.TimeBasedAccessInterceptor"> <property name="openingTime" value="9"/> <property name="closingTime" value="18"/> </bean> </mvc:interceptor> </mvc:interceptors>