首先,什么是SpringMVC?
SpringMVC是一个MVC框架,也是Spring的一个模块,有Spring的特性。
那么,什么是MVC?
MVC的全名是Model View Controller,M是指业务模型,V是指用户界面,C则是控制器,是模型(model)-视图(view)-控制器(controller)的缩写,是一种软件设计典范。
MVC在客户端/服务器(b/s)系统下的应用:
SpringMVC是一个基于Spring的web架构:
SpringMVC依赖于Core(IOC),SpringMVC需要导入Core包和SpringMVC特有的包。
SpringMVC框架:
需要程序员开发的:处理器Handler(Controller)视图View
具体怎么做呢?
举例(我用的是IDEA):
第一步,先在WEB-INF下的lib目录中加入如下包
Spring框架的各类包下载:https://repo.spring.io/ui/native/release/org/springframework
commons-logging的各个版本下载:http://archive.apache.org/dist/commons/logging/binaries/
jstl的jar包下载看
第二步,WEB-INF下的web.xml文件里添加配置
<!-- 配置DispatcherServlet(前端控制器) -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<!-- classpath:代表就是src这个路径 -->
<param-value>classpath:springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!-- /student.action /user.action
所有以action结尾的请求都交给DispatcherServlet来处理
-->
<url-pattern>*.action</url-pattern>
</servlet-mapping>
第三步,创建model(entity)类,比如我是在src下面创建了test目录,创建Student类
public class Student {
private Integer id;
private String name;
private Integer age;
private String gender;
public Student() {
}
public Student(Integer id, String name, Integer age, String gender) {
this.id = id;
this.name = name;
this.age = age;
this.gender = gender;
}
public Student(String name, Integer age, String gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", gender='" + gender + '\'' +
'}';
}
}
第四步,创建jsp文件,比如我这里创建的是student_insert.jsp和student_info.jsp
为了系统一些,我先创建了jsp目录
然后才在jsp目录下添加 jsp文件。
student_insert.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>学生插入</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/student/insert.action" method="post">
姓名:<input type="text" name="name"/><br/>
年龄:<input type="text" name="age"/><br/>
性别:<input type="text" name="gender"/><br/>
<input type="submit" value="提交"/>
</form>
</body>
</html>
student_info.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
student_info.jsp
${student}
</body>
</html>
第五步,创建控制器Controller(即之前说的Handler,一般来说,Controller是Handler,但Handler不一定是Controller),比如我创建的是StudentController
@Controller
// 请求映射,怎么样找到这些请求的路径 /student/insert.action,*.action在xml文件里面配置了
@RequestMapping(value = "/student")
public class StudentController {
@RequestMapping(value = "/insert")
public ModelAndView insert(Student student) {
System.out.println("StudentController.insert()");
System.out.println(student);
// Model:数据 View:界面
ModelAndView modelAndView = new ModelAndView();
// request.setAttribute("student", student);
modelAndView.addObject("student", student);
// request.getRequestDispatcher("/jsp/student_info.jsp").forward(request,response);
// /jsp/在之前配置过了,此处需要省略,否则如果加上,会造成两个/jsp/累加,找不到文件,但可以加上/
modelAndView.setViewName("student_info");
// modelAndView.setViewName("/student_info");这样也正确
return modelAndView;
}
}
第六步,创建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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<!-- 扫描base-package对应的包下面类中所有的注解-->
<context:component-scan base-package="test"/>
<!-- 注解驱动 -->
<mvc:annotation-driven/>
<!-- 视图解析器
1、如果Controller中书写的是视图的逻辑名,这个视图解析器必须要配置。
前缀+视图逻辑名+后缀=真实路径
/jsp/student_insert.jsp
/jsp/ + student_insert + .jsp
2、如果视图解析器书写的是视图的真实路径,那么这个视图解析器可以不配置
-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 路径前缀 -->
<property name="prefix" value="/jsp/"/>
<!-- 路径后缀 -->
<property name="suffix" value=".jsp"></property>
</bean>
</beans>
最后,Tomcat一运行,输入网址http://localhost:8080/SpringMVC/jsp/student_insert.jsp(我的端口号是8080,项目名是SpringMVC)
填写信息,提交,
浏览器跳到了http://localhost:8080/SpringMVC/student/insert.action,显示
一次SpringMVC的简单尝试就OK了
之前提到的都是服务器内部转发,如果要重定向,可以直接在return中写
@RequestMapping(value = "/insert")
public String insert() {
System.out.println("StudentController.insert");
// 这是重定向,不是转发,/jsp和.jsp不能省略!
return "redirect:/jsp/student_insert.jsp";
}
说一下ModelAndView和Model
ModelAndView:既放数据,也放转发的界面
Model:放数据,以方法返回值的形式设置转发的页面
之前举例中就用了ModelAndView
Model的用法举例:
@RequestMapping("/insert")
public String insert(Student student, Model model) {
System.out.println("StudentController.insert()");
System.out.println(student);
// req.setAttritbu("student", student);
model.addAttribute("student", student);
// req.getRequestDispatcher("/jsp/student_info.jsp").forword(req, resp);
return "student_info";
}
简单说一下RequestMapping注解:
RequestMapping是建立请求URL和处理方法之间的对应关系。
之前的@RequestMapping("/student")其实等于@RequestMapping(value = "/student")
之前的@RequestMapping("/insert")等于@RequestMapping(value = "/insert")
就像URL链接上是http://localhost:8080/SpringMVC/student/insert.action,就是通过这个注解(.action是通过之前的配置)
如果不写method,那么就会默认既支持get也支持post
但有些时候,我们希望只支持一种,怎么设置呢?
比如,
// 只能用post
@RequestMapping(value = "/insert", method = RequestMethod.POST)
public String insert(Student student, Model model) {
System.out.println("StudentController.insert");
System.out.println(student);
// Model:模型 数据 View:界面
// request.setAttribute("student", student);
model.addAttribute("student", student);
// request.getRequestDispatcher("/jsp/student_info.jsp").forward();
return "/student_info";
}
例子中,就是只能用post方法,否则报405错误,如果只要get方法,将method对应的值设为RequestMethod.GET。
当然,要是觉得ModelAndView和Model不熟悉,不想用,也可以用传统的web参数:
@RequestMapping(value = "/insert", method = RequestMethod.POST)
public void insert(HttpServletRequest request, HttpServletResponse response, HttpSession session)throws ServletException, IOException {
System.out.println("StudentController.insert()");
String name = request.getParameter("name");
String age = request.getParameter("age");
String gender = request.getParameter("gender");
Student student = new Student(name, Integer.parseInt(age), gender);
request.setAttribute("student", student);
// 不要省略/jsp/与.jsp
request.getRequestDispatcher("/jsp/student_info.jsp").forward(request, response);
}
乱码问题:
在post方式传递参数出现了中文乱码问题,就在web.xml中配置:
<!-- 解决POST乱码问题 -->
<filter>
<filter-name>characterEncoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
ResponseBody注解:
在lib目录下加入json有关jar包,可以使得函数返回值作为页面body里面的内容
下载地址:
https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations/2.4.0
https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core/2.4.2
https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind/2.2.4
@RequestMapping("/select")
@ResponseBody
public List<Student> selectById(Integer id) {
System.out.println("StudentController.selectById");
Student student1 = new Student(1, "zhangsan1", 1, "女");
Student student2 = new Student(2, "zhangsan2", 1, "女");
Student student3 = new Student(3, "zhangsan3", 1, "女");
Student student4 = new Student(4, "zhangsan4", 1, "女");
Student student5 = new Student(5, "zhangsan5", 1, "女");
List<Student> list = new ArrayList<>();
list.add(student1);
list.add(student2);
list.add(student3);
list.add(student4);
list.add(student5);
return list;
}
启动Tomcat,输入http://localhost:8080/SpringMVC/student/select.action,就可以得到有list数据的页面
RequestParameter注解:
在默认的情况下,只有方法参数名要与传递过来的name属性名相同,SpringMVC才会帮我们进行参数绑定,如果不相同,就需要用到RequestParameter注解了
在不相同的属性前加上注解@RequestParam,注解中的value设置为传递过来的参数名
比如,
@RequestMapping("/selectByPage")
public void selectByPage(@RequestParam(value = "page" ) Integer pageNo, Integer pageSize) {
System.out.println("StudentController.selectByPage");
System.out.println("pageNo :" + pageNo);
System.out.println("pageSize : " + pageSize);
}
输入http://localhost:8080/SpringMVC/student/selectByPage.action?page=3&pageSize=10,在控制台可以看到
该注解中还有required、defaultValue ,
required设置为true表示这个属性必须传递了参数,否则既不传递参数,也没有默认值,就报错,而如果不设置required,也不设置默认值,就表示默认值是null
defaultValue可以设置默认值
比如,
@RequestMapping("/selectByPage")
public List<Student> selectByPage(@RequestParam(value = "page",required = true) Integer pageNo, @RequestParam(defaultValue = "10") Integer pageSize) {
System.out.println("StudentController.selectByPage");
System.out.println("pageNo :" + pageNo);
System.out.println("pageSize : " + pageSize);
Student student1 = new Student(1, "zhangsan1", 1, "女");
Student student2 = new Student(2, "zhangsan2", 1, "女");
Student student3 = new Student(3, "zhangsan3", 1, "女");
Student student4 = new Student(4, "zhangsan4", 1, "女");
Student student5 = new Student(5, "zhangsan5", 1, "女");
List<Student> list = new ArrayList<>();
list.add(student1);
list.add(student2);
list.add(student3);
list.add(student4);
list.add(student5);
return list;
}
输入http://localhost:8080/SpringMVC/student/selectByPage.action?page=3
控制台打印的也是
PathVariable注解:
通过 @PathVariable 可以将URL中占位符参数{xxx}绑定到处理器类的方法形参中
比如,
@RequestMapping("/select/{id}")
public void select( @PathVariable("id") Long id) {
System.out.println(id);
}
输入http://localhost:8080/SpringMVC/student/select/12.action
可以在控制台打印12
视图解析器
1、如果Controller中书写的是视图的逻辑名,这个视图解析器必须要配置。
前缀+视图逻辑名+后缀=真实路径,比如之前的return "/student_info";在配置之后,就等于
return "/jsp/studnet_info.jsp";
2、如果Controller中写的是视图的真实路径,那么这个视图解析器可以不配置
具体配置(之前提到过):
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 路径前缀 -->
<property name="prefix" value="/jsp/"/>
<!-- 路径后缀 -->
<property name="suffix" value=".jsp"></property>
</bean>
拦截器:
如果想要对于用户信息进行拦截过滤,那么在这里,只需要创建相关的类,再在之前提到过的springmvc.xml中进行配置即可。拦截器本质是AOP(面向切面编程),也就是说符合横切关注点的所有功能都可以放入拦截器实现。
比如,
在test包下面建了一个interceptor包,在该包里我创建了两个拦截器用于测试,
MyInterceptor1
public class MyInterceptor1 implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
System.out.println("MyInterceptor1.preHandle");
return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptor1.postHandle");
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
}
MyInterceptor2
public class MyInterceptor2 implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
System.out.println("MyInterceptor2.preHandle");
return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptor2.postHandle");
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
}
然后在springmvc.xml文件里面加上配置(放在</beans>前,别放错在最下面了):
<!--配置拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="test.interceptor.MyInterceptor1"></bean>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="test.interceptor.MyInterceptor2"></bean>
</mvc:interceptor>
</mvc:interceptors>
如果再次输入之前提到过的http://localhost:8080/SpringMVC/student/select/12.action
就会发现控制台打印了
MyInterceptor1.preHandle MyInterceptor2.preHandle 12 MyInterceptor2.postHandle MyInterceptor1.postHandle MyInterceptor2.afterCompletion MyInterceptor1.afterCompletion
具体是怎么实现的呢?