简介
springmvc是一个web层mvc框架
model 模型
view 视图
controller 控制器
这是一种设计模式,将责任进行拆分,不同的组件负责不同的事情
优点:
结构清晰
更好维护(大量使用JSP的年代)
缺点:
更加复杂了
入门体验
1.创建wen项目
2.编写web.xml,在其中注册一个特殊的servlet,前端控制器
3.编写一个springmvc的配置文件
注册一个视图解析器
4.编写一个控制器
5.编写一个结果页面
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.dumbo</groupId>
<artifactId>SpringMVCDemo3</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<servlet>
<servlet-name>springmvcdemo3</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>springmvcdemo3</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
视图解析器
<beans xmlns="http://www.springframework.org/schema/beans"
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">
<!--配置一个视图解析器
常用内部资源视图解析器
-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--前缀-->
<property name="prefix" value="/jsp/"/>
<!--后缀-->
<property name="suffix" value=".jsp"/>
</bean>
<bean class="com.dumbo.controller.HelloController" name="/helloController">
</bean>
</beans>
控制器
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
//实现一个接口的方式
public class HelloController implements Controller {
public ModelAndView handleRequest(javax.servlet.http.HttpServletRequest httpServletRequest, javax.servlet.http.HttpServletResponse httpServletResponse) throws Exception {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("girl","菲菲");
modelAndView.setViewName("girl");
return modelAndView;
}
}
组件分析
# web.xml
注册前端控制器,目的在于,我们希望让springmvc去处理所有的请求
通过
<!--servlet映射配置-->
<servlet-mapping>
<servlet-name>springmvcdemo3</servlet-name>
<!--这里统一用斜扛-->
<url-pattern>/</url-pattern>
</servlet-mapping>
处理绝大部分的请求
urlPattern的写法
1. /
2. /* (永远不要这样写)
3. *.do ()
/*不能写的原因
请求/helloController过去的时候,他的仕途名称是girl,girl.jsp页面,
它将其当做一个叫girl.jsp的请求,尝试去匹配对应的controller,所以无法匹配,导致404.
*.do
这种方式,是有些团队此故事呢将请求的行为加个小尾巴进行区分,do *.action
/
处理所有的请求,但是和/.*不一样,他处理完之后要出去的时候不会将这个girl.jsp当做一个新的请求,将这个渲染的结构直接返回浏览器
关于前端控制器的解释
springmvc设计的里面是希望开发者尽量远离原生的servlet API,API不是很好用,比较繁琐,讲操作进一步简化,它将很多东西责任进行了拆分,不希望我们将一些技术点绑定死,可以做到随意的切换,本身还是基于servlet设计的,分发的servlet
springmvc配置文件,名字的问题
默认情况下是用dispatcherServlet的名字当做命名空间,
[servletName]-servlet.xml(WEB-INF下寻找)
[servletName]-servlet = namespace
<!--可以重新声明配置文件的名字-->
<init-param>
<param-name>namespace</param-name>
<param-value>mvc</param-value>
</init-param>
重新指定上下文配置文件的位置即可
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvcdemo3-servlet.xml</param-value>
</init-param>
在类路径下寻找springmvcdemo3-servlet.xml这个配置文件
视图解析器
spring支持多种视图技术
jsp
Freemaker
内部的资源解析器
视图前缀
/jsp/它是我们的请求响应的资源的路径的配置 viewName:girl /jsp/girl
后缀
.jsp 此时我们的前缀+视图名称+后缀=/jsp/girl.jsp
request.getDS.forward(request,response)的作用是一样一样的
物理视图由逻辑视图转换而来
物理视图是 webapp/jsp/girl.jsp
逻辑视图
prefix
logicViewName
suffix
p View = prefix + logicViewName + suffix
控制器
如果要给接口只有一个方法,这种接口叫做函数式接口
@Nullable
ModelAndView handleRequest(HttpServletRequest var1, HttpServletResponse var2) throws Exception;
它像谁?
servlet里面由doGet,doPost里面入参就是请求与响应
viod 无返回值
springmvc 设计为 ModelAndView 为返回值
在model当中填充数据,然后在具体的视图进行展示
还需要在配置文件当中配置一下这个bean,要取个名字,就用来充当了这个请求的uri
<bean class="com.dumbo.controller.HelloController" name="/helloController"></bean>
name是必须的
它就处理一个请求,跟servlet差别不是很大
注解开发模式
基本注解
@Controller
@RequestMapping
开发步骤
配置一下基础扫描包:
<!--配置一个注解扫描包-->
<context:component-scan base-package="com.dumbo.controller"></context:component-scan>
在指定的类上面添加@Controller注解
添加@RequestMapping 类似于前面的Controller的那个name
当我们写上Controller之后,就标记了它为spring的一个组件,并且是控制器的组件,此时我们的handlermapping会去扫描寻找这个Controller是否与之匹配,如果发现匹配就把这里处理的工作交给它
匹配的规则
具体的匹配就是通过请求的路径
@RequestMapping(URI)
此时就是通过URI进行匹配
@RequestMapping可以写在方法上也可以写在类上
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
//不要继承任何类和实现任何接口
@Controller
public class ByeController {
// ModelAndView Model + View
@RequestMapping("/bye")
public String bye(Model model){
model.addAttribute("model","modeller");
// 这里return的就是我们的那么viewName
// 此时去的是/jsp/bye.jsp页面
return "bye";
}
}
转发与重定向
转发到页面 默认到选项
重定向到页面 redirect:path
转发到另一个控制器 forward:path
package com.dumbo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/hello")
public class HelloController {
// 转发
@RequestMapping("/forward")
public String forward(Model model){
// springmvc model 默认上在请求域当中存储值
model.addAttribute("skill","睡觉");
System.out.println("shuijiao");
return "forward";
}
@RequestMapping("/redirect")
public String redirect(Model model){
model.addAttribute("skill","煮饭");
System.out.println("zhufan");
// 不带斜扛的写法上一个相对路径,根据当期上下文决定
// 如果上重定向,就跟视图解析的规则无关
return "redirect:/jsp/redirect.jsp";
}
@RequestMapping("/forword2")
public String forwardAnotherController(){
return "forward:/baby/hello";
}
}
关于springmvc 访问web元素
request
session
application
请求路径永远写 /
可以通过模拟的对象完成操作,也可以使用原生的Servlet API完成,直接在方法中入参即可
package com.dumbo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.request.WebRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
@Controller
@RequestMapping("/web")
public class WebElementController {
@RequestMapping("/request")
public String request(WebRequest request){
System.out.println(request.getParameter("girl"));
return "forward";
}
@RequestMapping("/request2")
public String request2(HttpServletRequest request){
System.out.println(request.getParameter("boy"));
return "forward";
}
@RequestMapping("/request3")
public String request3(HttpSession session){
session.setAttribute("g","神庙逃亡");
session.getServletContext().setAttribute("ctx","宇宙我最帅");
return "redirect:/jsp/redirect.jsp";
}
}
注解详解
@RequestMapping
value:写的是路径,是一个数组的形式,可以匹配多个路径
path:是value的别名,二选一,作用一致
method:是可以指定可以访问的请求的类型,比如,GET,POST
params:可以指定参数,你还可以去限定这个参数的特征,必须等于某个值
header:能够影响浏览器的行为
consumers:媒体类型,可以限定必须为application/json;charset="UTF-8"
// 请求映射路径path可以写多个值
// 如果没有限定,啥请求都可以
@RequestMapping(value = {"/m1","/m2"},method = RequestMethod.GET)
public String m1(){
System.out.println("m1..");
return "forward";
}
// params描述了,我需要两个参数,一个是boy,一个是girl
@RequestMapping(value = {"/m2"},params = {"boy=wangfei","girl=guandong"})
public String m2(){
System.out.println("m2..");
return "forward";
}
修改路径
@WebServlet(urlPatterns = {},loadOnStartup = 2)
public class WebPathInitServlet extends HttpServlet {
@Override
public void init(ServletConfig config) throws ServletException {
// 在整体应用上下文当中存储了一个ctx 的值,用它来引用上下文路径
config.getServletContext().setAttribute("ctx",config.getServletContext().getContextPath());
super.init(config);
}
}
${ctx} 调用
关于请求路径的问题
springmvc支持ant风格
? 任意的一个字符,斜杠例外
* 0到任意个字符都可以 不能含斜杠
** 支持任意层路径 m3/**
// @RequestMapping(path = {"/m3?"})
// @RequestMapping(path = {"/m3*"})
@RequestMapping(path = {"/m3/**"})
public String m3(){
System.out.println("m3...");
return "forward";
}
@GetMapping @PostMapping
getMapping:限定了只支持get请求
postMapping:限定了只支持post请求
对于非get,post请求的支持
对于非get,post请求的支持,需要有额外的内容添加,要添加一个过滤器额外处理
<!--注册一个支持所有http请求类型的过滤器-->
<filter>
<filter-name>hiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>hiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
表单提交里面还要添加一个隐藏的参数
<form action="${ctx}/web/m5" method="post">
<input type="hidden" name="_method" value="DELETE">
<input type="submit" value="提交">
</form>
关于静态资源访问的问题
由于我们的servlet设置了URL匹配方式为/ 所以,它将静态资源也当做一个后台的请求
他尝试去匹配一个static/css/index.css的Controller里面的RequestMapping的组合,因为没有,所以404
最简单的是 是让springmvc单独处理,将这些交给容器的默认的servlet处理,就不让 DispatcherServlet来处理了
<!--默认的Servlet处理者-->
<mvc:default-servlet-handler/>
<mvc:annotation-driven/>
@PathVariable
restful风格
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/product")
public class ProductController {
@RequestMapping("/add/{id}/{name}/{price}")
public String addProduct(@PathVariable("id")Integer id,@PathVariable("name")String name,@PathVariable("price")Double price){
System.out.println(id+name+price);
return "forward";
}
}
{id}与@PathVariable("id")Integer id进行绑定
@Responsebody
返回数据的,一般情况下返回json 格式
@SessionAttributes
这个用在类上面,它会将模型自动填充到会话里面去
@SessionAttribute
要求当前这次访问当中的会话里面必须要有某个对象
关于post请求中文乱码问题解决
<filter>
<filter-name>characterEncodingFilter</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>
<init-param>
<param-name>foeceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
方式一:
通过属性名称进行绑定,可以完成数据传递
页面当中表单元素的那么值要和后台的形参的名字一致
前台:
<form action="${context}/user/put" method="post">
<input type="hidden" name="_method" value="put">
<input type="text" name="name">
<input type="password" name="password">
<input type="submit" value="提交">
</form>
后台:
@PutMapping("/put")
@ResponseBody
public String put(String name,String password){
System.out.println(name + password);
return "ok";
}
方式二:
利用@RequestParam
后台:
@PutMapping("/put")
@ResponseBody
public String put(@RequestParam("name") String name,@RequestParam("password") String password){
System.out.println(name + password);
return "ok";
}
方法三:
直接利用POJO形式传递
后台:
@PutMapping("/put")
@ResponseBody
public String put(User user){
System.out.println(user.getName() + user.getPassword());
return "ok";
}
@ModelAttribute
方式一:
package com.dumbo.controller;
import com.dumbo.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/user2")
public class User2Controller {
// 就是在Controller里面的任意一个处理具体的方法之前都会执行
@ModelAttribute
public User init(){
System.out.println("init....");
User u = new User();
u.setName("王菲");
u.setPassword("123");
return u;
}
@RequestMapping("/login")
public String login(Model model){
System.out.println(model.containsAttribute("u"));
System.out.println(model.containsAttribute("user"));
return "msg";
}
}
如果某些对象从头到尾每次请求当中都要存在,不消失,就适合用
方法二:
@ModelAttribute("user")
public void init(Model model){
System.out.println("init....");
User user = new User();
user.setName("王菲");
user.setPassword("123");
model.addAttribute("user",user);
}
如果没有传递这个模型过来,那么方法上加了@ModelAttribute的为你提供,如果传了就用你的
前台:
<form action="${context}/user/put" method="post">
<input type="hidden" name="_method" value="put">
<input type="text" name="name">
<input type="password" name="password">
<input type="date" name="date">
<input type="submit" value="提交">
</form>
后台:
@Controller
@RequestMapping("/user")
public class UserController {
@InitBinder("user")
public void init(WebDataBinder dataBinder){
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
simpleDateFormat.setLenient(true);
dataBinder.registerCustomEditor(Date.class,new CustomDateEditor(simpleDateFormat,false));
}
@PutMapping("/put")
@ResponseBody
public String put(@ModelAttribute User user){
System.out.println(user.getName() + user.getPassword());
System.out.println(user.getDate());
return "ok";
}
}
注解:
@DateTimeFormat(pattern = "yyyy/MM/dd")
private Date date;
通过initBinder指定了user名字和modelAttribute 里面user绑定
不指定名字,根据数据类型一样可以分析解析转换成功
时间+日期:
@InitBinder("user")
public void init(WebDataBinder dataBinder){
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
simpleDateFormat.setLenient(true);
dataBinder.registerCustomEditor(Date.class,new CustomDateEditor(simpleDateFormat,false));
}