WEB及远程操作
1.Spring MVC总结
1.1 Spring MVC 框架请求处理流程
(1)整个过程始于客户端发出一个HTTP请求,Web应用服务器接收到这个请求。如果匹配DispatcherServlet的请求映射路径(在web.xml中指定),则Web容器将该请求转交给DispatcherServlet处理。
(2) DispatcherServlet 接收到这个请求后,将根据请求的信息(包括URL、HTTP方法、请求报文头、请求参数、Cookie 等)及HandlerMapping的配置找到处理请求的处理器(Handler)。 可将HandlerMapping看作路由控制器,将Handler看作目标主机。
值得注意的是,在Spring MVC中并没有定义-一个Handler接口,实际上,任何一个Object都可以成为请求处理器。
(3) 当DispatcherServlet根据HandlerMapping得到对应当前请求的Handler后,通过HandlerAdapter对Handler 进行封装,再以统一的适配器接口调用Handler。HandlerAdapter是Spring MVC的框架级接口,顾名思义,HandlerAdapter是一个适配器,它用统一的接口对各种Handler方法进行调用。
(4)处理器完成业务逻辑的处理后将返回一个ModelAndView给DispatcherServlet,ModelAndView包含了视图逻辑名和模型数据信息。
(5 ) ModelAndView 中包含的是“逻辑视图名”而非真正的视图对象,DispatcherServlet借由ViewResolver完成逻辑视图名到真实视图对象的解析工作。
(6)当得到真实的视图对象View 后,DispatcherServlet 就使用这个View 对象对ModelAndView中的模型数据进行视图渲染。
(7)最终客户端得到的响应消息可能是-一个普通的HTML页面,也可能是一个XML或JSON串,甚至是一-张图片或-一个PDF文档等不同的媒体形式。
1.2 Spring MVC框架包含内容
注解驱动控制器、请求及响应的信息处理、视图解析、本地化解析、上传文件解析、异常处理及表单标签绑定等内容。
Spring MVC定义了一套默认的组件实现类,即使在Spring容器中没有显式定义组件Bean, DispatcherServlet也会装配好一套可用的默认组件。在spring webmvc-4.x.jar包的org/springframework/web/servlet 类路径下拥有一个DispatcherServlet.properties配置文件,该文件指定了DispatcherServlet 所使用的默认组件。如视图解析器、异常处理器等。
1.3 Spring MVC 核心
1.3.1 DispatcherServlet
DispatcherServlet是Spring MVC的“灵魂”和“心脏”,它负责接收HTTP请求并协调Spring MVC的各个组件完成请求处理的工作。和任何Servlet 一样,用户必须在web.xml中配置好DispatcherServlet。
1.3.2 WebApplicationContext与ApplicationContext
1.3.2.1 BeanFactory
BeanFactory:Spring的IoC容器。是Spring创建并管理各类的对象的类工厂,是Spring框架的核心。
Spring通过一个配置文件描述Bean及Bean之间的依赖关系,利用Java语言的反射功能实例化Bean并建立Bean之间的依赖关系。Spring的IoC容器在完成这些底层工作的基础上,还提供了Bean实例缓存、生命周期管理、Bean 实例代理、事件发布、资源装载等高级服务。
1.3.2.2 ApplicationContext
ApplicationContext:应用上下文。应用上下文(com.springframework.context.ApplicationContext) 建立在BeanFactory基础之上,提供了更多面向应用的功能,它提供了国际化支持和框架事件体系,更易于创建实际应用。我们一般称BeanFactory为loC容器,而称ApplicationContext为应用上
下文。但有时为了行文方便,我们也将ApplicationContext称为Spring容器。
对于二者的用途,我们可以进行简单的划分: BeanFactory 是Spring框架的基础设施,面向Spring本身; ApplicationContext 面向使用Spring框架的开发者,几乎所有的应用场合都可以直接使用ApplicationContext而非底层的BeanFactory。
ApplicationContext的主要实现类是ClassPathXmlApplicationContext 和FileSystemXmlApplicationContext,前者默认从类路径加载配置文件,后者默认从文件系统中装载配置文件。
1.3.2.3 WebApplicationContext
WebApplicationContext是专门为Web应用准备的,它允许从相对于Web根目录的路径中装载配置文件完成初始化工作。从WebApplicationContext中可以获得ServletContext的引用,整个Web应用上下文对象将作为属性放置到ServletContext中,以便Web应用环境可以访问Spring 应用上下文。Spring 专门为此提供了一个工具类WebApplicationContextUtils,通过该类的getWebApplicationContext(ServletContext sc)方法,可以从ServletContext中获取WebApplicationContext实例。
在非Web应用的环境下,Bean只有singleton和prototype两种作用域。WebApplicationContext为Bean添加了三个新的作用域: request、ssion和global session。
WebApplicationContext的初始化方式和BeanFactory、ApplicationContext 有所区别,因为WebApplicationContext需要ServletContext实例,也就是说,它必须在拥有Web容器的前提下才能完成启动工作。通过在web.xml中配置自启动的Servlet 或定义Web容器监听器( ServletContextListener),借助二者中的任何一个,就可以完成启动Spring Web应用上下文的工作。
1.4 Spring MVC开发流程
Spring MVC应用开发一般包括以下几个步骤。
(1)配置web.xml, 指定业务层对应的Spring配置文件,定义DispatcherServlet。
(2)编写处理请求的控制器(处理器)。
(3)编写视图对象,如JSP、HTML文件等。
(4)配置Spring MVC的配置文件,使控制器、视图解析器等生效。
1.5 驱动注解控制器
@Controller
在POJO类定义处标注@Controller,再通过context:component-scan/扫描相应的类包,即可使POJO成为一一个能处理HTTP请求的控制器。
@RequestMapping
在控制器的类定义及方法定义处都可以标注@RequestMapping,类定义处的@RequestMapping提供初步的请求映射信息,方法定义处的@RequestMapping提供进一
步的细分映射信息。DispatcherServlet 截获请求后,就通过控制器上@RequestMapping提供的映射信息确定诸求所对应的外理方法。
@RequestMapping不但支持标准的URL,还支持Ant 风格(?、*和**字符,和带{xxx}占位符的URL。如
- /user/*/createUser: 匹配/user/a/createUser. /user/bbb/createUser 等URL。
- /user/**/createUser:匹配/user/createUser、 /user/abbb/createUser 等URL。
- /user/createUser??: 匹配/user/createUseraa、 /user/createUserbb 等URL。
- /user/ {userld}:匹配user/123、user/456 等URL。
- /user/**/{userld}:匹配userlaabbb/123、user/aa/456 等URL。
- company/{companyld}/user/ {userId}/detail:匹配company/123/user/456/detail 等
URL。
通过@PathVariable可以将URL中的占位符参数绑定到控制器处理方法的入参中。
@RequestMapping的value、method、 params 及headers 分别表示请求URL、请求方法、请求参数及报文头的映射条件,它们之间是与的关系,联合使用多个条件项可让请求映射更加精确化。
params和headers分别通过请求参数及报文头属性进行映射,它们支持简单的映射
表达式。下面以 params表达式为例进行说明,headers 可以参照params来理解。
- “paraml”: 表示请求须包含名为paraml的请求参数。
- “!paraml”: 表示请求不能包含名为paraml的请求参数。
- “paraml!=valuel”: 表示请求包含名为paraml的请求参数,但其值不能为valuel.
- {“param1=value1”,“param2”}: 表示请求必须包含名为paraml和param2的两个请求参数,且param1参数的值必须为value1.
其他请求处理方法签名注解
@RequestBody
用于方法签名中,将请求报文体转换为字符串绑定到requestBody标注的入参中。
Spring MVC根据requestBody的类型查找匹配的HttpMessageConverter,如果入参类型为String,由于StringHtpMessageConverter 的泛型类型对应String, 所以StringHtpMessageConverter将被SpringMVC选中,用它将请求体信息进行转换并将结果绑定到requestBody入参上。
@RequestMapping (path = "/hand1e41")
public String handle41(@RequestBody String requestBody){
System.out.println(requestBody);
return "success";
}
@ResponseBody
用于标注请求方法。将return返回的对象输出到客户端。当方法返回值类型为byte[],Spring MVC根据类型匹配的查找规则将使用ByteArrayHtpMessageConverter对返回值进行处理,即将byte[]内数据流输出到客户端。
@ResponseBody
@RequestMapping (path = "/handle42/{imageId}")
public byte[] handle42 (@PathVariable("imageId") String imageId) throws IOException{
System.out.println("load image of "+imageId) ;
Resource res = new ClassPathResource ("/image.jpg") ;
byte[] fileData = FileCopyUtils.copyToByteArray(res.getInputStream());
return fileData;
}
@RequestParam
用于方法签名中,绑定请求参数值。
- value: 参数名。
- required: 是否必需,默认为true, 表示请求中必须包含对应的参数名,如果不存在则抛出异常。
@RequestMapping(path = "/hand1e11")
public String handle11(
@RequestParam(value = "userName", required = false) String userName,
@RequestParam("age") int age){
...
}
@CookieValue
使用@CookieValue可让处理方法入参绑定某个Cookie的值,它和@RequestParam拥有3个- -样的参数。来看一个使用@CookieValue的实例,代码如下:
@RequestMapping (path = "/handle12")
public String handle12 (
@CookieValue (value =”sessionId",required false) String sessionId,
@RequestParam("age") int age){
...
)
@RequestHeader
@RequestHeader可将报文头属性值绑定到处理方法的入参中
@RequestMapping (path = " /handle13")
public String handle13 (@RequestHeader("Accept -Encoding") String encoding,
@RequestHeader("Keep-Alive") long keepAlive) {
....
}
使用命令/表单对象绑定请求参数值
SpringMVC会按请求参数名和命令/表单对象属性名匹配的方式,自动为该对象填充属性值。支持级联的属性名,如dept.deptld、dept.address.tel 等。
@RequestMapping (path = "/handle14")
public String handle14 (User user) {
}
使用Servlet API对象作为入参
在Spring MVC中,控制器类可以不依赖任何Servlet API对象,但是Spring MVC并不能阻止我们使用Servlet API的类作为处理方法的入参。以下处理方法都可以正确地工作。
@RequestMapping (path = "/handle21")
public void handle21 (HttpServ1etRequest request , HttpServletResponse response) {
String userName = Webutils.findParameterValue(request, "userName");
response.addCookie(new Cookie ("userName", userName));
}
使用I/0对象作为入参
Servlet的ServletRequest拥有getInputStream()和getReader(方法,可以通过它们读取请求的信息。相应的,Servlet 的ServletResponse拥有getOutputStream()和getWriter()方法,可以通过它们输出响应信息。
Spring MVC允许控制器的处理方法使用java.io.InputStream/java.io.Reader 及
java.io.OutputStream/java.io.Writer作为方法的入参,Spring MVC将获取ServletRequest
的InputStream/Reader或ServletResponse 的OutputStream/Writer,然后传递给控制器的
处理方法。
@RequestMapping (path = "/handle31")
public void handle31 (OutputStream os) throws IOException{
Resource res = new ClassPathResource ("/image. jpg");//读取类路径下的图片文件
FileCopyutils.copy (res.getInputStream(), os);//将图片写到输出流中
}
传参到视图的方法
对于MVC框架来说,模型数据是最重要的,因为控制(C)是为了产生模型数据(M),而视图(V)则是为了渲染模型数据。
Spring MVC通过@RequestMapping将请求引导到处理方法上,使用合适的方法签名将请求消息绑定到入参中,根据入参执行相应的逻辑,产生模型数据,导向到特定视图中。将模型数据暴露给视图是SpringMVC框架的一项重要工作。SpringMVC提供了多种途径输出模型数据,介绍如下。
- ModelAndView:当处理方法返回值类型为ModelAndView时,方法体即可通过该对象添加模型数据。
- @ModelAttribute:在方法入参标注该注解后,入参的对象就会放到数据模型中。
- Map及Model:如果方法入参为org. springframework.ui.Model、org.springframework.ui.ModelMap或java.util.Map,则当处理方法返回时,Map中的数据会自动添加到模型中。
- @SessionAttributes:将模型中的某个属性暂存到HttpSession中,以便多个请求
之间可以共享这个属性。
@RequestMapping("testModel1")
public ModelAndView testModel1(){
ModelAndView mv = new ModelAndView();
mv.setViewName("freemarkTest");
mv.addObject("name", "海粟");
return mv;
}
@RequestMapping("testModel2")
public String testModel2(@ModelAttribute("name") String name){
return "freemarkTest";
}
//在访问TestController中的任何一个请求处理方法前,SpringMVC先执行getUser方法,并将返回值以user为键添加到模型中
//然后,模型数据会赋给User的入参,然后再根据HTTP请求消息进一步填充覆盖user对象
@RequestMapping("testModel3")
public String testModel3(@ModelAttribute("user") HashMap user){
return "freemarkTest";
}
@ModelAttribute("user")
public HashMap getUser(){
HashMap map = new HashMap();
map.put("name", "name3");
map.put("age", "21");
return map;
}
@RequestMapping("testModel4")
public String testModel4(Map map){
map.put("name", "name4");
map.put("age", "22");
return "freemarkTest";
}
@RequestMapping("testModel5")
public String testModel5(Model model){
model.addAttribute("name", "name5");
model.addAttribute("age", "24");
return "freemarkTest";
}
@RequestMapping("testModel6")
public String testModel6(ModelMap modelMap){
modelMap.put("name", "name6");
modelMap.put("age", "23");
return "freemarkTest";
}
1.6 视图和视图解析器
请求处理方法执行完成后,最终返回-一个ModelAndView对象。对于那些返回String、View或ModelMap等类型的处理方法,SpringMVC也会在内部将它们装配成一个ModelAndView对象,该对象包含了视图逻辑名和模型对象的信息。
Spring MVC借助视图解析器( ViewResolver)得到最终的视图对象(View), 这可能是我们常见的JSP视图,也可能是-一个基于FreeMarker、Velocity 模板技术的视图,还可能是PDF、Excel、 XML、JSON等各种形式的视图。
对于最终究竟采取何种视图对象对模型数据进行渲染,处理器并不关心,处理器的工作重点聚焦在生产模型数据的工作.上,从而实现MVC的充分解耦。
1.6.1 视图
视图的作用是渲染模型数据,将模型里的数据以某种形式呈现给客户。视图对象可以是常见的JSP,还可以是Excel或PDF等形式不一的媒体形式。为了实现视图模型和具体实现技术的解耦,Spring 在org.springframework.web.servlet包中定义了一个高度抽象的View接口,不同类型的视图实现技术对应不同的View实现类,这些视图实现类都位于org.springframework.web.servlet.view包中。
视图对象是一个Bean, 通常情况下,视图对象由视图解析器负责实例化。由于视图Bean是无状态的,所以它们不会有线程安全的问题。
大类 | 视图类型 | 说明 |
---|---|---|
URL资源视图 | InternalResourceView | 将JSP或其他资源封装成一个视图,这是InternalResourceViewResolver默认使用的视图实现类 |
URL资源视图 | JstlView | 如果JSP文件中使用了JSTL国际化标签的功能,则需要使用该视图类,而非IntermalResourceView视图类 |
文档视图 | AbstractExcelView | Excel文档视图的抽象类,开发者可以通过该抽象类实现自己的Excel文档视图。该视图实现类基于POI构造Excel文档 |
文档视图 | AbstractPdfView | PDF文档视图的抽象类,可以通过该抽象类实现自己的PDF文档视图。该视图实现类基于iText构造PDF文档 |
模板视图 | FreeMarkerView | 使用FreeMarker模板引擎的视图 |
XML视图 | MarshallingView | 通过oxm的Marshaller 技术将模型数据以XML方式输出 |
JSON视图 | MappingJackson2JsonView | 将模型数据通过Jackson 开源框架的ObjectMapper以JSON方式输出 |
1.6.2 视图解析器
Spring MVC为逻辑视图名的解析提供了不同的策略,可以在Spring Web上下文中配置一种或多种解析策略,并指定它们之间的先后顺序。每种解析策略对应一个具体的视图解析器实现类。视图解析器的工作比较单一, 即将逻辑视图名解析为一个具体的视图对象。所有视图解析器都实现了ViewResolver 接口,该接口仅有一个方法。resolveViewName()方法的签名清楚地向我们传达了视图解析器工作的内涵:根据逻辑视图名和本地化对象得到一个视图对象。
View resolveViewName (String viewName, Locale locale)
用户可以选择一种视图解析器或混用多种视图解析器,每个视图解析器都实现了Ordered接口并开放出 一个orderNo属性,可以通过该属性指定解析器的优先顺序,值越小优先级越高。有些视图解析器默认为最高优先级(如ContentNegotiatingViewResolver),
而有些视图解析器默认为最低优先级(如IntermalResourceViewResolver、 XsItViewResolver
等),具体请参考API文档。
Spring MVC会按照视图解析器的优先级顺序对逻辑视图名进行解析,直到解析成功并返回视图对象,否则将抛出ServletException异常。
类 | 视图解析器类型 | 说 明 |
---|---|---|
解析为Bean名字 | BeanNameViewResolver | 将逻辑视图名解析为一个Bean, Bean 的id等于逻辑视图名和BeanNameViewResolver类似,只不过目标视图Bean对象 |
解析为Bean名字 | XmlViewResolver | 定义在一个独立的XML文件中,而非定义在DispatcherServlet上下文的主配置文件中 |
国际化解析 | ResourceBundleViewResolver | 在国际化资源文件中定义视图实现类及相关的信息。使用该视图解析器可以为不同的本地化类型提供不同的解析结果 |
解析为URL文件 | IntemalResourceViewResolver | 将视图名解析为一个URL文件,一般使用该解析器将视图名映射为保存在WEB-INF目录中的程序文件(如JSP) |
解析为URL文件 | XsItViewResolver | 将视图名解析为-一个指定XSLT样式表的URL文件 |
解析为URL文件 | JasperReportsViewResolver | JasperReports是-一个基于Java的开源报表工具,该解析器将视图名解析为报表文件所对应的URL |
模板文件视图 | FreeMarkerViewResolver | 解析为基于FreeMarker模板技术的模板文件 |
模板文件视图 | VelocityViewResolver | 解析为基于Velocity模板技术的模板文件 |
模板文件视图 | VelocityLayoutViewResolver | 解析为基于Velocity模板技术的模板文件 |
内容协商 | ContentNegotiatingViewResolver | 它不负责具体的视图解析,而是作为一个中间人的角色根据请求所要求的MIME类型,从上下文中选择一个适合的视图解析器,再将视图解析工作委托其负责 |
1.6.3 视图解析器混用示例
<?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" xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
">
<!--开启注解扫描-->
<!--开启注解扫描 无此配置也可扫描Controller和RequestMapping,但无法识别单独的统一异常处理类WebExceptionHandle-->
<mvc:annotation-driven/>
<!--指定扫描路径-->
<context:component-scan base-package="com.fxy.spring.mvc"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:prefix="/WEB-INF/jsp/" p:suffix=".jsp" p:order="100"/>
<!-- FreeMarker基础设施及视图解析器配置 -->
<bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer"
p:templateLoaderPath="/WEB-INF/ftl" p:defaultEncoding="UTF-8">
<property name="freemarkerSettings">
<props>
<prop key="classic_compatible">true</prop>
</props>
</property>
</bean>
<bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver"
p:order="5" p:suffix=".ftl" p:contentType="text/html; charset=utf-8"/>
</beans>
1.7 文件上传
Spring MVC为文件上传提供了直接支持,这种支持是通过即插即用的MultipartResolver实现的。Spring使用Jakarta Commons FileUpload技术实现了一个MultipartResolver 实现类: CommonsMultipartResolver。
在Spring MVC上”下文中默认没有装配MultipartResolver,因此默认情况下不能处理文件的上传工作。如果想使用Spring 的文件上传功能,则需要先在上下文中配置MultipartResolver。
<!-- 文件上传 -->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver"
p:defaultEncoding="UTF-8"
p:maxUploadSize="5000000"
p:uploadTempDir="upload/temp"/>
@RequestMapping(value = "/upload")
public String updateThumb(@RequestParam("name") String name,
@RequestParam("file") MultipartFile file) throws Exception{
if (!file.isEmpty()) {
file.transferTo(new File("d:/temp/"+file.getOriginalFilename()));
return "redirect:success.html";
}else{
return "redirect:fail.html";
}
}