Spring MVC
文章目录
一 什么是MVC
- Model : 模型层 封装数据和业务逻辑
- View : 视图层 用来收集数据和 显示数据
- Controller : 控制层 用来结合Model 和 View 的控制流程
传统的项目 MVC 组成 : javaBean + Servlet + html(jsp、css等)
二 Spring Web MVC
- Spring 提供了一个 Web MVC 框架,便于开发MVC结构 的 Java Web 程序
- Spring MVC 框架控制器为 DispatcherServlet,它负责接受请求,然后将请求分发到不同的处理器进行业务处理,最后由控制器完成转发动作
1、Spring Web MVC 的核心组件
- DispacherServlet 控制器层 程序的入口 (Web.xml里面配置)
- HandlerMapping 控制器层 请求派发
- Controller 控制器层 处理核心流程
- ModelAndView 模型和视图层 封装数据 和 视图信息
- ViewResolver 视图处理器层
2、Spring MVC 项目的建立
① 非注解形式SpringMVC建立
-
建立一个Web 项目
-
导入相关jar包
-
在 web.xml 中配置 DispacherServlet
<?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"> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <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>*.do</url-pattern> </servlet-mapping> </web-app>
-
在 Spring 配置文件(applicationContext.xml)中配置HandlerMapping 的实现类 SimpleUrlHandlerMapping
<bean id="handlerMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="mappings"> <props> <prop key="/hello.do">helloController</prop> </props> </property> </bean>
-
写一个类,实现Controller 接口,重写方法handleRequest,并返回会ModelAndView对象,之后在Spring 配置文件中配置控制器类
实现Controller 接口 类:package com.test.controller; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.Controller; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class HelloController implements Controller { @Override public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception { ModelAndView mav = new ModelAndView(); mav.setViewName("hello"); return mav; } }
配置控制器类:
<bean id="helloController" class="com.test.controller.HelloController"/>
-
在Spring 配置文件中配置 ViewResolver
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/"/> <property name="suffix" value=".jsp"/> </bean>
② 注解形式SpringMVC建立
- 建立一个Web 项目
- 导入相关jar包
- 在 web.xml 中配置 DispacherServlet
<?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"> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <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>*.do</url-pattern> </servlet-mapping> </web-app>
- 配置Spring 配置文件(applicationContext.xml)开启组件扫描, 开启标注模式的mvc(内置了更高级的HandlerMapping 并且要结合@RequestMapping(“地址”))使用
<!--开启组件扫描--> <context:component-scan base-package="com.test"/> <!--开启标注模式的MVC--> <mvc:annotation-driven />
- 写一个类,标注上@Controller注解(方法名,返回值,参数没有限制),类内部的方法要加上
package com.test.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class HelloController{ @RequestMapping("/hello.do") public String hello(){ return "hello"; } }
- 在Spring 配置文件中配置 ViewResolver
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/"/> <property name="suffix" value=".jsp"/> </bean>
3、控制器中参数的接受
① 直接使用HttpServletRequest
@RequestMapping("/register.do")
public String getHello(HttpServletRequest httpServletRequest){
String name = httpServletRequest.getParameter("name");
String password = httpServletRequest.getParameter("password");
String money = httpServletRequest.getParameter("money");
System.out.println(name+" "+password+" "+money);
return "register";
}
② 可以直接在,控制器方法上 写页面上对应的 参数名字,作为参数
参数接受代码:
@RequestMapping("/register2.do")
public String getHello(String name, String password, String money){
System.out.println(name+" "+password+" "+money);
return "register";
}
如果参数名无法保持一致,可以在参数前加上@RequestParam(“参数”)标注的形式,代码如下:
参数接受代码:
@RequestMapping("/register2.do")
public String getHello(String name, @RequestParam("password") String pwd, String money){
System.out.println(name+" "+pwd+" "+money);
return "register";
}
③ 使用对象 直接接受参数
要求对象中的属性 和 页面参数必须一一对应!!!
参数接受代码:
@RequestMapping("/register4.do")
public String getHello3(BankAccount bankAccount){
System.out.println(bankAccount);
return "register";
}
bean 实体类代码:
package com.test.bean;
public class BankAccount {
private int id;
private String name;
private String password;
private double money;
public BankAccount() {
}
public BankAccount(int id, String name, String password, double money) {
this.id = id;
this.name = name;
this.password = password;
this.money = money;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
@Override
public String toString() {
return "BankAccount{" +
"id=" + id +
", name='" + name + '\'' +
", password='" + password + '\'' +
", money=" + money +
'}';
}
前端代码:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>银行开户</h3>
<form action="${pageContext.request.contextPath}/register4.do">
名字:
<input type="text" name="name"> <br>
密码:
<input type="password" name="password"> <br>
钱数:
<input type="text" name="money"> <br>
<input type="submit" value="开户">
</form>
</body>
</html>
4、控制器向页面传递参数
① 直接使用HttpServletRequest
@RequestMapping("/register1.do")
public String Register2(String name, String password, String money, HttpServletRequest request) {
request.setAttribute("name", name);
request.setAttribute("password", password);
return "login";
②使用@ModelAttribute 可以 对控制器的参数进行传递
@ModelAttribute(“account”) 相当于request.setAttribute(“account”, account);
@RequestMapping("/register2.do")
public String Register3(@ModelAttribute("account")BankAccount bankAccount) {
System.out.println(bankAccount);
return "login";
}
③使用ModelAndView 控制器返回 ModelAndView
@RequestMapping("/register4.do")
public ModelAndView Register4(BankAccount bankAcceount) {
System.out.println(bankAccount);
ModelAndView mav = new ModelAndView();
mav.setViewName("login");
mav.getModel().put("account", bankAccount);
return mav;
}
④使用ModelMap 对象(控制器参数是ModelMap)
@RequestMapping("/register5.do")
public String Register5(BankAccount bankAccount, ModelMap modelMap) {
System.out.println(bankAccount);
modelMap.addAttribute("account", bankAccount);
// modelMap.put("account", bankAccount);
return "login";
}
⑤使用Model 类型的参数 传递数据
@RequestMapping("/register6.do")
public String Register6(BankAccount bankAccount, Model model) {
System.out.println(bankAccount);
model.addAttribute("account", bankAccount);
return "login";
}
5、转发和重定向
① 通过request 获取
② 在控制器中,直接使用HttpSession 获取
6、转发和重定向
①转发
默认就是转发 控制器无论返回的是一个字符串,还是一个ModelAndView,return “login”; 转发到/WEB-INF/login.jsp,默认路径是项目
② 重定向
-
使用 return “redirect:login”; 发起重定向 获取到的是相对servlet 路径,就是使用绝对路径“/”
@RequestMapping("/register7.do") public String Register7(BankAccount bankAccount, HttpSession session, HttpServletRequest request) { System.out.println(bankAccount+" register7.do"); request.setAttribute("account", bankAccount); session.setAttribute("account", bankAccount); return "redirect:/toRegister.do"; }
-
使用RedirectView 来完成重定向
@RequestMapping("/register8.do") public ModelAndView Register8(BankAccount bankAccount, HttpSession session, HttpServletRequest request) { System.out.println(bankAccount+" register8.do"); request.setAttribute("account", bankAccount); session.setAttribute("account", bankAccount); ModelAndView mav = new ModelAndView(); // 获取项目路径 String contextPath = session.getServletContext().getContextPath(); // 使用RedirectView 必须手动加上项目路径 RedirectView redirectView = new RedirectView(contextPath + "/register6.do"); mav.setView(redirectView); return mav; }
二者区别:
- 在效率上,转发(forward)>重定向(redirect)
- 显示上,重定向显示新的URL,转发的地址不变
- 数据上,转发共享数据,重定向不行
- 请求次数,转发1次,重定向2次
7、拦截器
- 作用 :
可以在 HandlerMapping 组件进行拦截 可以实现权限认证 登录检查等 - 实现:
实现 HandlerInterceptor 接口- preHandle 控制器点用之前
- postHandle 控制器调用之后, 视图处理之前
- asterHandle 视图处理之前,相应之后
代码实现:
配置代码:public class UserActionInterceptor implements HandlerInterceptor { @Override public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3) throws Exception { System.out.println("user action afterCompletion ...."); } @Override public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3) throws Exception { System.out.println("user action postHandle ...."); } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object method) throws Exception { System.out.println("user action preHandle ....:"); // 判定用户是否登录 登录就放行 不登录返回到登录页面 Object name = request.getSession().getAttribute("name"); if(name == null){ response.sendRedirect("/spring-mvc-day06/user/toLogin.do"); return false; }else{ return true; } } }
<!-- 配置拦截器 --> <mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/user/*" /> <!-- 不拦截的 --> <mvc:exclude-mapping path="/user/toLogin.do"/> <mvc:exclude-mapping path="/user/login.do"/> <bean class="com.test.interceptor.UserActionInterceptor"></bean> </mvc:interceptor> </mvc:interceptors>
8、多拦截器
- preHandle 是按照配置的顺序 顺序执行
- postHandle asterHandle 和配置的顺序相反
9、异常处理
-
处理方式1:返回自定义异常名称,处理全部Controller 的异常
使用 Spring 提供的SimpleMappingExceptionResolve
配置<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <property name="exceptionMappings" > <props> <prop key="java.lang.Exception">自定义异常名称1</prop> <prop key="java.lang.NullPointerException">自定义异常名称2</prop> </props> </property> </bean>
-
处理方式2:自定义异常处理器,处理全部Controller 的异常
配置<bean class="com.xdl.exception.reslover.MyExceptionResolver"> </bean>
实现
@Controller public class MyExceptionResolver implements HandlerExceptionResolver { @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object method, Exception e) { ModelAndView mav = new ModelAndView(); if(e instanceof NullPointerException){ mav.setViewName("error2"); }else if(e instanceof Exception){ mav.setViewName("error1"); } return mav; } }
-
处理方式3 :使用@ExceptionHandler 注解实现异常处理,处理指定Controller 的异常
实现@ExceptionHandler public String execute(HttpServletRequest request,Exception e){ if(e instanceof RuntimeException){ return "error3"; }else { return "error4"; } }