Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。使用 Spring 可插入的 MVC 架构,可以选择是使用内置的 Spring Web 框架还是 Struts 这样的 Web 框架。通过策略接口,Spring 框架是高度可配置的,而且包含多种视图技术,例如 JavaServer Pages(JSP)技术、Velocity、Tiles、iText 和 POI。Spring MVC 框架并不知道使用的视图,所以不会强迫您只使用 JSP 技术。Spring MVC 分离了控制器、模型对象、分派器以及处理程序对象的角色,这种分离让它们更容易进行定制。
常见MVC框架比较
运行性能上: Jsp+servlet>struts1>spring mvc>struts2+freemarker>>struts2,ognl,值栈。
开发效率上,基本正好相反。值得强调的是,spring mvc开发效率和struts2不相上下。
Struts2的性能低的原因是因为OGNL和值栈造成的。所以,如果你的系统并发量高,可以使用freemaker进行显示,而不是采用OGNL和值栈。这样,在性能上会有相当大得提高。
基于spring2.5的采用XML配置的spring MVC项目
注:本项目全部基于XML配置。同时,集成了hibernate。采用的是:spring MVC+hibernate+spring的开发架构。 1. 建立web项目
2. 导入jar包(spring.jar, spring-webmvc.jar, commons-logging.jar。其他jar包为hibernate相关jar包)
3. 修改web.xml如下:
6. 在WEB-INF下增加hib-config.xml(这里包含spring集成hibernate相关的配置)
8. 建立相关类和包结构,如下图所示:
9. 各类代码如下: package com.sxt.po; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; @Entity public class User { @Id @GeneratedValue(strategy=GenerationType.AUTO) private int id; private String uname; public int getId() { } public void setId(int id) { return id; } } this.id = id; public String getUname() { } public void setUname(String uname) { } this.uname = uname; return uname; package com.sxt.dao; import org.springframework.orm.hibernate3.HibernateTemplate; import com.sxt.po.User; public class UserDao { private HibernateTemplate hibernateTemplate; public void add(User u){ } public HibernateTemplate getHibernateTemplate() { } public void setHibernateTemplate(HibernateTemplate hibernateTemplate) { } } package com.sxt.service; import com.sxt.dao.UserDao; import com.sxt.po.User; public class UserService { private UserDao userDao; public void add(String uname){ } public UserDao getUserDao() { System.out.println(\"UserService.add()\"); User u = new User(); u.setUname(uname); userDao.add(u); this.hibernateTemplate = hibernateTemplate; return hibernateTemplate; System.out.println(\"UserDao.add()\"); hibernateTemplate.save(u); } return userDao; public void setUserDao(UserDao userDao) { } } package com.sxt.action; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.Controller; import com.sxt.service.UserService; public class UserController implements Controller { private UserService userService; @Override public ModelAndView handleRequest(HttpServletRequest req, } public UserService getUserService() { } public void setUserService(UserService userService) { } } this.userService = userService; return userService; HttpServletResponse resp) throws Exception { this.userDao = userDao; System.out.println(\"HelloController.handleRequest()\"); req.setAttribute(\"a\userService.add(req.getParameter(\"uname\")); return new ModelAndView(\"index\");
10. 运行测试:
http://locahost:8080/springmvc01/user.do?uname=zhangsan。
结果:数据库中增加zhangsan的记录。页面跳转到index.jsp上,显示:
基于spring2.5注解实现的spring MVC项目
我们采用sprng MVC开发项目时,通常都会采用注解的方式,这样可以大大提高我们的开发效率。实现零配置。下面我们从零开始重新做一个spring MVC的配置。这个项目完全采用注解的方式开发。同时,我们以后的spring MVC项目也都会采用注解的方式。 1. 建立web项目
2. 导入jar包(spring.jar, spring-webmvc.jar, commons-logging.jar。其他jar包为hibernate相关jar包)
3. 修改web.xml,文件内容如下:
8. User、UserDao、UserService、UserController类的代码如下: package com.sxt.po; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; @Entity public class User { @Id @GeneratedValue(strategy=GenerationType.AUTO) private int id; private String uname; private String pwd; public String getPwd() { } public void setPwd(String pwd) { } public int getId() { } public void setId(int id) { } public String getUname() { } public void setUname(String uname) { this.uname = uname; return uname; this.id = id; return id; this.pwd = pwd; return pwd; } } package com.sxt.dao; import javax.annotation.Resource; import org.springframework.orm.hibernate3.HibernateTemplate; import org.springframework.stereotype.Repository; import com.sxt.po.User; @Repository(\"userDao\") public class UserDao { @Resource private HibernateTemplate hibernateTemplate; public void add(User u){ } public HibernateTemplate getHibernateTemplate() { } public void setHibernateTemplate(HibernateTemplate hibernateTemplate) { } } package com.sxt.service; import javax.annotation.Resource; import org.springframework.stereotype.Service; import com.sxt.dao.UserDao; import com.sxt.po.User; @Service(\"userService\") public class UserService { @Resource private UserDao userDao; public void add(String uname){ System.out.println(\"UserService.add()\"); User u = new User(); u.setUname(uname); this.hibernateTemplate = hibernateTemplate; return hibernateTemplate; System.out.println(\"UserDao.add()\"); hibernateTemplate.save(u); } userDao.add(u); public UserDao getUserDao() { } public void setUserDao(UserDao userDao) { } } package com.sxt.web; import javax.annotation.Resource; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.SessionAttributes; import com.sxt.po.User; import com.sxt.service.UserService; @Controller(\"userController\") @RequestMapping(\"/user.do\") public class UserController { @Resource private UserService userService; @RequestMapping(params=\"method=reg\") public String reg(String uname) { } public UserService getUserService() { } public void setUserService(UserService userService) { } } this.userService = userService; return userService; System.out.println(\"HelloController.handleRequest()\"); userService.add(uname); return \"index\"; this.userDao = userDao; return userDao; 9. 运行测试:
http://pc-201110291327:8080/springmvc02/user.do?method=reg&uname=gaoqi 则会调用userController的reg方法,从而将数据内容插入到数据库中。
基于spring 3.0项目开发实例
spring3.0完全兼容spring2.5.因此,我们只要简单修改上面项目的类库和配置文件。类的代码保持不变。
1. 导入相关jar包,如下:
2. spring配置文件springmvc-servlet.xml修改如下:
6. 运行,测试。跟上一个项目保持一致。
Spring MVC 3.0 深入
核心原理
1. 2. 3. 4. 5.
用户发送请求给服务器。url:user.do
服务器收到请求。发现DispatchServlet可以处理。于是调用DispatchServlet。
DispatchServlet内部,通过HandleMapping检查这个url有没有对应的Controller。如果有,则调用Controller。 Controller开始执行。
Controller执行完毕后,如果返回字符串,则ViewResolver将字符串转化成相应的视图对象;如果返回ModelAndView对象,该对象本身就包含了视图对象信息。 6. DispatchServlet将执视图对象中的数据,输出给服务器。 7. 服务器将数据输出给客户端。
spring3.0中相关jar包的含义
org.springframework.aop-3.0.3.RELEASE.jar org.springframework.asm-3.0.3.RELEASE.jar org.springframework.beans-3.0.3.RELEASE.jar org.springframework.context-3.0.3.RELEASE.jar org.springframework.core-3.0.3.RELEASE.jar org.springframework.expression-3.0.3.RELEASE.jar org.springframework.web-3.0.3.RELEASE.jar spring的aop面向切面编程 spring独立的asm字节码生成程序 IOC的基础实现 IOC基础上的扩展服务 spring的核心包 spring的表达式语言 web工具包 org.springframework.web.servlet-3.0.3.RELEASE.jar mvc工具包 @Controller控制器定义
和Struts1一样,Spring的Controller是Singleton的。这就意味着会被多个请求线程共享。因此,我们将控制器设计成无状态类。
在spring 3.0中,通过@controller标注即可将class定义为一个controller类。为使spring能找到定义为controller的bean,需要在spring-context配置文件中增加如下定义: 注:实际上,使用@component,也可以起到@Controller同样的作用。 @RequestMapping 在类前面定义,则将url和类绑定。 在方法前面定义,则将url和类的方法绑定,如下所示: package com.sxt.web; import javax.annotation.Resource; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import com.sxt.service.UserService; @Controller @RequestMapping(\"/user.do\") public class UserController { @Resource private UserService userService; //http://localhost:8080/springmvc02/user.do?method=reg&uname=zzzz @RequestMapping(params=\"method=reg\") public String reg(String uname) { System.out.println(\"HelloController.handleRequest()\"); } } userService.add(uname); return \"index\"; public UserService getUserService() { } public void setUserService(UserService userService) { } this.userService = userService; return userService; @RequestParam 一般用于将指定的请求参数付给方法中形参。示例代码如下: @RequestMapping(params=\"method=reg5\") public String reg5(@RequestParam(\"name\")String uname,ModelMap map) { } System.out.println(\"HelloController.handleRequest()\"); System.out.println(uname); return \"index\"; 这样,就会将name参数的值付给uname。当然,如果请求参数名称和形参名称保持一致,则不需要这种写法。 @SessionAttributes 将ModelMap中指定的属性放到session中。示例代码如下: @Controller @RequestMapping(\"/user.do\") @SessionAttributes({\"u\",\"a\"}) //将ModelMap中属性名字为u、a的再放入session中。这样,request和session中都有了。 public class UserController { } 注:名字为”user”的属性再结合使用注解@SessionAttributes可能会报错。 @ModelAttribute 这个注解可以跟@SessionAttributes配合在一起用。可以将ModelMap中属性的值通过该注解自动赋给指定变示例代码如下: package com.sxt.web; 量。 import javax.annotation.Resource; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.SessionAttributes; @Controller @RequestMapping(\"/user.do\") @SessionAttributes({\"u\",\"a\"}) public class UserController { @RequestMapping(params=\"method=reg4\") public String reg4(ModelMap map) { } @RequestMapping(params=\"method=reg5\") System.out.println(\"HelloController.handleRequest()\"); map.addAttribute(\"u\",\"尚学堂高淇\"); return \"index\"; public String reg5(@ModelAttribute(\"u\")String uname,ModelMap map) { //@ModelAttribute(\"u\")String uname:将属性u的值赋给形参uname } } System.out.println(\"HelloController.handleRequest()\"); System.out.println(uname); return \"index\"; 先调用reg4方法,再调用reg5方法。我们发现控制台打印出来:尚学堂高淇 Controller类中方法参数的处理 Controller类中方法返回值的处理 1. 返回string(建议) a) 根据返回值找对应的显示页面。路径规则为:prefix前缀+返回值+suffix后缀组成 b) 代码如下: @RequestMapping(params=\"method=reg4\") public String reg4(ModelMap map) { } System.out.println(\"HelloController.handleRequest()\"); return \"index\"; 前缀为:/WEB-INF/jsp/ 后缀是:.jsp 在转发到:/WEB-INF/jsp/index.jsp 2. 也可以返回ModelMap、ModelAndView、map、List、Set、Object、无返回值。 一般建议返回字符串! 请求转发和重定向 代码示例: package com.sxt.web; import javax.annotation.Resource; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.SessionAttributes; @Controller @RequestMapping(\"/user.do\") public class UserController { @RequestMapping(params=\"method=reg4\") public String reg4(ModelMap map) { System.out.println(\"HelloController.handleRequest()\"); return \"forward:index.jsp\"; return \"forward:user.do?method=reg5\"; //转发 return \"redirect:user.do?method=reg5\"; //重定向 return \"redirect:http://www.baidu.com\"; //重定向 // // // } } @RequestMapping(params=\"method=reg5\") public String reg5(String uname,ModelMap map) { } System.out.println(\"HelloController.handleRequest()\"); System.out.println(uname); return \"index\"; 访问reg4方法,既可以看到效果。 获得request对象、session对象 普通的Controller类,示例代码如下: @Controller @RequestMapping(\"/user.do\") public class UserController { } @RequestMapping(params=\"method=reg2\") public String reg2(String uname,HttpServletRequest req,ModelMap map){ } req.setAttribute(\"a\", \"aa\"); req.getSession().setAttribute(\"b\", \"bb\"); return \"index\"; ModelMap 是map的实现,可以在其中存放属性,作用域同request。下面这个示例,我们可以在modelMap中放入数据,然后在forward的页面上显示这些数据。通过el表达式、JSTL、java代码均可。代码如下: package com.sxt.web; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.mvc.multiaction.MultiActionController; @Controller @RequestMapping(\"/user.do\") public class UserController extends MultiActionController { } <%@ page language=\"java\" import=\"java.util.*\" pageEncoding=\"gbk\"%> <%@ taglib prefix=\"c\" uri=\"http://java.sun.com/jsp/jstl/core\" %> 见名知意,从名字上我们可以知道ModelAndView中的Model代表模型,View代表视图。即,这个类把要显示的数据存储到了Model属性中,要跳转的视图信息存储到了view属性。我们看一下ModelAndView的部分源码,即可知其中关系: public class ModelAndView { /** * Default constructor for bean-style usage: populating bean * properties instead of passing in constructor arguments. * @see #setView(View) * @see #setViewName(String) /** * Indicates whether or not this instance has been cleared with a call to {@link #clear()}. */ private boolean cleared = false; /** Model Map */ private ModelMap model; /** View instance or view name String */ private Object view; */ public ModelAndView() { } /** * Convenient constructor when there is no model data to expose. * Can also be used in conjunction with Can be used to suppress rendering of a given ModelAndView object * in the Returns package com.sxt.web; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.multiaction.MultiActionController; import com.sxt.po.User; @Controller @RequestMapping(\"/user.do\") public class UserController extends MultiActionController { @RequestMapping(params=\"method=reg\") public ModelAndView reg(String uname){ ModelAndView mv = new ModelAndView(); mv.setViewName(\"index\"); mv.setView(new RedirectView(\"index\")); User u = new User(); u.setUname(\"高淇\"); mv.addObject(u); //查看源代码,得知,直接放入对象。属性名为”首字母小写的类名”。 一般建议手动增加属性名称。 mv.addObject(\"a\", \"aaaa\"); return mv; // } } <%@ page language=\"java\" import=\"java.util.*\" pageEncoding=\"gbk\"%> <%@ taglib prefix=\"c\" uri=\"http://java.sun.com/jsp/jstl/core\" %> 1. spring使用了apache-commons下得上传组件,因此,我们需要引入两个jar包: 1. apache-commons-fileupload.jar 2. apache-commons-io.jar 2. 在springmvc-servlet.xml配置文件中,增加CommonsMultipartResoler配置: 进入项目发布后的目录,发现文件上传成功: 处理ajax请求 spring使用了jackson类库,帮助我们在java对象和json、xml数据之间的互相转换。他可以将控制器返回的对象直接转换成json数据,供客户端使用。客户端也可以传送json数据到服务器进行直接转换。使用步骤如下: 1. 项目中需要引入如下两个jar包: jackson-core-asl-1.7.2jar jackson-mapper-asl-1.7.2jar 2. spring配置文件中修改: 3. 客户端代码a.jsp如下: <%@ page language=\"java\" import=\"java.util.*\" pageEncoding=\"gbk\"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+\"://\"+request.getServerName()+\":\"+request.getServerPort()+path+\"/\"; %> 5. 测试。 a) 启动服务器。输入:http://localhost:8080/springmvc03/a.jsp Spring中的拦截器 定义spring拦截器两种基本方式 1. 实现接口:org.springframework.web.servlet.HandlerInterceptor。 接口中有如下方法需要重写: 注意:参数中的Object handler是下一个拦截器。 a) public boolean preHandle (HttpServletRequest request,HttpServletResponse response, Object handler) throws Exception 该方法在action执行前执行,可以实现对数据的预处理,比如:编码、安全控制等。 如果方法返回true,则继续执行action。 b) public void postHandle (HttpServletRequest request,HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception 该方法在action执行后,生成视图前执行。在这里,我们有机会修改视图层数据。 c) public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception 最后执行,通常用于释放资源,处理异常。我们可以根据ex是否为空,来进行相关的异常处理。因为我们在平时处理异常时,都是从底层向上抛出异常,最后到了spring框架从而到了这个方法中。 2. 继承适配器: org.springframework.web.servlet.handler.HandlerInterceptorAdapter 这个适配器实现了HandlerInterceptor接口。提供了这个接口中所有方法的空实现。 如下我们写出两个拦截器的示例代码,仅供大家参考: package com.sxt.interceptor; import javax.interceptor.Interceptors; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; public class MyInterceptor implements HandlerInterceptor { @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { @Override public boolean preHandle(HttpServletRequest request,HttpServletResponse response, Object handler) throws @Override public void postHandle(HttpServletRequest request,HttpServletResponse response, Object handler,ModelAndView modelAndView) throws Exception { } System.out.println(\"Action执行之后,生成视图之前执行!!\"); } System.out.println(\"最后执行!!!一般用于释放资源!!\"); Exception { } package com.sxt.interceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; public class MyInterceptor2 extends HandlerInterceptorAdapter { @Override public boolean preHandle(HttpServletRequest request,HttpServletResponse response, Object handler) throws } System.out.println(\"action之前执行!!!\"); return true; //继续执行action Exception { } } System.out.println(\"MyInterceptor2.preHandle()\"); return true; //继续执行action 3. XML中如何配置。如下为示例代码: 因篇幅问题不能全部显示,请点此查看更多更全内容**********${requestScope.u.uname}
**********${sessionScope.u.uname}
@RequestMapping(params=\"method=reg4\") public String reg4(ModelMap map) { } System.out.println(\"HelloController.handleRequest()\"); map.addAttribute(\"u\",\"uuuu\"); //将u放入request作用域中,这样转发页面也可以取到这个数据。 return \"index\";${requestScope.a}
addObject
. * @param viewName name of the View to render, to be resolved * by the DispatcherServlet's ViewResolver * @see #addObject */ public ModelAndView(String viewName) { } this.view = viewName; /** * Convenient constructor when there is no model data to expose. * Can also be used in conjunction with addObject
. * @param view View object to render * @see #addObject */ public ModelAndView(View view) { } this.view = view; /** * Creates new ModelAndView given a view name and a model. * @param viewName name of the View to render, to be resolved * by the DispatcherServlet's ViewResolver * @param model Map of model names (Strings) to model objects * (Objects). Model entries may not be null
, but the * model Map may be null
if there is no model data. */ public ModelAndView(String viewName, Mapnull
, but the * model Map may be null
if there is no model data. */ public ModelAndView(View view, Mapnull
if we are using a View object. */ public String getViewName() { } return (this.view instanceof String ? (String) this.view : null); this.view = viewName; /** * Set a View object for this ModelAndView. Will override any * pre-existing view name or View. */ public void setView(View view) { } this.view = view; /** * Return the View object, or null
if we are using a view name * to be resolved by the DispatcherServlet via a ViewResolver. */ public View getView() { } return (this.view instanceof View ? (View) this.view : null); /** * Indicate whether or not this ModelAndView
has a view, either * as a view name or as a direct {@link View} instance. */ public boolean hasView() { } return (this.view != null); /** * Return whether we use a view reference, i.e. true
* if the view has been specified via a name to be resolved by the * DispatcherServlet via a ViewResolver. */ public boolean isReference() { } return (this.view instanceof String); /** * Return the model map. May return null
. * Called by DispatcherServlet for evaluation of the model. */ protected MapModelMap
instance (never null
). */ public ModelMap getModelMap() { } if (this.model == null) { } return this.model; this.model = new ModelMap(); /** * Return the model map. Never returns null
. * To be called by application code for modifying the model. */ public Mapnull
) * @see ModelMap#addAttribute(String, Object) * @see #getModelMap() */ public ModelAndView addObject(String attributeName, Object attributeValue) { } getModelMap().addAttribute(attributeName, attributeValue); return this; /** * Add an attribute to the model using parameter name generation. * @param attributeValue the object to add to the model (never null
) * @see ModelMap#addAttribute(Object) * @see #getModelMap() */ public ModelAndView addObject(Object attributeValue) { } getModelMap().addAttribute(attributeValue); return this; /** * Add all attributes contained in the provided Map to the model. * @param modelMap a Map of attributeName -> attributeValue pairs * @see ModelMap#addAllAttributes(Map) * @see #getModelMap() */ public ModelAndView addAllObjects(MappostHandle
method of a HandlerInterceptor. * @see #isEmpty() * @see HandlerInterceptor#postHandle } */ public void clear() { } this.view = null; this.model = null; this.cleared = true; /** * Return whether this ModelAndView object is empty, * i.e. whether it does not hold any view and does not contain a model. */ public boolean isEmpty() { } return (this.view == null && CollectionUtils.isEmpty(this.model)); /** * Return whether this ModelAndView object is empty as a result of a call to {@link #clear} * i.e. whether it does not hold any view and does not contain a model. *false
if any additional state was added to the instance * after the call to {@link #clear}. * @see #clear() */ public boolean wasCleared() { } return (this.cleared && isEmpty()); /** * Return diagnostic information about this model and view. */ @Override public String toString() { } StringBuilder sb = new StringBuilder(\"ModelAndView: \"); if (isReference()) { } else { } sb.append(\"; model is \").append(this.model); return sb.toString(); sb.append(\"materialized View is [\").append(this.view).append(']'); sb.append(\"reference to view with name '\").append(this.view).append(\"'\"); 测试代码如下:${requestScope.a}
${requestScope.user.uname}
地址栏输入:http://localhost:8080/springmvc03/user.do?method=reg 结果为: 基于spring 3.0mvc 框架的文件上传实现上传成功!
6. 建立upload_error.jsp页面 <%@ page language=\"java\" import=\"java.util.*\" pageEncoding=\"gbk\"%> 上传失败!
7. 发布项目,运行测试:http://localhost:8080/springmvc03/upload.jsp