常见MVC框架比较
运行性能上:
Jsp+servlet>struts1>spring mvc>struts2+freemarker>>struts2,ognl,值栈。
开发效率上,基本正好相反。值得强调的是,spring mvc开发效率和struts2不相上下。
web-config.xml:
和Struts1一样,Spring的Controller是Singleton的。这就意味着会被多个请求线程共享。因此,我们将控制器设计成无状态类。(和struts2不一样,因为函数的参数是唯一的)
下面是一个用户管理的基本功能:
运行性能上:
Jsp+servlet>struts1>spring mvc>struts2+freemarker>>struts2,ognl,值栈。
开发效率上,基本正好相反。值得强调的是,spring mvc开发效率和struts2不相上下。
Struts2的性能低的原因是因为OGNL和值栈造成的。所以,如果你的系统并发量高,可以使用freemaker进行显示,而不是采用OGNL和值栈。这样,在性能上会有相当大得提高
1.环境搭建web.xml
<web-app
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param> <!-- 不设置此内容的话,会默认加载:<servlet-name>标签内名字+"-servlet”的xml,即dispatcherServlet-servlet.xml-->
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/web-config.xml</param-value><!-- 有多个配置文件,中间用,隔开 -->
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern> <!-- 所有请求都捕获 -->
</servlet-mapping>
</web-app>
web-config.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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<mvc:annotation-driven/>
<context:component-scan base-package="com.test.controller" />
<mvc:resources location="/resources/" mapping="/resource/**"/><!-- 指定静态资源(请求不被spring过滤)如css,js等的位置 ,css路径应是mapping中的路径-->
<!-- 页面View层基本信息设定 -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/> <!-- 如果使用jstl的话,配置的属性 -->
<property name="suffix" value=".jsp"/> <!-- 后缀 -->
<property name="prefix" value="/WEB-INF/jsp/" /> <!-- 前缀 -->
</bean>
<!-- 处理文件上传 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver" >
<property name="defaultEncoding" value="utf-8"/> <!-- 默认编码 (ISO-8859-1) -->
<property name="maxInMemorySize" value="10240"/> <!-- 最大内存大小 (10240)-->
<property name="uploadTempDir" value="/upload/"/> <!-- 上传后的目录名 (WebUtils#TEMP_DIR_CONTEXT_ATTRIBUTE) -->
<property name="maxUploadSize" value="-1"/> <!-- 最大文件大小,-1为无限止(-1) -->
</bean>
</beans>
@Controller
@SessionAttributes({"param","se"})获取session,将ModelMap中属性名字为param、se的再放入session中,这样,request和session中都有了。
@RequestMapping(value="user") //--1 @RequestMapping在类前面定义,则将url和类绑定。在方法前面定义,则将url和类的方法绑定
public class HelloController {
@RequestMapping(value={"/hello2","/hello"},params="method=do") //--2 value指定多个值,可以多个访问路径
public String hello() {
return "hello"; //根据设置将跳到/WEB-INF/jsp/hello.jsp中
}
// 1、2处如果同时定义了路径则是两处的叠加 上面的这个访问路径:
//http://localhost:8080/springMVC/user/hello2?method=do http://localhost:8080/springMVC/user/hello?method=do
//获取传过来的值 直接用参数就可以获取
@RequestMapping(params="param=do") //http://localhost:8080/springMVC/user?param=do&name=ljf&age=22&id=1&uname=test
public String serv(int id,@RequestParam String name,@RequestParam("age") int old,User u){ //@RequestParam 注解用來表示传来的参数不和形參一致解決方法
System.out.println(u.getUname()+" "+id+" "+name+" "+old); //传过来的值会和对象属性名字自动匹配
return "hello";
}
//传过来的值不能少,如果少了任一个就会出错,少id会报异常,少name,age会报404找不到路径
//值返回去 用map 或者 model 它们的值就会存在request域中
@RequestMapping //http://localhost:8080/springMVC1/user
public String say(Map<String, Object> map,Model model,HttpServletRequest req,HttpSession session) {//获取request,session
map.put("param", "v1");
model.addAttribute("p1", "v2");
//使用Object的类型作为key,String-->string
User u=new User("ljf");
model.addAttribute("ok");//${string}
model.addAttribute(u); //${user.uname}
session.setAttribute("se", "se");
return "hello";
}
@RequestMapping("/req")
public String req(@ModelAttribute("se") String name) { //@ModelAttribute 把session中的"se"賦給name,注意,如果session中没有,将报错
System.out.println(name);
return "hello";
}
//ModelAndView模型视图类
//从名字上可以知道ModelAndView中的Model代表模型,View代表视图。即,这个类把要显示的数据存储到了Model属性中,要跳转的视图信息存储到了view属性
@RequestMapping(params="method=reg6")
public ModelAndView reg6(String uname)
{
ModelAndView mv = new ModelAndView();
mv.setViewName("hello");
// mv.setView(new RedirectView("index"));
User u = new User("abc");
mv.addObject(u); //查看源代码,得知,直接放入对象。属性名为”首字母小写的类名”。 一般建议手动增加属性名称。
mv.addObject("a", "aaaa");
return mv;
}
}
和Struts1一样,Spring的Controller是Singleton的。这就意味着会被多个请求线程共享。因此,我们将控制器设计成无状态类。(和struts2不一样,因为函数的参数是唯一的)
核心原理
1.用户发送请求给服务器。url:user.do
2.服务器收到请求。发现DispatchServlet可以处理。于是调用DispatchServlet。
3.DispatchServlet内部,通过HandleMapping检查这个url有没有对应的Controller。如果有,则调用Controller。
4.Controller开始执行。
5.Controller执行完毕后,如果返回字符串,则ViewResolver将字符串转化成相应的视图对象;如果返回ModelAndView对象,该对象本身就包含了视图对象信息。
6.DispatchServlet将执视图对象中的数据,输出给服务器。
7.服务器将数据输出给客户端。
spring 转发,重定向
@RequestMapping(value="/add",method=RequestMethod.POST)
public String add(@Valid User user,BindingResult bind){
if(bind.hasErrors())
return "user/add";
users.put(user.getUsername(),user);
return InternalResourceViewResolver.REDIRECT_URL_PREFIX+"/user/users";//"forward:user.do"; 默認转发//重定向return "redirect:user.do";
}
spring采用rest形式访问资源,以资源为导向
@RequestMapping(value="/{username}/update",method=RequestMethod.GET)//**************加{}
public String update(@PathVariable String username,Model model){//@PathVariable 路径变量username即为路径中的内容:{}中内容
model.addAttribute(users.get(username));
return "user/update";
}
下面是一个用户管理的基本功能:
user类:
public class User {
private String username;
private String password;
private String email;
public User() {
super();
}
public User(String username, String password, String email) {
super();
this.username = username;
this.password = password;
this.setEmail(email);
}
@NotEmpty(message="用户名不能为空")
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@NotEmpty(message="密码不能为空")
@Size(max=10,min=4)
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@NotEmpty(message="邮箱不能为空")
@Email(message="邮箱格式不正确")
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
处理user异常的类:
public class UserException extends RuntimeException{
public UserException() {
super();
}
public UserException(String message, Throwable cause) {
super(message, cause);
}
public UserException(String message) {
super(message);
}
public UserException(Throwable cause) {
super(cause);
}
}
controller类:
@Controller
@RequestMapping("/user")
public class UserController {
private final static Map<String,User> users=new HashMap<String, User>();
public UserController(){
users.put("ldh",new User("ldh", "123", "123123"));
users.put("zxy",new User("zxy", "123", "123123"));
users.put("gfc",new User("gfc", "123", "123123"));
users.put("lm",new User("lm", "123", "123123"));
}
@RequestMapping(value={"users","/"})
public String list(Model model){
model.addAttribute("users", users);
return "user/list";
}
@RequestMapping(value="/add",method=RequestMethod.GET) //method 属性要求请求方式必须是get
public String add(Model model){
model.addAttribute(new User());// 这步不能少,如果是new User("a","b","c"),则表单里显示user内容
return "user/add";
}
/*验证字段:springMVC使用 JSR-303 Validator(java标准)验证, 可以查看 JSR 303 - Bean Validation文档, 需要bean-validator.jar
然后在实体类中采用注解形式标明验证条件 ,在调用方法中使用@Valid指明验证的model,以及用BindingResult存储错误信息,在页面中用spring form表单显示错误
*/
@RequestMapping(value="/add",method=RequestMethod.POST)
public String add(@Valid User user,BindingResult bind){
if(bind.hasErrors())
return "user/add";
users.put(user.getUsername(),user);
return InternalResourceViewResolver.REDIRECT_URL_PREFIX+"/user/users";//"forward:user.do"; 默認转发//重定向return "redirect:user.do";
}
@RequestMapping(value="{username}",method=RequestMethod.GET)
public String show(@PathVariable String username,Model model){
model.addAttribute(users.get(username));
return "user/show";
}
/**spring采用rest形式访问资源,以资源为导向**/
@RequestMapping(value="/{username}/update",method=RequestMethod.GET)//**************加{}
public String update(@PathVariable String username,Model model){//@PathVariable 路径变量username即为路径中的内容:{}中内容
model.addAttribute(users.get(username));
return "user/update";
}
@RequestMapping(value="/{username}/update",method=RequestMethod.POST)
public String update(@PathVariable String username,@Valid User user,BindingResult bind){//bind一定要紧跟在User之后
if(bind.hasErrors())
return "user/update";
users.put(username, user);
return "redirect:/user/users";
}
@RequestMapping("ex")
public String ex(){ //测试处理异常
if(true)
throw new UserException("用户出现错误 !");
return "user/users";
}
//异常处理
@ExceptionHandler(value={Exception.class}) //此处是Exception.class,所以可以处理所有的异常
public String handlerException(Exception ex,HttpServletRequest req){
req.setAttribute("ex", ex);
return "user/error"; //转发到error.jsp,${error.message}输出错误信息
}
}
add.jsp:
<%@ page language="java" pageEncoding="UTF-8"%>
<%@ taglib prefix="sf" uri="http://www.springframework.org/tags/form" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>title</title> <link rel="stylesheet" href="/springMVC2/resource/css/main.css" type="text/css"/> <!-- 此处是mapping位置 -->
</head>
<body>
<sf:form method="post" modelAttribute="user" >
username:<sf:input path="username"/> <sf:errors path="username"/><br>
password:<sf:password path="password"/><sf:errors path="password"/><br>
email:<sf:input path="email"/><sf:errors path="email"/><br>
<input type="submit" value="提交" />;
</sf:form>
</body>
</html>
文件上传:用到commons-fileupload.jar,commons-io.jar
@RequestMapping(value="/upload", method = RequestMethod.POST)//要求form表单以post方式上传
public String handleUploadData(String name,@RequestParam("file") CommonsMultipartFile file){//多文件 多文件MultipartFile[] file用for处理
if (!file.isEmpty()) {
String path = this.servletContext.getRealPath("/tmp/"); //应放在指定的静态资源文件中<mvc:resource />,获取本地存储路径 存储在了D:\study\apache-tomcat-6.0.35\webapps\test\tmp
System.out.println(path);
String fileName = file.getOriginalFilename();//获取文件名
String fileType = fileName.substring(fileName.lastIndexOf("."));
System.out.println(fileType);
File file2 = new File(path,new Date().getTime() + fileType); //新建一个文件
try {
file.getFileItem().write(file2); //将上传的文件写入新建的文件中
} catch (Exception e) {
e.printStackTrace();
}
return "redirect:upload_ok.jsp";
}else{
return "redirect:upload_error.jsp";
}
}