简介
Spring MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面。Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。使用 Spring 可插入的 MVC 架构,从而在使用Spring进行WEB开发时,可以选择使用Spring的SpringMVC框架或集成其他MVC开发框架,如Struts1(现在一般不用),Struts2(一般老项目使用)等。
处理流程
容器
Spring和SpringMVC各有一个容器的,而Spring和SpringMVC整合时需要注意:为了使bean在一个容器需要进行以下配置。
Spring文档中的容器描述

官方文档的配置方式

了解了官方文档的配置方式,接下来开始SpringMVC技术学习
准备jar包
web.xml
配置SpringMVC+Spring只使用一个容器,该容器以Spring作为主导。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<display-name>spring4.xMVC</display-name>
<filter>
<!-- 使用Spring自带的字符编码过滤器 -->
<filter-name>characterFilter</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>characterFilter</filter-name>
<!-- 只过过滤进入SpringMVC中的请求 -->
<servlet-name>hncu</servlet-name>
</filter-mapping>
<!-- 加载Spring容器需要下面的listener和context-param -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<!-- name是死的 -->
<param-name>contextConfigLocation</param-name>
<!-- Spring容器配置文件路径 -->
<param-value>/WEB-INF/classes/beans.xml</param-value>
</context-param>
<!-- 下面这个Servlet是进入SpringMVC的入口 -->
<servlet>
<servlet-name>hncu</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<!-- 采用值为空的contextConfigLocation即可把'所有bean'放在Spring容器中 -->
<param-name>contextConfigLocation</param-name>
<param-value></param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>hncu</servlet-name>
<url-pattern>/hncu/*</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
beans.xml
该配置文件是Spring容器的核心,Spring容器初始化时需要读取该文件。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
" >
<!--
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
当访问bean的方式如下时,需要使用上面这个HandlerMapping
<bean id="/stud/one" class="cn.hncu.controller.HelloController" />
-->
<!-- 下面这个是用来 解析classpath下 视图资源的解析器 -->
<bean class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
<!-- 给定一个默认视图类型,其它视图如果给出具体的父类视图,默认继承该视图类型 -->
<property name="defaultParentView" value="default" />
</bean>
<!-- 使用 @Controller 注解时早期版本采用 bean 的这种方式进行映射,现在已经过时。
现在采用下面 标签mvc:annotation-driven这种方式。
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" />
-->
<mvc:annotation-driven />
<!-- 扫描注解并且把符合规则的类在容器中生成相应的bean -->
<context:component-scan base-package="/cn/hncu" />
<!-- 下面两个bean通过name的值 配合 @Resource注解 给Controller的userService注入具体实现类 -->
<!-- 模拟进行维护,只要换一个class就以换掉具体实现,
不用改原来实现类的代码 ,采用注解的话就要移除@Service注解
<bean name="userService" class="cn.hncu.user.service.UserServiceImpl">
-->
<bean name="userService" class="cn.hncu.user.service.UserServiceImpl2">
<property name="userDao">
<bean class="cn.hncu.user.dao.UserDaoImpl" />
</property>
</bean>
<!-- 使用Apache的文件上传工具时,必须使用下面这个Bean 并且id/name属性必须为 "multipartResolver" -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver" />
</beans>
常用配置方式
下面只是部分代码,但是却可以展现出SpringMVC中Controller主要的配置方式。
package cn.hncu.controller;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.fileupload.util.Streams;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.context.ServletContextAware;
import org.springframework.web.multipart.MultipartFile;
import cn.hncu.domain.User;
/* 实现ServletContextAware接口就可在 ServletContext初始化
* 通过监听者模式调用ServletContextAware接口的setServletContext()方法
* 给 ctx 赋值,这样就可以在POJO中获取到ServletContext容器了。
*/
@Controller
public class DemoController implements ServletContextAware{
private ServletContext ctx; //servletContext容器
private String basePath; //项目的本地路径
public ServletContext getCtx() {
return ctx;
}
public void setCtx(ServletContext ctx) {
this.ctx = ctx;
}
public String getBasePath() {
return basePath;
}
public void setBasePath(String basePath) {
this.basePath = basePath;
}
@Override //该方法是ServletContextAware接口的方法!!!
public void setServletContext(ServletContext servletContext) {
ctx = servletContext;
basePath = servletContext.getRealPath("/");
}
// http://127.0.0.1:8080/spring4.xMVC/hncu/demo1?name=张三
// http://127.0.0.1:8080/spring4.xMVC/hncu/demo1?name=张三&age=22
@RequestMapping(value="/demo1",method={RequestMethod.GET})
public String demo1(String name,Integer age) { //使用包装类,否则会出现null异常
System.out.println("demo1..."+name+","+age);
return "res";
}
/* 使用@RequestParam注解被注解的参数默认是必填的
* http://127.0.0.1:8080/spring4.xMVC/hncu/demo2 错误代码400
* 可以设置required为false
* 还可以设置前端的name为'nm'才封装给被注解的参数
* http://127.0.0.1:8080/spring4.xMVC/hncu/demo2?nm=张三
*
*/
@RequestMapping(value="/demo2")
public String demo2(@RequestParam(required=false,name="nm") String name,Integer age) { //使用包装类,否则会出现null异常
System.out.println("demo2..."+name+","+age);
return "res";
}
/* 使用@PathVariable注解可以解析url路径
* http://127.0.0.1:8080/spring4.xMVC/hncu/demo3/123/abc/stud
* ---> path=123,path2=stud
*/
@RequestMapping(value="/demo3/{p}/abc/{pp}")
public String demo3(@PathVariable(value="p") String path,@PathVariable(value="pp") String path2) { //使用包装类,否则会出现null异常
System.out.println("demo3..."+path +","+path2);
return "res";
}
/* http://127.0.0.1:8080/spring4.xMVC/hncu/demo4?name=张三&age=22
* 使用Model对象调用addAttribute()相当于 request.setAttribute()
* 还可在方法参数给的 req、session、resp、out 等
* 注意 OutputStream 和 Model 不能共存
*/
@RequestMapping(value="/demo4")
public String demo4(User user, Model model,
HttpServletRequest req,
HttpSession session,
HttpServletResponse resp) { //resp的输出流
System.out.println("demo4...");
model.addAttribute("user", user);
return "res";
}
/* 使用@RequestHeader注解可以获得请求头信息
* 使用@CookieValue注解可以获得cookie的信息
*/
@RequestMapping(value="/demo5")
public String demo5(@RequestHeader(name="accept")String accept,
@CookieValue(name="JSESSIONID")String sessionid
){
System.out.println(accept);
System.out.println(sessionid);
return "res";
}
/* http://127.0.0.1:8080/spring4.xMVC/hncu/demo6?name=张三&age=22&addr.id=a001&addr.name=hncu
* 通过使用 addr.id=a001&addr.name=hncu 可以级联到User中的 Addr属性
*/
@RequestMapping(value="/demo6")
public String demo6(User user,Model model){
System.out.println(user);
model.addAttribute("user",user);
return "res";
}
文件下载//
@RequestMapping(value="/demo7")
public void demo7( OutputStream out, HttpServletResponse resp ) {
//设置文件下载响应头
resp.setHeader("Content-Disposition", "attachment;filename=a.txt");
resp.setContentType("application/force-download;charact=utf-8");
File file = new File(basePath+"/file/a.txt");
if( file != null && file.isFile()) {
try {
InputStream in = new FileInputStream(file);
//使用appache的工具包commons-io进行流拷贝
Streams.copy(in, out, true);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
文件上传//
/* 需要 apache的 commons-fileupload-X.X.X.jar和commons-io-X.X.jar
* 同时需要 beans.xml中配置 <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver" />
*/
//上传单个文件
@RequestMapping(value="/demo8")
public String demo8(@RequestParam(name="file") MultipartFile file ) { //必须使用MultipartFile接口进行获取上传的文件
try {
InputStream in = file.getInputStream();
//获取上传文件的文件名 解决中文乱码
String fileName = new String(file.getOriginalFilename().getBytes("iso8859-1"),"utf-8");
System.out.println( fileName );
OutputStream out = new FileOutputStream(basePath+"/file/"+fileName);
Streams.copy(in, out, true);
} catch (IOException e) {
e.printStackTrace();
}
return "res";
}
//上传多个文件,使用list再封装一层
@RequestMapping(value="/demo9")
public String demo9(@RequestParam(name="files") ArrayList<MultipartFile> files ) {
if( files != null ) {
for (MultipartFile file : files) {
//当文件不存在时跳过,预防空指针
if( file == null || file.isEmpty() ) {
continue;
}
try {
InputStream in = file.getInputStream();
//获取上传文件的文件名 解决中文乱码
String fileName = new String(file.getOriginalFilename().getBytes("iso8859-1"),"utf-8");
System.out.println( fileName );
OutputStream out = new FileOutputStream(basePath+"/file/"+fileName);
Streams.copy(in, out, true);
} catch (IOException e) {
e.printStackTrace();
}
}
}
return "res";
}
}
完整代码链接
完整代码链接中有一个user模块如何从Controller ===> Service ===> DAO