Java框架开发技术之SpringMVC
1. SpringMVC
首先看下Spring的架构图
从Spring的架构图可以看到,Spring MVC 是Spring的一部分,Spring MVC是一个基于MVC模式开发的表现层框架(与用户打交道),它是类似于 Struts2 的一个 MVC 框架,在实际开发中,接收浏览器的请求响应,对数据进行处理,然后返回页面进行显示,但是上手难度却比 Struts2 简单多了。而且由于 Struts2 所暴露出来的安全问题,SpringMVC 已经成为了大多数企业优先选择的框架。
优点
① 性能高于Struts2;
② Spring MVC可以和Spring实现无缝集成(AOP/IOC);
③ 简单易学,容易上手;
④ 完全支持restful风格的URL;
⑤ 轻量级的框架;
⑥ 使用的人多;
⑦ 支持多种拦截器。
工作流程
一个http请求过来springMVC是如何处理的?
Spring的MVC框架主要由DispatcherServlet、处理器映射、处理器(控制器)、视图解析器、视图组成。
1 用户向服务器发送请求,请求被Spring 前端控制Servelt DispatcherServlet捕获;
2 DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI)。然后根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器,最后以HandlerExecutionChain对象的形式返回;
3 DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter。附注:如果成功获得HandlerAdapter后,此时将开始执行拦截器的preHandler(…)方法。
4 提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)。 在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:
HttpMessageConveter:将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息。
数据转换:对请求消息进行数据转换。如String转换成Integer、Double等;
数据格式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等;
数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中。
5 Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象;
6 根据返回的ModelAndView,选择一个适合的ViewResolver(必须是已经注册到Spring容器中的ViewResolver)返回给DispatcherServlet;
7 ViewResolver 结合Model和View,来渲染视图;
8 将渲染结果返回给客户端。
SpringMVC运行原理
- 客户端请求提交到DispatcherServlet
- 由DispatcherServlet控制器查询一个或多个HandlerMapping,找到处理请求的Controller
- DispatcherServlet将请求提交到Controller
- Controller调用业务逻辑处理后,返回ModelAndView
- DispatcherServlet查询一个或多个ViewResoler视图解析器,找到ModelAndView指定的视图
- 视图负责将结果显示到客户端
2.配置SpringMVC项目
2.1 在配置文件中配置
① 新建javaweb项目:mvc1
② 手动导包
③ 新建java类并实现Controller接口,添加未实现的方法处理请求
TestController.java
package com.xixw.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
/**
* 处理器
* @author Administrator
*/
public class TestController implements Controller{
@Override
public ModelAndView handleRequest(HttpServletRequest arg0,
HttpServletResponse arg1) throws Exception {
ModelAndView mv=new ModelAndView();
//添加数据
mv.addObject("msg","hello!!");
//添加页面/WEB-INF/jsp/index.jsp
mv.setViewName("index");
//返回一个ModelAndView对象
return mv;
}
}
④ 配置web.xml
配置核心控制器(DispatcherServlet)
DispatcherServlet是前置控制器,配置在web.xml文件中。作用是拦截匹配的请求,Servlet拦截匹配规则要自已定义,把拦截下来的请求,依据相应的规则分发到目标Controller来处理。
<!-- 配置核心控制器 -->
<servlet>
<servlet-name>mvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mvc</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
⑤ 配置主配置文件(在WEB-INF下面)
新建xml文件([servlet-name]-servlet.xml)
mvc-servlet.xml
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 配置handlermapping对象 -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"></bean>
<!-- 配置handleAdatper对象 -->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"></bean>
<!-- 配置视图渲染器 -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"></property>
<property name="prefix" value="/WEB-INF/jsp/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<!-- 配置处理器 -->
<bean name="/test.do" class="com.xixw.controller.TestController">
</bean>
</beans>
⑥ 编写jsp页面
/WEB-INF/jsp/index.jsp
<body>
This is my JSP page. <br>
${msg}
</body>
⑦ 将项目加入Tomcat,Run Server
运行结果
2.2 使用注解配置
TestController.class
@Controller:负责注册一个bean 到spring 上下文中。
@RequestMapping:注解为控制器指定可以处理哪些 URL 请求。
package com.xixw.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
/**
* 处理器
* @author Administrator
*/
@Controller
@RequestMapping("/test")
public class TestController{
@RequestMapping("/test.do")
public ModelAndView handleRequest(HttpServletRequest arg0,
HttpServletResponse arg1) throws Exception {
ModelAndView mv=new ModelAndView();
//添加数据
mv.addObject("msg", "helloworld!!");
//添加页面(WEB-INF/jsp/index.jsp)
mv.setViewName("index");
return mv;
}
}
mvc.servlet.xml
<!-- 配置注解扫描 -->
<context:component-scan base-package="com.xixw.controller">
</context:component-scan>
<!-- 配置视图渲染器 -->
<bean id="viewResolver"
class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass"
value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
运行结果
3.基于MVC表现层框架所做的工作
3.1 如何将URL和java类或者方法映射起来?
将请求URL和类中的方法进行映射使用@RequestMapping,如果省略了method则代表可以处理任何方式的请求。
@RequestMapping(value=“设置映射的URL”,method={ 指定请求的方式 })
3.2 如何将请求的参数封装?
参数的封装:请求的参数都是默认封装到处理的方法的参数列表中
(1)普通的参数
在处理方法入参处使用 @RequestParam 可以把请求参数传递给请求方法。
请求的参数名必须要和处理的方法参数名称保持一致;如果不一致必须手动进行匹配,在方法的参数上添加如下注解,但不建议这样做。
@RequestParam(“请求的参数名”)
(2)对象参数(常用的方式)
请求的参数名必须要和方法参数的对象中的属性名保持一致。
(3)模板传参(restful风格)
- 普通的get请求
http://localhost:8080/mvc3/deleteUser.do?uid=3
- 映射格式
@RequestMapping("/deleteUser/{uid}.do")
- restful风格URL
http://localhost:8080/mvc3/deleteUser/3.do
3.3 如何将请求处理后的结果返回给客户端?
(1)乱码的解决
① post请求:配置编码过滤器
web.xml
<!-- 配置编码过滤器 -->
<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>
</filter>
<filter-mapping>
<filter-name>EncodingFilter</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>
② get请求
(2)页面跳转的方式
① 使用ModelAndView对象来进行页面跳转(必须配置视图渲染器)
通过mv.setViewName(“页面地址”)(地址后缀不需要写)的方法来设置要跳转的页面进行页面跳转(请求转发的方式)
返回的数据封装的方式:通过**mv.addObject(“name”, name)**将数据保存起来。
注意:在底层还是将数据保存在request中,相当于request.setAttrubite(“name”,name);
② 使用Servlet的API进行跳转(没有视图渲染器)
请求转发
request.getRequestDispatcher(“index.jsp”).forward(request, response);
重定向
response.sendRedirect(“index.jsp”);
返回的数据封装方式:可以将数据保存在某一个存储空间中。
③ 使用springmvc方式进行跳转(没有视图渲染器)
请求转发
return “forward:跳转的页面”;
注意:默认就是请求转发所以一般将forward:省略
重定向
return “redirect:跳转的页面”;
④ 使用springmvc方式进行跳转(有视图渲染器)
请求转发
return “跳转的页面”;
注意:只要是经过视图渲染器都不能使用重定向进行页面跳转。
返回的数据封装:都可以封装到ModelMap对象中,map.addAttribute(键,值);
注意:对象不能自己创建,使用的时候必须声明在参数列表中。
3.4 如何将响应的数据渲染到页面?
使用JSTL及EL表达式。
4.实现一个文件上传的示例
4.1 上传单个文件
① 添加文件上传的包
② 编写上传文件的页面
新建JSP页面,使用form表单进行文件上传:file_upload.jsp
<body>
<h2>文件上传</h2>
<form action="upload.do" method="post" enctype="multipart/form-data">
请选择文件:<input type="file" name="file"><br>
普通参数:<input type="text" name="test"><br>
<input type="submit" value="提交">
</form>
</body>
③ 编写处理器方法
新建Java类,控制器的类:FileUploadController.java
package com.xixw;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.io.IOUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
@Controller
public class FileUploadController {
@RequestMapping("/upload.do")
public String upload(@RequestParam MultipartFile file,String test,HttpServletRequest request) throws IOException{
//进行文件上传
//获取输入流
InputStream is=file.getInputStream();
//获取上传的文件的目标路径
String path=request.getRealPath("upload");
//获取上传的文件的名称
String name=file.getOriginalFilename();
File f=new File(path+"/"+name);
//创建一个输出流
OutputStream os=new FileOutputStream(f);
//将输入流中的数据通过输出流写入到目标文件中
IOUtils.copy(is, os);
//关闭流
is.close();
os.close();
//跳转到index.jsp页面
return "index.jsp";
}
}
④ 在主配置文件中添加文件上传的配置
mvc.xml
<!-- 配置註解掃描 -->
<context:component-scan base-package="com.xixw">
</context:component-scan>
<!-- 配置文件上传 -->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- one of the properties available; the maximum file size in bytes -->
<property name="maxUploadSize" value="1000000"/>
</bean>
⑤ 配置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">
<display-name></display-name>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<!-- 配置编码过滤器 -->
<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>
</filter>
<filter-mapping>
<filter-name>EncodingFilter</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>
<!-- 配置核心控制器 -->
<servlet>
<servlet-name>mvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 指定配置文件的路徑 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mvc</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
⑥ 进行测试
4.2 上传多个文件
注意:上传多个文件使用数组。
FileUploadController.java
/**
* 处理文件上传
* @author Administrator
*/
@Controller
public class FileUploadController {
@RequestMapping("/upload.do")
public String upload(@RequestParam MultipartFile[] files,String test,HttpServletRequest request) throws IOException{
//进行文件上传
//获取输入流
for (MultipartFile file : files) {
InputStream is=file.getInputStream();
//获取上传的文件的目标路径
String path=request.getRealPath("upload");
//获取上传的文件的名称
String name=file.getOriginalFilename();
File f=new File(path+"/"+name);
//创建一个输出流
OutputStream os=new FileOutputStream(f);
//将输入流中的数据通过输出流写入到目标文件中
IOUtils.copy(is, os);
//关闭流
is.close();
os.close();
}
return "index.jsp";
}
}
fileUpload.jsp
<body>
<h2>文件上传</h2>
<form action="upload.do" method="post" enctype="multipart/form-data">
请选择问件:<input type="file" name="files"><br>
请选择问件:<input type="file" name="files"><br>
请选择问件:<input type="file" name="files"><br>
普通参数:<input type="text" name="test"><br>
<input type="submit" value="提交">
</form>
</body>
测试
结果
5.使用Spring MVC返回JSON数据
5.1 JSON简单语法
JSON是javaScript中的对象,是最常用的数据交互格式。
① JSON中的对象表示
对象使用 { } 括起来,其中数据是以键值对的形式出现,键必须是字符串类型,值的数据类型没有要求(数值、字符串、函数、对象、数组等)。
{“键”:“值”,“键”:“值”}
{“name”:“张三”,“age”:“18”,“password”:“123456”,“study”:“function(){}}”}
② JSON中的数组表示
JSON中的数组是用 [ ] 括起来的数据。
[ {“键”:“值”,“键”:“值”},{“键”:“值”,“键”:“值”},{“键”:“值”,“键”:“值”}]
{“status”:1,“msg”:“ok”,“data”:[{“name”:“张三”,“age”:“18”},{“name”:“张三”,“age”:“18”},{“name”:“张三”,“age”:“18”}]}
5.2 使用Spring MVC返回JSON数据
① 导入需要的JSON包
② 编写用户实体类
UserVo.java
package com.xixw.vo;
/**
* 用户实体类
* @author Administrator
*/
public class UserVo {
private int id;//用户id
private String name;//用户名
private String password;//用户密码
//get/set方法
}
③ 编写处理器方法(新建Java类,控制器的类)
JsonController.java
package com.xixw.controller;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.xixw.vo.UserVo;
/**
* 处理json数据的处理器
* @author Administrator
*/
@Controller
public class JsonController {
/*
* 返回的是对象
*/
@RequestMapping("/getUser.do")
@ResponseBody //处理器中的方法默认返回的是页面,该注解指定方法返回的是数据而非页面
public UserVo getUser(){
UserVo user=new UserVo();
user.setId(10);
user.setName("小明");
user.setPassword("123456");
return user;
}
/*
* 返回的是数组
*/
@RequestMapping("/getUsers.do")
@ResponseBody
public List<UserVo> getUsers(){
List<UserVo> users=new ArrayList<UserVo>();
UserVo user=new UserVo();
user.setId(10);
user.setName("小红");
user.setPassword("123");
users.add(user);
user=new UserVo();
user.setId(11);
user.setName("小绿");
user.setPassword("123");
users.add(user);
user=new UserVo();
user.setId(12);
user.setName("小蓝");
user.setPassword("123");
users.add(user);
return users;
}
}
④ 配置web.xml
<!-- 配置编码过滤器 -->
<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>
</filter>
<filter-mapping>
<filter-name>EncodingFilter</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>
<!-- 配置核心控制器 -->
<servlet>
<servlet-name>mvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet </servlet-class>
<!-- 指定配置文件的路径 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mvc</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
⑤ 在主配置文件中配置Json转换器
mvc.xml
<!-- 配置注解扫描 -->
<context:component-scan base-package="com.xixw">
</context:component-scan>
<!-- 配置json转换器 -->
<bean id="stringConverter" class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/plain;charset=UTF-8</value>
</list>
</property>
</bean>
<bean id="jsonConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
</bean>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="stringConverter"/>
<ref bean="jsonConverter"/>
</list>
</property>
</bean>
⑥ 测试能否返回json数据
返回的是对象
返回的是数组
5.3 进一步优化(封装Json数据)
封装一个返回数据的类:DataResult.java
package com.xixw.util;
/**
* 封装一个数据返回的类
* @author Administrator
*/
public class DataResult {
private int status;//返回的状态码
private String msg;//对状态码的描述
private Object obj;//返回的数据
//get/set方法
}
修改处理器:返回DataResult对象
package com.xixw.controller;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.xixw.util.DataResult;
import com.xixw.vo.UserVo;
/**
* 处理json数据的处理器
* @author Administrator
*/
@Controller
public class JsonController {
@RequestMapping("/getUser.do")
@ResponseBody //处理器中的方法默认返回的是页面,该注解指定方法返回的是数据而非页面
public DataResult getUser(){
DataResult dr=new DataResult();
UserVo user=new UserVo();
user.setId(10);
user.setName("小明");
user.setPassword("123456");
dr.setStatus(1);
dr.setMsg("ok");
dr.setObj(user);
return dr;
}
@RequestMapping("/getUsers.do")
@ResponseBody
public DataResult getUsers(){
DataResult dr=new DataResult();
List<UserVo> users=new ArrayList<UserVo>();
UserVo user=new UserVo();
user.setId(10);
user.setName("小红");
user.setPassword("123");
users.add(user);
user=new UserVo();
user.setId(11);
user.setName("小绿");
user.setPassword("123");
users.add(user);
user=new UserVo();
user.setId(12);
user.setName("小蓝");
user.setPassword("123");
users.add(user);
dr.setStatus(1);
dr.setMsg("ok");
dr.setObj(users);
return dr;
}
}
测试