SpringMVC
1. Spring简化IOC容器的获取
1.1 原理
- 在spring学习中,我们要获取IOC容器通常都是
new ClasspathXmlApplicationContext(applicationContest.xml)
来获取,这种方式的弊端是每次通过IOC获取Bean时都要new一下,applicationContest.xml文件要加载多次。 - 在web项目中,我们可以利用监听器监听web应用的启动,当web应用启动时,就自动加载Spring配置文件applicationContext.xml,创建容器,并把容器存入到Servlet中的最大作用域servletContext中。然后就可以在任一位置获取servletContext中存储的ioc容器。进而获得Bean。
- 上述这一过程Spring帮我们简化了,我们直接调用相应API就行。
- spring 提供了一个监听器ContextLoaderListener,该监听器内部加载配置文件**,创建ioc容器**,并存储到ServletContext域中,并提供一个客户端工具WebApplicationContextUtils供使用者来获取容器。
1.2 如何利用spring的监听器来获取IOC
- 导入spring-web坐标(pom.xml)
- 在web.xml里配置ContextLoaderListener监听器。
- 使用WebApplicationContextUtils获取IOC容器。
示例:
- 导入Spring集成web的坐标
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
- 在web.xml里配置ContextLoaderListener监听器
<!-- spring的配置文件 指定为classpath下的applicationContext.xml-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!--Spring的监听器-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
- 通过WebApplicationContextUtils工具来获得IOC容器。
ApplicationContext applicationContext =
WebApplicationContextUtils.getWebApplicationContext(servletContext);
Object obj = applicationContext.getBean("id");
2. SpringMVC的概述
- SpringMVC是目前主流的实现MVC设计模式的企业级开发框架,它是spring框架的一个子模块,无需整合,开发起来更加便捷。
- MVC设计模式:将应用程序分为Controller,Model,View三层,Controller接收客户端请求,调用Model生成业务数据,传递给View。
- SpringMVC就是对这套流程的封装,屏蔽了很多底层代码,开放出接口,让开发者可以更加轻松便捷的完成基于MVC模式的web开发。
3. springmvc容器的创建及与spring容器的关系
web.xml(只有关于spring和springmvc的配置,其他的先省略)
<!-- spring的配置文件 指定为classpath下的applicationContext.xml-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- spring的监听-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- spring mvc核心:分发servlet -->
<servlet>
<servlet-name>mvc-dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- spring mvc的配置文件 为classpath下的springMVC.xml-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springMVC.xml</param-value>
</init-param>
<!--表示容器在启动时立即加载servlet -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<!-- 处理所有的url,即所有的请求都要经过DispatcherServlet-->
<servlet-name>mvc-dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
- Tomcat启动时会依次加载web.xml配置中的Listener,Filter和Servlet,当加载到Listener时,会根据ContextLoaderListener监听器来创建spring ioc容器,当加载到Servlet时,会依据上面的已经创建的spring ioc 容器在DispatcherServlet里创建springMVC容器。spring容器是父容器,springMVC是子容器。
- spring中的bean指的是被实例的,组装的及被spring容器管理的java对象。
- spring和springMVC都是管理bean对象的容器。springMVC是负责管理controller对象(bean)的容器,Spring是负责管理service和Dao对象的容器。因此我们在springMVC配置文件里配置的扫描路径是controller的路径,而spring的配置文件里配的是service和dao的路径
- spring容器和springMVC容器是父子容器的关系。spring容器是父容器,springMVC是子容器。在子容器里可以访问父容器里的对象(bean),但在父容器里不可以访问子容器的对象(bean)。通俗的说就是在controller里可以访问service,dao对象,但在service,dao里不能访问controller对象。
- 注意:在web.xml里也可以不配置ContextLoaderListener,这样的话会将所有的bean全都放在DispatcherServlet创建的springMVC容器里。
4. SpringMVC入门
- 通过一个小案例来快速了解一下:客户端发起请求,服务端接收请求,执行逻辑并进行视图跳转。
4.1 开发步骤
- 导入SpringMVC的坐标
- 配置SpringMVC核心控制器DispathcerServlet
- 创建Controller类和视图页面
- 使用注解配置Controller类中业务方法的映射地址
- 配置SpringMVC核心文件spring-mvc.xml
- 客户端发起请求测试
4.2 代码实现
-
在pom.xml里导入Spring和SpringMVC的坐标,导入Servlet和jsp的坐标
<!--Spring坐标--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.5.RELEASE</version> </dependency> <!--SpringMVC坐标--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.0.5.RELEASE</version> </dependency> <!--Servlet坐标--> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> </dependency> <!--Jsp坐标--> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.0</version> </dependency>
-
在web.xml里配置SpringMVC的核心控制器
//通过DispatcherServlet创建springMVC容器,并且加载spring-mvc.xml配置文件 <servlet> <servlet-name>DispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-mvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <!-- 处理所有的url,即所有的请求都要经过DispatcherServlet--> <servlet-name>DispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
-
创建Controller类
public class QuickController { public String quickMethod(){ System.out.println("quickMethod running....."); return "index"; } }
视图界面(/WEB-INF/jsp/index.jsp)
<html> <body> <h2>Hello SpringMVC!</h2> </body> </html>
-
使用注解配置Controller类中业务方法的映射地址
@Controller//将QuickController类对象加入SpringMVC容器中 @RequestMapping("")//用于建立请求url和处理请求方法之间的对应关系 public class QuickController { @RequestMapping("/quick") public String quickMethod(){ System.out.println("quickMethod running....."); return "index"; } }
-
配置SpringMVC核心文件spring-mvc.xml
<!--配置注解扫描 ,让其扫描controller下的注解--> <context:component-scan base-package="com.itheima"/> <!-- 配置视图解析器,对Controller类中返回的字符串进行解析,在其前面加上/WEB-INF/jsp/,后面加上.jsp --> <!--这样才能使DispatchServlet找到index.jsp的具体位置,而不是一个代表文件名字的字符串 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" /> <property name="prefix" value="/WEB-INF/jsp/" /> <property name="suffix" value=".jsp" /> </bean>
-
客户端发起请求测试
http://localhost:8080/itheima_springmvc1/quick
页面显示:
4.3 流程分析
- 当浏览器访问测试地址时,Tomcat会接收客户的请求,然后将请求传给前端控制器(DispatchServlet),前端控制器会根据url的内容找到对应的Controller处理器类,(这里注意url里的quick和requestMapping("/quick")里的quick要对应) 然后执行这个Controller类QuickController,在控制台输出quickMethod running…,然后返回一个字符串(代表要进行页面跳转),这个字符串会被返回到DispatchServlet前端控制器,前端控制器并不知道代表"index"什么意思,会把它交给视图解析器(就是在spring-mvc.xml里配置的),经过视图解析器解析后将结果返回,然后前端控制器再将结果index.jsp页面输出到浏览器。可以看出,这其中最重要的就是前端控制器DispatchServlet
- 上面是对SpringMVC流程的简单分析,实际上要比这个复杂一点,下面我们会深入分析,同时只要理解了这个流程就行,实际开发中要我们写代码的地方很少,springmvc框架自动帮我们做了,但这个过程要了解!
5. SpringMVC的执行流程及分析
5.1 执行流程图
5.2 SpringMVC的核心组件的说明
- DispatcherServlet:前端控制器,是整个springmvc流程控制的核心,控制其他的组件的执行,进行统一的调度,降低组件之间的耦合性,相当于总指挥。
- HandlerMapping:处理器映射器,负责完成客户端请求与controller类的映射,根据请求找到具体的处理器(即controller类),通常通过注解方式查找,例如requestMapping("/xxx")。
- HandlerExecutionChain:处理器执行链包括两部分内容:Handler和HandlerInterceptor(处理器拦截器,是一个接口,如果要完成一些拦截处理,可以实现该接口,系统有一个默认的,要额外拦截时才实现接口)。
- HandlerAdaptor:处理器适配器,Handler要执行业务操作之前,需要进行一系列的操作,包括表单数据的验证,数据类型的转换,将表单数据封装到javaBean等,这些操作都是由HandlerAdaptor来完成,开发者只需要将注意力集中到业务逻辑的处理上,DispatcherServlet通过HandlerAdaptor执行不同的Handler。
- Handler:处理器,也就是controller类,是我们开发中要具体编写的业务控制器,由DispatcherServlet把用户请求转发给Handler。Handler对具体的用户请求进行处理。处理完后返回ModelAndView
- ViewResolver:视图解析器,负责将传进的ModelAndView解析,生成View视图,ViewResolver首先根据逻辑视图解析成物理视图名,即具体的页面地址,再生成View视图对象,将其返回给前端控制器。
- 视图View:它的作用是渲染数据模型,将模型里的数据按照某种MIME类型呈现给客户端,常见的MIME类型有tetx/html,image/jpeg等。
MIME 类型:https://www.w3school.com.cn/media/media_mimeref.asp
5.3 执行流程文字描述
- 用户发送请求到前端控制器DispatcherServlet
- 前端控制器根据请求调用HandlerMapping处理器映射器
- 处理器映射器找具体的处理器Handler 生成处理器对象及处理器拦截器,然后将它们作为一个处理器执行链返回前端控制器。
- 前端控制器根据处理器执行链中的处理器 首先访问HandlerAdapter处理器适配器,
- HandlerAdapter处理器适配器经过适配调用具体的处理器(controller)
- controller执行完逻辑代码返回ModelAndView。
- HandlerAdapter处理器适配器将ModelAndView传给前端控制器。
- 前端控制器将ModelAndView传给ViewReslover视图解析器进行解析
- ViewReslover视图解析器解析后返回具体 的View给前端控制器
- 前端控制器根据返回的view进行渲染视图(将模型数据填充到视图中)。
- 前端控制器将结果返回给客户端浏览器。
6. SpringMVC的相关注解
@RequestMapping
- SpringMVC通过@RequestMapping注解将url请求与业务方法controller进行映射,在Handler的类定义处以及方法处都可以添加@RequestMapping注解,在类定义处添加,相当于客户端多了一层访问路径。
- 相关参数:
- value:指定url请求的实际地址,是@RequestMapping的默认值。例如
@RequestMapping("/index")
等价于@@RequestMapping(value="/index")
,因此value可以省略。 - method:指定请求的method类型,GET,POST,PUT,DELETE等。例如
@RequestMapping(value="/index",method=ReauestMethod.GET)
表示这个注解修饰的方法只能接收get请求的url。浏览器请求大多都是get,表单请求是post,其他请求可以通过postman来实现演示。 - params:指定请求中必须包含某些参数,否则无法调用方法。
@RequestMapping(value="/index",method=ReauestMethod.GET,params={"name","id=10"})
表示请求中必须包含name和id两个参数,同时id的值必须是10
- value:指定url请求的实际地址,是@RequestMapping的默认值。例如
@Controller
- @Controller在类定义处添加,表示将该类交给springMVC容器来管理(需要在springmvc.xml配置文件里开启自动扫描controller包),同时使其成为一个控制器,可以接收客户端请求。
@RestController
- @RestController注解是@Controller和@ResponseBody的合集,表明这是个控制器Bean,并且将返回值直接返回到http响应体中。即作用也是@Controller和@ResponseBody两个注解 的合集。
- @RestController返回的对象数据直接以json或xml形式写入http响应体中。
@RequestParam
- 当请求参数的名称与Controller方法的参数名称不一致,即无法自动进行映射时,需要使用这个注解来完成手动映射。
请求参数为name,接收的形参为username,这时需要这个注解来进行映射。这个工作是由HandlerAdapter处理器映射器来完成的。
public void save16(@RequestParam(value="name") String username)
此外还有一些参数可供选择:
@RequestParam(value="name",required=false,defaultValue="hello")
value:与请求参数的名称对应,将请求参数的值赋给它所修饰的形参
required:默认不写时为true,表示请求中的请求参数不能为空。可以设置为false,表示请求参数为空。
defaultValue:当required为false时,即请求参数为空时,可以给请求参数一个默认值hello。
@Autowired*
@PathVariable
url:http://localhost:8080/quick17/zhangsan
@RequestMapping("/quick17/{name}")
@ResponseBody
public void save17(@PathVariable(value="name") String username) throws IOException {
System.out.println(username);
}
- @PathVariable注解主要是用来完成@RequestMapping("/quick17/{name}")中的占位符{name}与形参username的映射。在restful风格的url中常用。
- 注意:@RequestMapping("/quick17/{name}")中的name要和@PathVariable(value=“name”)中的name匹配。后面的username形式可以变
- 参考10.10 获得Restful风格的参数
@PathParam*
@ResponseBody
- 这个注解用在Controller类下的方法上,表示告知SpringMVC框架这个方法不进行视图跳转,直接进行数据响应。ResponseBody即响应体,方法返回的字符串不是进行页面跳转而是直接在http响应体中返回。
@RequestBody
- 作用在形参列表上,用于将前台发送来的固定格式的数据(json或者xml)封装为对应的javaBean对象(封装时用到的一个对象是SpringMVC默认配置的HttpMessageConverter进行解析,然后封装在形参)
- @RequestBody注解常用来处理content-type为application/json类型(即content-type不是默认的application/x-www-form-urlcoded编码的内容,比如说:application/json或者是application/xml等)。同时使用@RequestBody时请求方式要选择post方式,get方式返回为null
@RequestHeader
-
可以获取http请求消息的请求头信息。
-
@RequestHeader注解的属性如下:
value:请求头的名称
required:是否必须携带此请求头
-
详见10.12 获取请求头中的消息。
@CookieValue
-
获取请求头中Cookie 的值
-
@CookieValue注解的属性如下:
value:指定cookie的名称
required:是否必须携带此cookie
-
详见10.12 获取请求头中的消息。
@PostMapping
@PostMapping("users")
等价于@RequestMapping(value="/users",method=RequestMethod.POST)
@GetMapping
@GetMapping("users")
等价于@RequestMapping(value="/users",method=RequestMethod.GET)
7. spring-mvc.xml配置文件的分析
7.1注解扫描
<context:component-scan base-package=“com.itheima.controller"/>
表示将controller包下面的所有标记@Controller的类装载进springMVC容器。
7.2 视图解析器
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
表示将handler返回的ModelAndView进行解析。例如返回字符串"index"后,会在前边加上/WEB-INF/views/ 后面加上.jsp。
7.3 注解驱动
<mvc:annotation-driven/>
在springmvc.xml里配置后,会自动加载RequestMappingHandlerMapping(处理器映射器)和RequestMappingHandlerAdapter(处理器适配器)。但不配置也会加载默认的处理器映射器和处理器适配器。
还有一个作用是配置后底层就会集成jackson进行对象或集合的json格式字符串的转换。
7.4 开通静态资源访问
<!--开通静态资源的访问-->
<mvc:default-servlet-handler />
这个标签的意思是当请求的url经过前端控制器来到HandlerMapping后,如果找不到Controller对应的RequestMapping,就交由默认的Tomcat的servelt来处理寻找默认资源。
7.5 文件上传
<!--配置文件上传解析器-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
//设置编码和上传文件的大小
<property name="defaultEncoding" value="UTF-8"/>
<property name="maxUploadSize" value="500000"/>
</bean>
详见11.2 单文件上传
8. web.xml配置文件的分析(补充)
8.1 中文过滤器
<!--中文过滤器-->
<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>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
8.2 spring的配置文件和监听
<!-- spring的配置文件 指定为classpath下的applicationContext.xml-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- spring的监听-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
- 主要的作用是通过spring配置的监听器自动加载配置文件,帮我们创建一个IOC容器。
- 详见1.2 如何利用spring的监听器来获取IOC
8.3 SpringMVC的配置文件
<!-- spring mvc核心:分发servlet -->
<servlet>
<servlet-name>mvc-dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- spring mvc的配置文件 为classpath下的springMVC.xml-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springMVC.xml</param-value>
</init-param>
<!--表示容器在启动时立即加载servlet -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<!-- 处理所有的url,即所有的请求都要经过DispatcherServlet-->
<servlet-name>mvc-dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
- 主要作用是在根据spring-mvc.xml配置文件在DispatcherServlet里创建一个springMVC容器,然后完成初始化。
- 可以在springmvc.xml里开通静态资源的访问,这样在访问静态资源时,前端控制器会放行相应的url。
9.springMVC的数据响应
9.1 springMVC返回String和ModelAndView的区别
- 这两种方法来自不同的Spring版本。ModelAndView方法是Spring2.0之前从控制器返回模型和视图的主要方法。现在可以结合Model参数和String返回值,但是旧的方法任然有效。原理上他们是一样的,根据习惯不同,选择哪个都行。
9.2 直接返回字符串String-页面跳转
- 转发:转发的地址栏路径不变,是一次请求,可以使用request对象共享数据。只能访问当前服务器下的资源。
- 重定向:地址栏路径发生变化,是两次请求,不能使用request对象来共享数据。可以访问其他服务器的资源。
9.3 通过ModelAndView对象返回-页面跳转
形式一:在Controller中方法返回ModelAndView对象,并设置视图名称:
@RequestMapping("/quick1")
public ModelAndView save1(){
//Model:模型,作用是封装数据
//View:视图,作用是展示数据
//创建一个ModelAndView对象
ModelAndView modelAndView = new ModelAndView();
//设置模型数据
modelAndView.addobject("username","itcast");
//设置视图名称
modelAndView.setViewName("success");
return modelAndView;
}
返回的modelAndView会被视图解析器解析,将setViewName(“success”)中的逻辑视图success转换为物理视图xx/xx/success.jsp,在这个页面可以用EL标签取出模型数据中的值,${“username”}=itcast
补充:EL表达式:
EL表达式的作用是替换和简化jsp页面中java代码的编写。jsp默认支持el表达式,如果要忽略可以在jsp的page指令中<%@ page isELIgnored="false" %>
设置为true
形式二:在Controller中方法形参上直接声明ModelAndView,无需在方法中自己创建,在方法中直接使用该对象设置视图,同样可以跳转页面。
@RequestMapping("/qiuck2")
public ModelAndView save2(ModelAndView modelAndView){
modelAndView.addObject("username","itheima");
modelAndView.setViewName("success");
return modelAndView;
}
当SpringMVC调用相应的方法时,会根据方法中对象参数的类型自动创建相应的对象,然后调用方法。这个过程是SpringMVC自动完成的。
形式三:使用Model和字符串结合的方式
@RequestMapping("/qiuck3")
public String save3(Model model){
model.addAttribute("username","itheima");
return "success";
}
注意:Model和ModelAndView的区别是 Model只是用来传数据的,ModelAndView可以传数据并进行业务寻址,即返回到指定的静态文件(jsp)。
同样在success.jsp可以通过EL 标签${“username”}来获取值itheima。
形式四:在Controller方法的形参上可以直接使用原生的HttpServletRequest对象,只需声明即可。
@RequestMapping("/qiuck4")
public String save4(HttpServletRequest request){
request.setAttribute("username","hello");
return "success";
}
效果同上。
9.4 直接返回字符串-回写数据
形式一:将需要回写的字符串直接返回给客户端浏览器,需要加一个@ResponseBody注解告诉SpringMVC框架,方法返回的字符串不进行页面跳转而是直接在http响应体中返回,即直接显示在浏览器页面上。
@RequestMapping(value="/quick5")
@ResponseBody //告知SpringMVC框架 不进行视图跳转 直接进行数据响应
public String save5() throws IOException{
return "hello itheima";
}
会在浏览器中看到hello itheima字符串
形式二:通过SpringMVC框架注入的response对象,使用response.getWriter().print()方法回写数据。
@RequestMapping("/quick6")
pubilc void save6(HttpServletResponse response) throws IOException{
response.getWriter().print("hello itheima");
}
同样会在浏览器看到hello itheima
9.5 返回对象或集合或json格式字符串-回写数据
json的快速入门:https://mp.weixin.qq.com/s/RAqRKZJqsJ78HRrJg71R1g
形式一:直接回写json格式字符串
@RequestMapping("/quick7")
@ResponseBody
public String save7() throws IOException{
return "{\"username\":\"zhangsan\",\"age\":18}"
}
形式二:使用json转换工具返回json格式字符串
手动拼接json格式字符串的方式很麻烦,开发中往往要将复杂的java对象转换成json格式的字符串,我们可以使用json转换工具jackson进行转换,通过jackson转换json格式字符串,回写数据。
@RequestMapping("/quick8")
@ResponseBody
public String save8() throws IOEXception{
User user=new User();
user.setUsername("lisi");
user.setAge(30);
//使用json的转换工具将对象转换成json格式字符串再返回
ObjectMapper objectMapper=new ObjectMapper();
String json=objectMapper.writeValueAsString(user);
return json;
}
注意:使用json转换工具要在pom.xml里导入三个包的坐标:jsonson-core,jackson-databind,jackson-annotations
形式三:通过SpringMVC配置来返回json格式字符串
在SpringMVC.xml里配置
//配置处理器映射器,springmvc内部帮我们实现了处理器映射器,但如果要进行json的转换就要手动重新配置这个处理器映射器
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
</list>
</property>
</bean>
java代码:
@RequestMapping("/quick9")
@ResponseBody
//期望SpringMVC自动将User转换成json格式的字符串
public User save10() throws IOException {
User user = new User();
user.setUsername("lisi2");
user.setAge(32);
return user;
}
通过在springMVC.xml里配置代码,springmvc会帮助我们自动进行对象转json字符串,无需其他操作。
形式四:通过SpringMVC简化配置来返回json字符串
在springmvc.xml里配置注解驱动
<mvc:annotation-driven/>
java代码
@RequestMapping("/quick10")
@ResponseBody
//期望SpringMVC自动将User转换成json格式的字符串
public User save10() throws IOException {
User user = new User();
user.setUsername("lisi2");
user.setAge(32);
return user;
}
形式三中的配置比较繁琐,可以简化为形式四。使用<mvc:annotation-driven/>
,底层就会继承jackson进行对象或集合的json格式字符串的转换。
总结
四种形式都是一样的作用,但逐步优化,使用形式四的最便捷,也最常用
10. SpringMVC的请求获取
10.1 请求参数的类型
-
客户端请求参数的格式是:name=value&name=value……
-
类型:
基本数据类型参数
pojo类型参数
数组类型参数
集合类型参数
10.2 获得基本类型的参数
- Controller中的业务方法的参数名称要和请求参数的name一致,参数值会进行自动映射匹配,并且能够自动做类型转换。
- 自动做类型转换指的是 从String类型转其他类型。例如String转int
Get请求url : http://localhost:8080/quick10?username=zhangsan&age=12
@RequestMapping("/quick11")
@ResponseBody
public void save11(String username,int age) throws IOException{
System.out.println(username);
System.out.println(age);
}
注意:请求url中的类型名字要和方法中的参数名字一致。这样SpringMVC框架就会自动帮我们进行映射,username=zhangsan中的zhangsan就会自动赋给参数username,age=12中的12自动赋值给参数age,并进行类型转换,转换为方法中定义的参数的类型。
10.3 获得pojo/javaBean类型的参数
-
Controller中的业务方法的pojo参数的属性名与请求参数的name一致,参数值会自动映射匹配。
pojo类(pojo:简单的Java对象,就是Javabean)
import lombok.Data; @Data//生成Getter,Setter,toString public class User{ private String username; private int age; }
Controller:
@RequestMapping("/quick12") @ResponseBody public void save12(User user) throws IOException{ System.out.println(user); }
请求url:http://localhost:8080/quick12?username=zhangsan&age=12
因为请求参数中的username和age正好和Controller方法参数中的user的属性名相对应,所以会自动进行参数映射,将传入的username和age的值自动赋值给user中的username属性和age属性。这个过程SpringMVC自动完成。
10.4 获得数组类型的参数
- Controller中的业务方法数组名称与请求参数的name一致,参数值会进行自动映射匹配。
请求url:http://localhost:8080/quick13?strs=111&strs=222&strs=333
@RequestMapping("/quick13")
@ResponseBody
public void quick13(String[] strs) throws IOException{
System.out.println(Arrays.toString(strs));
}
当传入的数组数组名和参数对应时,SpringMVC会自动进行封装,将请求参数封装进数组。然后调用Arrays.tostring()方法输出
10.5 获得集合类型的参数
-
获得集合类型的参数时要将集合参数包装到一个pojo中才可以。
先定义一个提交表单form.jsp:
<from action="${pageContext.request.contextPath}/quick14" method="post"> <%--表明是第一个User对象的username,age --%> <input type="text" name="userList[0].username"><br/> <input type="text" name="userList[0].age"><br/> <input type="text" name="userList[1].username"><br/> <input type="text" name="userList[1].age"><br/> <input type="submit" value="提交"><br/> </from>
这里的userList[0].username即表示集合userList中的第一个user对象的username属性。
定义一个user类,有username,age属性。
定义一个类VO
import lombok.Data; @Data//生成Getter,Setter,toString public class VO { private List<User> userList; }
Controller类
@RequestMapping("/quick14") @ResponseBody public void save14(VO vo) throws IOException { System.out.println(vo); }
请求url:http://localhost:8080/form.jsp
然后输入表单信息后会跳转到Controller方法,将userList集合数据传给vo,然后输出:
VO{userList=[User{username=‘zhangsan’,age=18},User{username=‘lisi’,age=20}]}
10.6 获得集合类型的参数2
- 当使用Ajax提交时,可以指定contentType为json格式,那么在方法参数位置使用@RequestBody可以直接接收集合数据而无需使用pojo进行包装。
定义Ajax代码ajax.jsp
<html>
<head>
<script src="${pageContext.request.contextPath}/js/jquery-3.3.1.js"></script>
<script>
var userList = new Array();
userList.push({username:"zhangsan",age:18});
userList.push({username:"lisi",age:28});
$.ajax({
type:"POST",
url:"${pageContext.request.contextPath}/user/quick15",
data:JSON.stringify(userList),
contentType:"application/json;charset=utf-8"
});
</script>
</head>
<body></body>
</html>
Controller:
@RequestMapping("/quick15")
@ResponseBody
public void save15(@RequestBody List<User> userList) throws IOException {
System.out.println(userList);
}
分析:请求Url:http://localhost/ajax.jsp ,这个url会提交三次请求向服务器,第一次访问ajax,jsp,第二次访问/js/jquery-3.3.1.js,第三次访问要跳转的quick15。在访问quick15时,@RequestBody这个注解会将传入的json格式的数据转为javaBean,然后userList获取后输出。
结果:[User{username=‘zhangsan’,age=18}, User{username=‘lisi’,age=28}]
@RequestBody 的作用:作用在形参列表上,用于将前台发送来的固定格式的数据(json或者xml)封装为对应的javaBean对象user,封装时用到的一个对象是SpringMVC默认配置的HttpMessageConverter进行解析,然后封装在形参userList ,然后输出userList即得json转为javaBean的字符串。
@RequestBody注解常用来处理content-type不是默认的application/x-www-form-urlcoded编码的内容,比如说:application/json或者是application/xml等。一般情况下来说常用其来处理application/json类型。同时使用@RequestBody时要选择post方式,get方式返回为null
10.7 上面出现的问题
-
当我们访问ajax.jsp页面时,通过谷歌抓包工具发现,没有加载到jQuery文件,原因是SpringMVC的前端控制器DispatcherServlet的url-pattern配置的是 / ,因此所有的url请求都会经过前端控制器DispatcherServlet,然后交由HandlerMapping处理,但对jsp页面的访问并不适应于 专门处理用户请求对某个Controller访问的HandlerMapping,因此我们不应该让DispatcherServlet来管理访问静态资源的请求url,要放行静态资源
-
放行静态资源的方式:在Spring-mvc.xml里配置要放行的静态资源的位置
<mvc:resource mapping="/js/**" location="/js/"/>
或者使用如下标签:
<mvc:default-servlet-handler/>
这个标签的意思是当请求的url经过前端控制器来到HandlerMapping后,如果找不到Controller对应的RequestMapping,就交由默认的Tomcat的servelt来处理寻找默认资源。
10.8 配置全局乱码过滤器
- 当我们进行请求时,如果参数中有中文可能会出现数据乱码。我们可以在web.xml配置文件里设置一个过滤器来进行编码的过滤。
<!--中文过滤器-->
<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>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
10.9 获得的参数与形参不匹配
参数绑定注解@RequestParam
- 之前我们获得基本类型,pojo类型,数组类型的参数时都是要请求参数和Controller参数方法一一对应,然后SpringMVC才能帮我们完成映射。
- 如果请求的参数名称与Controller的业务方法参数名称不一致时,就要通过@RequestParam注解来进行参数的绑定,完成http请求参数与Controller方法中形参的映射。
<form action="${pageContext.request.contextPath}/quick16" method="post">
<input type="text" name="name"><br>
<input type="submit" value="提交"><br>
</form>
@RequestMapping(value="/quick16")
@ResponseBody
public void save16(@RequestParam(value="name") String username) throws IOException {
System.out.println(username);
}
-
上面的代码将请求参数name与Controller方法的形参username进行映射,同时有必要的话会进行数据类型转换。这些工作都是由HandlerAdapter完成的。
-
此外注解@RequestParam还有一些其他参数可以使用。
@RequestParam(value="name",required=false,defaultValue="hello")
value:与请求参数的名称对应,将请求参数的值赋给它所修饰的形参
required:默认不写时为true,表示请求中的请求参数不能为空。可以设置为false,表示请求参数为空。
defaultValue:当required为false时,即请求参数为空时,可以给请求参数一个默认值hello。
10.10 获得Restful风格的参数
-
什么是Restful:Restful是一种软件架构风格,设计风格,而不是标准,只是提供了一组设计原则和约束条件。主要用于客户端和服务器交互的软件,基于这个风格,设计的软件可以更加简洁,更有层次,更易于实现缓存机制等。
-
Restful风格的请求是使用"url+请求方式"表示一次请求目的,http协议里面有四个表示操作方法的动词。
GET:用于获取资源
POST:用于新建资源
PUT:用于更新资源
DELETE:用于删除资源
例如:
/user/1 GET:得到id=1的user
/user/1 DELETE: 删除 id = 1 的 user
/user/1 PUT: 更新 id = 1 的 user
/user POST: 新增 user
-
传统类型与Restful风格的url:
传统类型:http://localhost:8080/user?name=zhangsan&id=10
Restful:http://localhost:8080/user/zhangsan/10
这里要注意在浏览器中访问url时,大部分操作都是GET类型的。
-
在SpringMVC中可以使用占位符进行参数绑定/quick17/zhangsan可以写成/quick17/{name},占位符{name}对应的值就是zhangsan, 在业务方法中可以通过使用@PathVariable注解进行占位符的匹配获取
url:http://localhost:8080/quick17/zhangsan
@RequestMapping("/quick17/{name}")
@ResponseBody
public void save17(@PathVariable(value="name") String username) throws IOException {
System.out.println(username);
}
- 注意这里value="/quick17/{name}中的name和@PathVariable(value=“name”)中的name要匹配,后面的username形式可以变。
- 通过 @PathVariable 注解完成请求参数与形参的映射。
10.11 自定义数据类型转换器
- 数据类型转换器是指将客户端http请求中的参数转换为业务方法中定义的形参。SpringMVC默认提供了一些常用的类型转换,例如String转int,String转double,表单数据的封装等。这些是由HandlerAdapter处理器适配器完成的。
- 如果我们需要的数据类型HandlerAdapter处理器适配器不能转换,这时我们需要自定义一个数据类型装换器,来达到目的。
自定义一个将"2021-4-16"的String类型的数据转换成Date类型的类型转换器。
步骤:
-
定义转换器类实现Converter接口
-
在spring-mvc.xml配置文件中声明转换器,在
<annotation-driven>
中引用转换器 -
Controller包中的方法测试
实现:
- 定义转换器类DateConverter,实现Converter接口
public class DateConverter implements Converter<String,Date>{
public Date convert(String dateStr){
//将字符串转换为Date对象
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
Date date=null;
try{
date=format.prase(dateStr);
}catch(ParseException e){
e.printStackTrace();
}
return date;
}
}
- 在Springmvc.xml配置文件里声明转换器,在
<annotation-driven>
中引用转换器
//声明转换器
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="xxx.xxx.DateConverter"></bean>
//这里是自定义的转换器类DateConverter所在的全类名
</list>
</property>
</bean>
//配置注解驱动,注意要将上面bean里的id加入下面的conversion-service里
<mvc:annotation-driven conversion-service="conversionService">
- Controller包中的方法测试
@RequestMapping("/quick18")
@ResponseBody
public void save18(Date date) throws IOException{
System.out.println(date);
}
请求url:http://localhost:8080/quick18&date=2021-04-16
10.12 获得请求头中的信息
- 在浏览器地址栏输入url后,浏览器会解析url,首先会根据请求方式的不同(GET,POST等)生成不同的http请求消息。
- 补充一下HTTP的相关知识:
GET请求消息格式:
请求行:GET /url HTTP/1.1
请求头:客户端浏览器告诉服务器的一些信息。例如日期,客户端支持的语言,数据类型,压缩格式等。格式:请求头名称:请求头值。
请求空行:告诉服务器请求头已经结束。
POST请求消息格式:
请求行:POST /url HTTP/1.1
请求头:客户端浏览器告诉服务器的一些信息
请求空行:告诉服务器请求头已经结束。
请求体:封装post请求消息的请求参数的。
二者的区别:
**GET:**请求参数在请求行,在url后。请求的url长度有限制,且不太安全。
**POST:**请求参数在请求体中,请求的url长度没有限制,相对安全。
- 使用@RequestHeader可以获得请求头信息,相当于web阶段学习的request.getHeader(name)。
@RequestMapping("/quick19")
@ResponseBody
public void save19(@RequestHeader(value="User-Agent",required=false) String user_agent) throws IOException{
System.out.println(user_agent);
}
@RequestHeader的属性:
value:请求头的名称
required:是否必须携带此请求头
- 使用@RequestHeader注解也可以获取Cookie的值,但有一个更简便的获取Cookie的注解:@CookieValue
@RequestMapping("/quick20")
@ResponseBody
public void save20(@CookieValue(value = "JSESSIONID") String jsessionId) throws IOException {
System.out.println(jsessionId);
}
@CookieValue注解的属性如下:
value:指定cookie的名称
required:是否必须携带此cookie
10.13 获取Servlet的相关api
SpringMVC支持使用原始ServletAPI对象作为控制器方法的参数进行注入,常用的对象如下:
HttpServletRequest
HttpServletResponse
HttpSession
@RequestMapping(value="/quick21")
@ResponseBody
public void save21(HttpServletRequest request, HttpServletResponse response, HttpSession session) throws IOException {
System.out.println(request);
System.out.println(response);
System.out.println(session);
}
11. SpringMVC文件上传
11.1 客户端表单的三要素
- 表单项:type=“file”
- 表单的提交方式:post
- 表单的enctype属性是多部分表单形式:enctype=“multipart/form-data”。如果不设置这个属性,只能将文件名传给服务器。
- 例如:
<form action="${pageContext.request.contextPath}/user/quick22" method="post" enctype="multipart/form-data">
名称<input type="text" name="username"><br/>
文件<input type="file" name="uploadFile"><br/>
<input type="submit" value="提交">
</form>
-
注意:文件上传的底层是使用Apache fileupload组件完成上传,SpringMVC对这种方式进行了封装。
-
当form表单修改为多部分表单时,request.getParameter()将失效
11.2 单文件上传
步骤:
- 在pom.xml里导入fileupload和io坐标
- 在sping-mvc.xml里配置文件上传解析器
- 编写表单
- 编写Handler
实现:
- pom.xml里导坐标
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.3</version>
</dependency>
- 在sping-mvc.xml里配置文件上传解析器
<!--配置文件上传解析器-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
//设置编码和上传文件的大小
<property name="defaultEncoding" value="UTF-8"/>
<property name="maxUploadSize" value="500000"/>
</bean>
- 编写表单
<form action="${pageContext.request.contextPath}/user/quick22" method="post" enctype="multipart/form-data">
名称<input type="text" name="username"><br/>
文件<input type="file" name="uploadFile"><br/>
<input type="submit" value="提交">
</form>
- 编写Handler
@RequestMapping(value="/quick22")
@ResponseBody
public void save22(String username, MultipartFile uploadFile) throws IOException {
System.out.println(username);
//获得上传文件的名称
String originalFilename=uploadFile.getOriginalFilename();
//将上传的文件转存入c盘下的upload,文件名字就是上传的文件名
uploadFile.transferTo(new File("c:\\upload\\"+originalFilename));
}
- 注意:Handler里的 MultipartFile uploadFile参数要和表单里的name属性名字一致。
11.3 多文件上传
- 多文件上传,只需将页面修改为多个文件上传项,将方法参数MultipartFile类型修改为MultipartFile[]即可。
实现:
- 在pom.xml里导坐标
- 在springmvc.xml里配置文件上传解析器
- 编写表单
<form action="${pageContext.request.contextPath}/user/quick23" method="post" enctype="multipart/form-data">
名称<input type="text" name="username"><br/>
文件1<input type="file" name="uploadFile"><br/>
文件2<input type="file" name="uploadFile"><br/>
<input type="submit" value="提交">
</form>
- 编写Handler
@RequestMapping("/quick23")
@ResponseBody
public void save23(String username, MultipartFile[] uploadFile) throws IOException {
System.out.println(username);
for (MultipartFile multipartFile : uploadFile) {
String originalFilename = multipartFile.getOriginalFilename();
multipartFile.transferTo(new File("C:\\upload\\"+originalFilename));
}
}
12. SpringMVC的拦截器
13. SpringMVC异常处理
14 补充
14.1 EL表达式相关知识
- https://how2j.cn/k/jsp/jsp-el/579.html#nowhere