1、MVC 框架作用:
- 将 url 映射到 java 类或 java 类的方法
- 封装用户提交的数据
- 处理请求-调用相关的业务处理-封装响应数据
- 将响应的数据进行渲染 jsp,html,freemaker 等
2、Spring MVC 是一个轻量级的基于响应的 MVC 框架
3、Spring MVC 优势
- 性能比 Struts2 好
- 简单,便捷,易学
- 和 Spring 无缝集成(使用 spring ioc, aop)
- 使用约定优先于配置
- 能够进行简单 junit 测试
- 支持 Restful 风格
- 异常处理
- 本地化,国际化
- 数据验证,类型转换
- 拦截器
- 使用的公司多
4、结构
spring-beans spring-context spring-core spring-web spring-webmvc commons-logging
项目目录
.
├── pom.xml
└── src
├── main
│ ├── java
│ │ └── com
│ │ └── pengshiyu
│ │ └── controller
│ │ └── HelloController.java
│ ├── resources
│ │ └── springmvc-servlet.xml
│ └── webapp
│ ├── WEB-INF
│ │ ├── jsp
│ │ │ └── hello.jsp
│ │ └── web.xml
│ └── index.html
└── test
└── java
配置文件
pom.xml
4.0.0
org.example
spring-mvc-demo
1.0-SNAPSHOT
org.apache.tomcat.maven
tomcat7-maven-plugin
2.2
8080
/
UTF-8
true
src/main/webapp/WEB-INF/web.xml
true
org.springframework
spring-core
5.2.6.RELEASE
org.springframework
spring-beans
5.2.6.RELEASE
org.springframework
spring-context
5.2.6.RELEASE
org.springframework
spring-web
5.2.6.RELEASE
org.springframework
spring-webmvc
5.2.6.RELEASE
org.springframework
spring-aop
5.2.6.RELEASE
org.springframework
spring-expression
5.2.6.RELEASE
commons-logging
commons-logging
1.2
javax.servlet
javax.servlet-api
4.0.1
provided
jstl
jstl
1.2
src/main/webapp/WEB-INF/web.xml
springmvc
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
classpath:springmvc-servlet.xml
1
springmvc
/
src/main/resources/springmvc-servlet.xml
控制器
src/main/java/com/pengshiyu/controller/HelloController.java
package com.pengshiyu.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 {
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
// 封装要显示的数据
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("name", "Tom");
modelAndView.addObject("age", 25);
// 视图名
modelAndView.setViewName("hello");
return modelAndView;
}
}
视图文件
src/main/webapp/WEB-INF/jsp/hello.jsp
${name}
${age}
访问测试
GET http://localhost:8080/hello
Tom 25
Spring-MVC 注解开发
修改配置文件
src/main/resources/springmvc-servlet.xml
修改 Controller
package com.pengshiyu.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Controller
public class HelloController {
@RequestMapping("/hello")
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
// 封装要显示的数据
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("name", "Tom");
modelAndView.addObject("age", 24);
// 视图名
modelAndView.setViewName("hello");
return modelAndView;
}
}
配置总结
1、通过 url 对应 bean
适用于小型的应用系统
2、为 url 分配 bean
helloController
3、注解
Controller 配置对应的注解
结果跳转方式1、返回 ModelAndView 对象
根据 view 的名称和视图解析器,会跳转到指定的页面
页面:视图解析器的前缀+ view name + 视图解析器的后缀
springmvc-servlet.xml
package com.pengshiyu.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Controller
public class HelloController {
@RequestMapping("/hello")
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
throws Exception {
// 封装要显示的数据
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("name", "Tom");
// 相当于:
// request.setAttribute("name", "Tom");
modelAndView.setViewName("hello");
// WEB-INF/jsp/hello.jsp
// 类似转发
return modelAndView;
}
}
2、返回字符串
不需要视图解析器
springmvc-servlet.xml
package com.pengshiyu.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Controller
public class HelloController {
@RequestMapping("/hello")
public void handleRequest(HttpServletRequest request, HttpServletResponse response)
throws Exception {
response.getWriter().println("hello");
}
}
3、重定向(地址改变)
package com.pengshiyu.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Controller
public class HelloController {
@RequestMapping("/hello")
public void handleRequest(HttpServletRequest request, HttpServletResponse response)
throws Exception {
response.sendRedirect("index.jsp");
}
}
或者
package com.pengshiyu.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Controller
public class HelloController {
@RequestMapping("/hello")
public String handleRequest(HttpServletRequest request, HttpServletResponse response)
throws Exception {
// 不需要视图解析
return "redirect:index.jsp";
}
}
4、转发(地址不变)
package com.pengshiyu.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Controller
public class HelloController {
@RequestMapping("/hello")
public void handleRequest(HttpServletRequest request, HttpServletResponse response)
throws Exception {
request.setAttribute("name", "Tom");
request.getRequestDispatcher("index.jsp").forward(request, response);
}
}
或者
package com.pengshiyu.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Controller
public class HelloController {
@RequestMapping("/hello")
public String handleRequest(HttpServletRequest request, HttpServletResponse response)
throws Exception {
return "hello";
}
}
或者
package com.pengshiyu.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Controller
public class HelloController {
@RequestMapping("/hello")
public String handleRequest(HttpServletRequest request, HttpServletResponse response)
throws Exception {
return "forward:index.jsp";
}
}
数据处理
1、提交数据
package com.pengshiyu.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HelloController {
@RequestMapping("/hello")
public String handleRequest(String name){
System.out.println(name);
return "index.jsp";
}
}
GET http://localhost:8080/hello
null
GET http://localhost:8080/hello?name=Tom
Tom
2、指定参数名
package com.pengshiyu.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
public class HelloController {
@RequestMapping("/hello")
public String handleRequest(@RequestParam("uname") String name){
System.out.println(name);
return "index.jsp";
}
}
GET http://localhost:8080/hello?uname=Tom
Tom
3、提交对象
实体类
package com.pengshiyu.bean;
public class User {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
package com.pengshiyu.controller;
import com.pengshiyu.bean.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HelloController {
@RequestMapping("/user")
public String user(User user){
System.out.println(user);
return "index.jsp";
}
}
GET http://localhost:8080/user?name=Tom&age=23
User{name='Tom', age=23}
将数据显示到 UI 层
1、通过 ModelAndView 需要视图解析器
package com.pengshiyu.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Controller
public class HelloController {
@RequestMapping("/hello")
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
throws Exception {
// 封装要显示的数据
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("name", "Tom");
// 相当于:
// request.setAttribute("name", "Tom");
modelAndView.setViewName("hello");
// WEB-INF/jsp/hello.jsp
// 类似转发
return modelAndView;
}
}
2、通过 ModelMap 不需要视图解析器
package com.pengshiyu.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HelloController {
@RequestMapping("/user")
public String user(String name, ModelMap modelMap){
System.out.println(name);
modelMap.addAttribute("name", name);
return "index.jsp";
}
}
区别:ModelAndView ModelMap
相同点:
- 都可以将数据封装显示到表示层页面
不同点:
- ModelAndView 可以指定跳转的视图,而 ModelMap 不能
- ModelAndView 需要视图解析器,而 ModelMap 不需要配置
通过过滤器解决 web.xml
CharacterEncodingFilter
org.springframework.web.filter.CharacterEncodingFilter
encoding
utf-8
CharacterEncodingFilter
/*
2、Restful
package com.pengshiyu.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HelloController {
@RequestMapping("/user/{id}")
public String user(@PathVariable int id, ModelMap modelMap){
System.out.println(id);
modelMap.addAttribute("name", id);
return "/index.jsp";
}
}
GET http://localhost:8080/user/123
123
3、通过参数访问处理方法
package com.pengshiyu.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping("/user")
public class HelloController {
// /user?method=add
@RequestMapping(params = {"method=add"}, method = RequestMethod.GET)
public String add(ModelMap modelMap){
modelMap.addAttribute("name", "add");
return "/index.jsp";
}
// /user?method=delete
@RequestMapping(params = {"method=delete"}, method = RequestMethod.GET)
public String delete(ModelMap modelMap){
modelMap.addAttribute("name", "delete");
return "/index.jsp";
}
}
文件上传
依赖 commons-io commons-fileupload
pom.xml
commons-io
commons-io
2.7
commons-fileupload
commons-fileupload
1.4
配置文件
src/main/resources/springmvc-servlet.xml
src/main/java/com/pengshiyu/controller/HelloController.java
package com.pengshiyu.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
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 HelloController {
@RequestMapping(value = "/upload", method = RequestMethod.GET)
public String upload(){
return "/template/upload.jsp";
}
@RequestMapping(value = "/upload", method = RequestMethod.POST)
public String upload(@RequestParam("file") CommonsMultipartFile file, HttpServletRequest request) throws IOException {
String path = request.getRealPath("/uploads");
InputStream is = file.getInputStream();
System.out.println(file.getOriginalFilename());
OutputStream os = new FileOutputStream(new File(path, file.getOriginalFilename()));
int len = 0;
byte[] buffer = new byte[400];
while ((len = is.read(buffer)) != -1){
os.write(buffer, 0, len);
}
is.close();;
os.close();
return "/index.jsp";
}
}
src/main/webapp/template/upload.jsp
Ajax 和 JSON
1、Ajax
为了直接获取 html 文件,将配置文件匹配规则修改如下
springmvc
*.do
package com.pengshiyu.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Controller
public class HelloController {
@RequestMapping(value = "/ajax")
public void ajax(String name, HttpServletResponse response) throws IOException {
System.out.println(name);
response.getWriter().println("ok");
}
}
$(function () {
$("#name").blur(function () {
$.post(
"/ajax.do",
{
name: $("#name").val(),
},
function (res) {
alert(res);
}
);
});
});
2、JSON jackson-annotations jackson-core jackson-databind
com.fasterxml.jackson.core
jackson-annotations
2.11.0
com.fasterxml.jackson.core
jackson-databind
2.11.0
com.fasterxml.jackson.core
jackson-core
2.11.0
配置 JSON 转换器
text/plain;charset=UTF-8
package com.pengshiyu.controller;
import com.pengshiyu.bean.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@Controller
public class HelloController {
@RequestMapping("/json")
@ResponseBody
public List ajax() throws IOException {
List users = new ArrayList();
users.add(new User("Tom", 23));
users.add(new User("Jack", 24));
return users;
}
}
拦截器
interceptor 1、实现 HandlerInterceptor 接口 DispatcherServlet
package com.pengshiyu.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyInterceptor implements HandlerInterceptor {
// 在请求处理的方法执行之前执行,
// 返回true执行下一个拦截器,
// 返回false不执行下一个拦截器
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle");
return true;
}
// 在处理方法执行之后执行
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle");
}
// 在DispatcherServlet 之后执行 清理工作
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion");
}
}
2、配置拦截器
3、执行顺序
package com.pengshiyu.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Controller
public class HelloController {
@RequestMapping("/echo")
public void echo(HttpServletResponse response) throws IOException {
System.out.println("echo");
response.getWriter().println("echo");
}
}
preHandle
echo
postHandle
afterCompletion
登录拦截器实现
配置
login.do
拦截器
package com.pengshiyu.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
public class MyInterceptor implements HandlerInterceptor {
private List allowedUrls;
// 在请求处理的方法执行之前执行,
// 返回true执行下一个拦截器,
// 返回false不执行下一个拦截器
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle");
// 解决中文打印乱码输出
response.setContentType("text/html;charset=utf-8");
// 判断session
Object user = request.getSession().getAttribute("user");
if(user != null){
return true;
}
// 判断放行路径
String url = request.getRequestURL().toString();
System.out.println(url);
for(String temp: allowedUrls){
if(url.endsWith(temp)){
return true;
}
}
// 如果没有登录就重定向到登录页面
response.sendRedirect("/login.do");
return false;
}
// 在处理方法执行之后执行
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle");
}
// 在DispatcherServlet 之后执行 清理工作
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion");
}
public List getAllowedUrls() {
return allowedUrls;
}
public void setAllowedUrls(List allowedUrls) {
this.allowedUrls = allowedUrls;
}
}
User 类
package com.pengshiyu.bean;
public class User {
private String name;
private String password;
public User() {
}
public User(String name, String password) {
this.name = name;
this.password = password;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
}
Controller
package com.pengshiyu.controller;
import com.pengshiyu.bean.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
@Controller
public class UserController {
@RequestMapping("/userInfo")
public void userInfo(HttpSession session, HttpServletResponse response) throws IOException {
User user = (User) session.getAttribute("user");
response.getWriter().println(user);
}
@RequestMapping(value = "/login", method = RequestMethod.GET)
public String login() throws IOException {
return "/template/login.jsp";
}
@RequestMapping(value = "/login", method = RequestMethod.POST)
public void login(User user, HttpSession session, HttpServletResponse response) throws IOException {
String message = "";
if ("root".equals(user.getName()) && "123".equals(user.getPassword())) {
message = "登录成功";
session.setAttribute("user", user);
} else {
message = "用户名或密码错误";
}
response.getWriter().println(message);
}
}
登录页
登录