您的位置:首页 > 编程语言 > Java开发

Spring MVC

2016-07-13 13:12 411 查看
Spring为展现层提供了一个优秀的Web框架--SpringMVC。和众多Web框架一样,它基于MVC设计理念,此外,它采用了松散耦合可插拔组件结构,比其他MVC框架更具扩展性和灵活性。
1、SpringMVC概述
SpringMVC框架是围绕DispatcherServlet这个核心展开的,DispatcherServlet是Spring MVC的总策划,它负责接货请求并将其分派给相应的处理器处理。SpringMVC框架包括注解驱动控制器、请求及响应的信息处理、视图解析、本地化解析、上传文件解析、异常处理以及表单标签绑定等内容。
1.1、体系结构
从接收请求到返回响应,Spring框架的众多组件通力合作、各司其职,有条不紊地完成分内工作。在整个框架中,DispatcherServlet处于核心位置,它负责协调和组织不同组件以完成请求处理并返回响应的工作。和大多数WebMVC框架一样,SpringMVC通过一个前端Servlet接收所有请求,并将具体工作委托给其他组件进行处理,DispatcherServlet就是SpringMVC的前端Servlet。SpringMVC处理请求的整体过程如下。
整个过程始于客户端发出一个HTTP请求,Web应用服务器接收到这个请求,如果匹配DispatcherServlet的请求映射路径(在web.xml中指定),Web容器就将该请求转交给DispatcherServlet处理。

DispatcherServlet接收到这个请求后,将根据请求的信息(包括URL、HTTP方法、请求报文头、请求参数、Cookie等)以及HandlerMapping的配置找到处理请求的处理器Handler。

当DispatcherServlet根据HandlerMapping得到对应当前请求的Handler后,通过HandlerAdapter对Handler进行封装,再以统一的适配器接口调用Handler。HandlerAdapter是SpringMVC的框架级接口,它用统一的接口对各种handler方法进行调用。

处理器完成业务逻辑处理的处理后将返回一个ModelAndView给DispatcherServlet,ModelAndView包含了视图逻辑名和模型数据信息。

ModelAndView中包含的是“逻辑视图名”而非真正的视图对象,DispatcherServlet借由ViewResolver完成逻辑视图名到真正视图对象的解析工作。

当得到真正视图对象View后,DispatcherServlet就使用这个View对象对ModelAndView中的模型数据进行视图渲染

最终客户端得到响应消息可能是一个普通的HTML页面,也可能是一个XML或JSON串等不同的媒体形式。

1.2、配置DispatcherServlet
和任何Servlet一样,用户必须在web.xml文件中配置好DispatcherServlet。要了解Spring MVC框架的工作原理,必须回答一下三个问题
DispatcherServlet框架如何截获特定的HTTP请求,交由SpringMVC框架处理的?

位于Web层的Spring容器(WebApplicationContext)如何与位于业务层的Spring容器(ApplicationContext)建立关联,以使Web层的Bean可以调用业务层的Bean

如何初始化SpringMVC的各个组件,并将它们装配到DispatcherServlet中?

1.2.1、配置DispatcherServlet,截获特定的URL
我们可以在web.xml中配置一个Servlet,并通过<servlet-mapping>指定其处理的URL。
<?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"> <display-name></display-name>
<!-- 使用Spring提供的日志配置方法 -->
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>/WEB-INF/classes/log4j.properties</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<context-param>
<param-name>log4jRefreshInterval</param-name>
<param-value>3000</param-value>
</context-param>
<context-param>
<param-name>webAppRootKey</param-name>
<param-value>onlineEdu.root</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>springDispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springDispatcher</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>

<filter>
<filter-name>encodingFilter</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>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<welcome-file-list>
<welcome-file>login.jsp</welcome-file>
</welcome-file-list>
</web-app>
通过contextConfigLocation参数指定业务层Spring容器的配置文件(多个文件用逗号隔开),ContextLoaderListener是一个ServletContextListener,它通过contextConfigLocation参数所指定的Spring配置文件启动“业务层”的Spring容器
一个web.xml可以配置多个DispatcherServlet
2、注解驱动的控制器
2.1、使用@RequestMapping映射请求
在POJO类定义处标注@Controller,再通过<context:componect-scan/>扫描相对应的类包,即可使POJO成为一个能处理HTTP请求的控制器。可以创建数量不限的控制器,分别处理不同的业务请求。每个控制器可以有多个处理请求的方法,每个方法负责不同的请求操作。如何将请求映射到对应的控制器方法中是Spring MvC框架最重要的任务之一,这项任务由@RequestMapping承担。
在控制器的类定义及方法定义处都可标注@RequestMapping,类定义出的@RequestMapping提供初步的请求映射信息,方法处的@RequestMapping提供进一步的细分映射信息。
@RequestMapping除了可以使用URL映射请求外,还可以使用请求方法、请求头参数以及请求参数映射请求。@RequestParam("userid") -- 获取请求参数userid的值
package com.zzia.controller.admin;
...//省略import
@Controller
@RequestMapping(value="/admin")
public class AdminController implements Serializable {

private static final long serialVersionUID = 1L;
private static Logger logger=Logger.getLogger(AdminController.class);
@Autowired
private IAdminService adminService;

@Autowired
private ILoginLogService loginLogService;

//管理员登陆方法
@RequestMapping("/login")
public String login(Admin admin,HttpServletRequest request,HttpSession session){
logger.warn(admin.getAdminName()+"试图登陆");
...
}
//更新管理员信息方法
@RequestMapping("/updateAdmin")
public String updateAdminInfo(Admin admin,@RequestParam("img") CommonsMultipartFile file,HttpServletRequest request){
logger.info("更新"+admin.getAdminName()+"的信息");
...
}
//得到管理员的更新信息
@RequestMapping("/getAdminInfo")
public String getAdminInfo(int adminId,HttpServletRequest request){
logger.info("根据Id得到管理员详细信息"+adminId);
...
}
//退出登录的方法
@RequestMapping("/loginOut")
public String loginOut(HttpSession session){
logger.info(((Admin)session.getAttribute("adminInfo")).getAdminName()+"退出登录");
...
}
}
2.2、处理模型数据
对于MVC框架来说,模型数据是最重要的。Spring提供了以下几个途径将模型数据输出给视图。
ModelAndView:处理方法返回值类型为ModelAndView时,方法体即可通过该对象添加模型数据。

@ModelAttrbute:方法入参标注该注解后,入参的对象就会放到数据模型中

Map及Model:入参为Model和ModelMap或者Map时,处理方法返回时,Map中的数据会自动添加到模型中

@SessionAttributes:将模型中的某个属性暂存到HttpSession中,以便多个请求之间可以共享这个属性

2.3、数据校验
应用程序在执行业务逻辑前,必须通过数据校验保证接收到的输入数据是正确合法的。为了避免数据的冗余校验,将验证逻辑和相应的域模型进行绑定,将代码验证的逻辑集中起来管理。
2.3.1、Spring校验框架
Spring3.0拥有自己独立的数据校验框架,同事支持JSR 303标准的校验框架。Spring的DataBinder在进行数据绑定时,可同时调用校验框架完成数据校验工作。在SpringMVC中,可直接通过注解驱动的方式进行数据校验。
Spring的org.springframework.validation是校验框架所在的包,Validator接口拥有以下两个方法:
boolean supports(Class<?> clazz):该校验器能够对clazz类型的对象进行校验

void validate(Object target,Errors erros):对目标类target进行校验,并将校验错误记录在errors中

LocalValidatorFactoryBean既实现了Spring的Validator接口,也实现了JSR 303的Validator接口路,只要再Spring容器中定义一个LocalValidatorFactoryBean,即可将其注入需要数据校验的Bean中。定义一个LocalValidatorFactoryBean非常简单
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
注意:Spring本身没有提供JSR 303的实现,所以必须将JSR 303的实现者(如Hibernate Validator)的jar文件放到类路径下,Spring将自动加载并装配好JSR 303的实现者。<mvc:annotaion-driver/>会默认装配好一个LocalValidatorFactoryBean,通过在处理方法的入参上标注@Valid注解即可让SpringMVC在完成数据绑定后执行数据校验工作。
2.3.2、如何获得校验结果
只要再表单/命令对象类中标注校验注解,在处理方法对应的入参前添加@Valid,springMVC就会实施校验并将校验结果保存在被校验入参对象之后的BindingResult或Error入参中。在处理方法内部可以通过BindingResult或Errors入参对象获取错误信息。例如通过BindingResult对象的hashErrors()方法判断入参对象是否存在校验错误。
package org.worm.biz.springmvc.controller.hibernate;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.worm.biz.springmvc.dao.User;
import org.worm.biz.springmvc.service.hibernate.IUserService;
/**
* @ClassName: UserController
* @Description: TODO 用户操作控制器
* @author Administrator
* @date 2016年7月13日 上午10:12:44
*
*/
@Controller
@RequestMapping(value="/user")
public class UserController {
@Autowired
private IUserService userService;

@RequestMapping("/valid")
public String handleValid(@Valid @ModelAttribute("user") User user,BindingResult bindResult){
if(bindResult.hasErrors()){
return "/user/register";
}else{
userService.addEntity(user);
return "/user/showUser";
}
}

}

//对User进行校验
package org.worm.biz.springmvc.dao;
import javax.persistence.*;
import javax.validation.constraints.Pattern;

@Entity
//@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
@Table(name = "t_user")
public class User{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "user_no")
@Pattern(regexp = "w{4,30}")
//通过正则表达式进行校验,匹配4~30个数字和字母以及下划线
protected int userId;
@Column(name = "user_nick_name")
protected String userName;
protected String password;

@Column(name = "user_age")
protected String userAge;

public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getUserAge() {
return userAge;
}
public void setUserAge(String userAge) {
this.userAge = userAge;
}

}
3、视图和视图解析器
在请求处理方法执行完成后,最终返回一个ModelAndView对象,对应那些返回String、View或者ModelMap等类型的处理方法,Spring MVC内部也会在将它们装配成一个ModelAndView对象,它包含了视图逻辑名和模型对象的信息。Spring MVC借助视图解析器(ViewResolver)得到最终的视图对象(View),这可能是常见的JSP视图,也可能是一个基于FreeMarker、Velocity模板技术的视图,还可能是PDF、Excel、XML、JSON等各种形式的视图。
3.1、认识视图
视图的作用:渲染视图模型数据,将模型里的数据以某种形式呈现给客户。Spring提供了一个高度抽象的View接口。该接口中定义了两个方法
String getContentType():视图对应的MIME类型,如text/html、imge/jpeg等

void render(Map model,HttpServletRequest request,HttpServletResponse response):将模型数据以某种MIME类型渲染出来

视图类型



3.2、认识视图解析器
SpringMVC为逻辑视图名的解析提供了不同的策略,可以在Spring Web上下文中配置一种或多种解析策略,并指定他们之间的先后顺序。视图解析器的工作比较单一:将逻辑视图名解析为一个具体的视图对象。解析器都实现了ViewResolver接口,该接口仅有一个方法View resolverViewName(String viewName,Locale locale);
3.3、JSP和JSTL
JSP是最常见的视图技术,InternalResourceViewResolver默认使用InternalResourceView作为视图实现类。如果JSP文件使用了JSTL的国际化功能,也即JSP页面使用了JSTL的<fmt:message>等标签是,用户需要使用JstlView替换默认的视图实现类。
使用Spring MVC表单标签,可以很容易地将模型数据中的表单/命令对象绑定到HTML表单元素中。和使用任何JSP扩展标签一样,在使用Spring表单标签之前,必须先在JSP页面中添加引用
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<html>
<!-- ...-->
<!-- 使用<form:form/>标签实例,无须通过action属性指定表单提交的目标url -->
<form:form modelAttributes="user">
user:<form:input path="userName"/><br/>
password:<form:password path="password"/><br/>
<input type="submit" value="登陆" name="testSubmit"/>
<input type="rest" value="重置"/>
</form:form>
</html>
3.4、模板视图
FreeMarker和Velocity是除JSP外使用最多的页面模板技术。页面模板编写好页面结构,并使用一些特殊的变量标识符绑定Java对象的动态数据。Spring对FreeMarker和Velocity都提供了支持。由于我对它没什么兴趣,有兴趣的童鞋可自行学习。
3.5、文件上传
SpringMVC为文件上传提供了直接的支持,这种支持是通过即插即用的MultipartResolver实现的。Spring使用Jakarta Commons FileUpload技术实现了一个MultipartResolver实现类:CommonsMultipartResolver
3.5.1、配置MultipartResolver
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="utf-8" />
<!-- 设置文件上传的最大尺寸 -->
<property name="maxUploadSize" value="10485760000" />
<property name="maxInMemorySize" value="40960" />
<property name="uploadTempDir" value="upload/temp"/> <!-- 上传文件的临时路径-->
</bean>
3.5.2、编写控制器和文件上传表单页面
package com.zzia.controller.admin;
...
@Controller
@RequestMapping(value="/admin")
public class AdminController {

private static Logger logger=Logger.getLogger(AdminController.class);
@Autowired
private IAdminService adminService;

@Autowired
private ILoginLogService loginLogService;

//更新管理员信息方法
@RequestMapping("/updateAdmin")
public String updateAdminInfo(Admin admin,@RequestParam("img") CommonsMultipartFile file,HttpServletRequest request){
logger.info("更新"+admin.getAdminName()+"的信息");
String updateResult="更新失败";
if(!file.isEmpty()){
String type=file.getOriginalFilename().substring(file.getOriginalFilename().indexOf("."));//读取文件后缀
String fileName=System.currentTimeMillis()+type;//取当前时间戳为文件名
String path=request.getSession().getServletContext().getRealPath("/")+"upload/admin/"+fileName;
//System.out.println(path);
File destFile = new File(path);
try {
FileUtils.copyInputStreamToFile(file.getInputStream(), destFile);//复制临时文件到指定目录下
} catch (IOException e) {
logger.debug("文件上传异常");
}
admin.setAdminHead("upload/admin/"+fileName);
}
if(adminService.updateAdminInfo(admin)>0){
updateResult="更新成功";
logger.info("更新成功");
}
request.setAttribute("updateResult", updateResult);
return "/admin/getAdminInfo.do?adminId="+admin.getAdminId();
}
}
SpringMVC会将上传文件绑定到MultipartFile对象中,MultipartFile提供了获取上传文件内容、文件名等内容,通过其transferTo()方法还可将文件存储到硬盘中,具体说明如下:
byte[] getBytes():获取文件数据

String getContentType():获取文件MIME类型,如image/pjpeg,text/plain等

InputStream getInputStream():获取文件流

String getName():获取表单中文件组件的名称

String getOriginalFileName():获取上传文件的原名

long getSize():获取文件的字节大小,单位为byte

boolean isEmpty():是否有文件上传

void transferTo(File dest):可以使用该文件将上传文件保存到一个目标文件中

负责上传文件的表单页面和一般表单有一些区别,表单的编码类型必须是”multipart/form-data“
<form action="admin/updateAdmin.do" method="post" enctype="multipart/form-data">
<ul class="forminfo">
<li><label>管理员编号</label><input name="adminId" type="text" readonly="readonly" class="dfinput" value="${admin.adminId}" /></li>
<li><label>管理员名称</label><input name="adminName" type="text" class="dfinput" value="${admin.adminName }" /></li>
<li><label>管理员旧密码</label><input  type="password" class="dfinput" value="${admin.adminPassword }"/></li>
<li><label>管理员新密码</label><input name="adminPassword" type="password" class="dfinput" /></li>
<li><label>更新头像</label><input type="file" name="img"/>
<a href="javascript:void(0);" onclick="yulan('${admin.adminId}')" class="infolist" style="display: block;float: left;position: relative;left:200px;">预览</a>
</li>
</ul>
<div id="adminHead">
<img alt="管理员头像" src="${admin.adminHead }" style="text-align: center;vertical-align: middle;">
</div>
<input style="margin-left: 150px;" type="submit" class="btn" value="确认保存"/>
</form>


本文出自 “阿酷博客源” 博客,请务必保留此出处http://aku28907.blog.51cto.com/5668513/1826012
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: