SpringMVC是Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。是Spring的一个子模块。Spring Web MVC是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想,将web层进行职责解耦,基于请求驱动指的就是使用请求-响应模型,框架的目的就是帮助我们简化开发,Spring Web MVC也是要简化我们日常Web开发的一个框架。
原生的Servlet大多情况只能响应一个Get或Post请求,简简单单的一个CRUD业务可能要写四个Servlet,这让项目的类数量显得过于庞大,SpringMVC提供了一个所有请求的入口,我们一般称之为前置控制器,他能把进入到Servlet的请求拦截,并且通过映射,把请求发送到加了@Controller的控制器类中,这样我们可以在一个控制器中写多个不同请求的页面逻辑。
在web.xml中注册,语法是原生Servlet
<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">
<servlet>
<servlet-name>DispatcherServletservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:application.xmlparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>DispatcherServletservlet-name>
<url-pattern>*.dourl-pattern>
servlet-mapping>
web-app>
1.可以选择类似xml文件中*.do的形式来指定请求的后缀,所有以.do结尾的请求都会被DispatcherServlet拦截
2.可以用/代替*.do形式的映射路径,拦截所有来自客户端的请求,但是会把静态资源请求拦截
拦截静态请求的原因:/拦截了所有来自客户端的请求,包括静态资源的请求,但是一般情况下我们不会在Controller中对静态资源进行处理,这就导致了url-pattern中填了/后无法对静态资源进行访问
在SpringMVC配置文件中使用默认的servlet-handler对静态资源进行处理,需要搭配注解驱动进行使用
<mvc:default-servlet-handler/>
<mvc:annotation-driven/>
SpringMVC为我们提供了一个资源处理映射标签,location指定静态资源的位置,mapping指定处理哪种静态资源
location指定静态资源的位置
<mvc:resources mapping="/img/**" location="/img/"/>
<mvc:annotation-driven/>
在方法二的基础上,每当我们有一种静态资源时,我们就需要去配置一个resource标签,这令我们的配置文件相当臃肿,所以在开发中,我们可以采用把所有的静态资源统一放在一个文件夹下(通常命名为static),我们只需要指定这一个静态资源文件夹的映射即可处理所有静态资源请求。
像这样:
<mvc:resources mapping="/static/**" location="/static/"/>
<mvc:annotation-driven/>
我们知道,Spring为开发者提供了四个注解用于注册javaBean,他们分别是:
@Controller 控制层,就是我们的action层
@Service 业务逻辑层,就是我们的service或者manager层
@Repository 持久层,就是我们常说的DAO层
@Component (字面意思就是组件),它在你确定不了事哪一个层的时候使用。
其实,这四个注解的效果都是一样的,Spring都会把它们当做需要注入的Bean加载在上下文中,但是在SpringMVC中,赋予了@Controller注解一个新的功能:处理请求。我们把一个注有@Controller的注解作为一个控制器使用,一般情况下一个控制器类对应着一个页面的所有请求。
控制器类:
@Controller
public class FirstController {
@RequestMapping("/some.do")
public ModelAndView doSome(){
ModelAndView mv = new ModelAndView();
mv.addObject("a","你好世界"); //向页面中加入一个名称为a,值为你好世界的数据
mv.setViewName("result.jsp"); //设置处理完请求后要跳转到result。jsp页面
return mv;
}
}
index.jsp页面:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Titletitle>
head>
<body>
<p>springMVC初体验p>
<P><a href="some.do">发起some.do请求a>P>
body>
html>
result页面:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>请求结果title>
head>
<body>
返回了一个数据:${a}
body>
html>
请求结果:
1:注解可以使用在注明了控制器类的方法上,表面该方法处理对应请求
2:注解使用在控制器类上时,该类处理的所有请求前都相当于加上了对应的url,
例如在类上注明@RequestMapping("/a"),类中一个方法注明了@RequestMapping("/b"),则该方法实际处理的是"/a/b"请求
3:RequestMapping具有一个参数method来说明该方法处理的是何种http请求(Get、Post等)如果不指定该参数,则当前请求路径下所有http方法均支持
.控制器方法中的三个形参HttpServletRequest、HttpServletResponse、HttpSession会由框架自动赋值,只需开发人员直接调用即可
要求在形参列表的形参命名和请求传入参数名称相同,相当于servlet中HttpServletRequest.getParameter()方法,框架会自动将获取的String转化为int/long/double等。
需要注意的是,出于健壮性考虑,形参使用包装类参数可以避免前端传入的参数为空时,类型转换导致的异常
逐个接收会出现的问题:
当传入的参数名和方法名不一样时,可以使用@RequestParam注解限定该参数接受的是传入的哪个命名的参数
当@RequestParam在使用时会默认限定该形参不能为空,可以通过将注解的参数required=false来使得该形参可以为空
把对象当作控制器方法形参时,spring会自动把传入的参数对应的名字注入到对象中相同的成员变量里,使用的是无参构造和set方法
@Controller
public class ParamController {
/**
* 框架自动赋值
*/
@RequestMapping("some.do")
public ModelAndView doSome(HttpServletRequest req, HttpServletResponse resp, HttpSession session){
return null;
}
/**
* 逐个接收参数
*/
@RequestMapping(value = "post.do",method = RequestMethod.POST)
public ModelAndView doPost(String name,Integer password){
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("name",name);
modelAndView.addObject("password",password);
System.out.println(name + password);
modelAndView.setViewName("res");
return modelAndView;
}
/**
* 逐个接收参数(不同名)
*/
@RequestMapping(value = "name.do",method = RequestMethod.POST)
public ModelAndView doNamPost(@RequestParam(value = "rname",required=false) String name,
@RequestParam(value = "rpassword",required=false)Integer password){
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("name",name);
modelAndView.addObject("password",password);
System.out.println(name + password);
modelAndView.setViewName("res");
return modelAndView;
}
/**
* 对象接收
*/
@RequestMapping(value = "pojo.do",method = RequestMethod.POST)
public ModelAndView dojo(User user){
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("user",user);
System.out.println(user);
modelAndView.setViewName("res");
return modelAndView;
}
}
1):ModelAndView 既含有数据,也有视图,执行的是请求转发
2):String:视图,SpringMVC会用配置的视图解析器来找到MVC视图
3);void: 空返回值,可以利用HttpServletResponse获取Writer向页面中响应json数据
4):obj: 必须定义MVC注解驱动mvc:annotation-driven/,这个驱动向spring上下文中添加了几个实现了HttpMessageConverter接口的bean,接着,框架会调用实现类的writer向页面中写入对应格式数据(xml,json),配合@ResponseBody向页面响应数据
返回集合时,实现类的writer会自动将其写成对应格式的json数组或是map
返回值为ModelAndView时,使用setViewName方法执行的默认是请求转发,类似
HttpServletRequest().getRequestDispatcher().forward();
我们可以手动设置页面跳转的方式。手动设置跳转的方法好处是可以跳转到其他的控制器,
都可以访问视图资源(html,jsp等),手动页面跳转不经过视图解析器,
必须设置视图的全路径名,且重定向操作无法访问到WEB-INF目录下的文件(转发操作可以)
@Controller
public class MyController {
@RequestMapping("/red.do")
public ModelAndView doRed(String
name,Integer age){
ModelAndView mv = new ModelAndView();
mv.addObject("name",name);
mv.addObject("age",age);
mv.setViewName("redirect:/view/res.jsp");
return mv;
}
}
springMVC为重定向也提供了参数携带,我们可以在jsp页面中使${param.val}
的方式取到我们向ModelAndView中添加的参数。
如上面代码访问”/red.do"请求后的路径会变为http://localhost:8080/view/res.jsp?name=name&age=age
@Controller
public class MyController {
@RequestMapping("/for.do")
public ModelAndView doForward(String name,Integer age){
ModelAndView mv = new ModelAndView();
mv.addObject("name",name);
mv.addObject("age",age);
mv.setViewName("forward:/WEB-INF/view/res.jsp");
return mv;
}
}
由于SpringMVC默认为转发操作,所以forward在这的作用大多应用与转发到其他控制器方法或是将视图设置为视图解析器搜索不到的位置。
1):返回值是java对象时,可以向页面中输出对应格式的数据
2):当返回值时String时,由于@ResponseBody的存在,视图处理器将不会把他当作一个视图,而把他当作一个普通数据返回到前端
注意:使用String像页面响应字符串时,虽然有字符编码过滤器的存在,但是数据传到客户端时使用的编码格式是ISO-8859-1,文本格式是text/plain会导致乱码,原因是此时向页面写入数据采用的是writer流写入,而非响应体返回数据需要在@RequestMapping参数中注明produces = "text/html;charset=utf-8",
在原生servlet开发中,我们重写的doGet/doPost方法上带有参数HttpServletRequest和HttpServletResponse, 我们处理字符乱码的方法一般为:
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
亦或者是写一个过滤器来使所有请求都先设置字符编码:
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
chain.doFilter(request,response);
}
但在SpringMVC中我们写控制器类大多数情况下并不会带有这两个参数,而自己写一个过滤器又显得有点麻烦,这时就可以使用SpringMVC自带的一个字符编码过滤器,只需要在web.xml配置文件中声明它即可
需要提供的三个初始参数
encoding(String):设置编码集
forceRequestEncoding和forceResponseEncoding(Boolean) 指定的编码是否应该覆盖现有的响应/请求编码
<filter>
<filter-name>CharacterEncodingFilterfilter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
<init-param>
<param-name>encodingparam-name>
<param-value>utf-8param-value>
init-param>
<init-param>
<param-name>forceRequestEncodingparam-name> <param-value>trueparam-value>
init-param>
<init-param>
<param-name>forceResponseEncodingparam-name>
<param-value>trueparam-value>
init-param>
filter>
<filter-mapping>
<filter-name>CharacterEncodingFilterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
当控制器方法返回值ModelAndView对象中不包含真正的视图,而是一个逻辑视图路径的时候,视图解析器就会把该逻辑视图路径解析为真正的View视图对象,然后通过View的渲染,将最终结果返回给用户。
通俗来讲,我们在进行页面跳转时,假设我现在有许多页面,他们均在web根目录下一个名为jsper文件夹中,我们可以配置一个视图解析器,让我们只需要输入文件名,而非文件全路径,即可完成对视图的选择。
配置视图解析器
在SpringMVC配置文件中注册名为InternalResourceViewResolver的Bean:
需要设置两个属性:
prefix:视图所在路径
suffix:视图名后缀
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/jsper/"/>
<property name="suffix" value=".jsp"/>
bean>