一. SpringMVC简介
Spring MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面。Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。使用 Spring 可插入的 MVC 架构,从而在使用Spring进行WEB开发时,可以选择使用Spring的SpringMVC框架或集成其他MVC开发框架,如Struts1,Struts2等。
1. Servlet 的缺点
(1)每个Servlet声明时需要在web.xml中配置8行代码,配置繁琐,内容多后,web.xml结构不清晰,不易维护。
(2)每个Servlet只有一个执行方法,如果要实现数据库表的CRUD操作,需要写4个Servlet。
(3)获取参数繁琐只能强制转类型,复杂的还需判断如日期类型转换,代码繁多重复手工劳动,非业务代码。
2. Struts2的缺点
虽然没有具体用过Struts2框架,但是对比Spring MVC框架,还是看到不少缺点评价,主要如下:
(1)学习成本高,学习难度大,很多复杂的原理
(2)值栈对象结构复杂,每个request创建时会自动创建值栈对象,效率低
(3)action声明xml配置,result返回值配置,配置多,团队开发冲突大,没有校验,常会因为配置出错,开发效率低。
(4)OGNL表达式非常复杂,极其难以掌握,性能低,不安全,全球性安全事故频出。
3. Spring MVC的优点(借用百度百科)
(1)它是一个典型的教科书式的mvc构架,而不像struts等都是变种或者不是完全基于mvc系统的框架,对于初学者或者想了解mvc的人来说 spring是最好的,它的实现就是教科书!
(2)它和tapestry一样是一个纯正的servlet系统,这也是它和tapestry相比 struts所具有的优势。而且框架本身有代码,看起来容易理解。
二. SpringMVC工作原理
SpringMVC核心组件:
1. 前端控制器(DispathcerServlet):负责Request对象和Response对象请求和响应
2. 处理器映射器(HandlerMapping):负责根据URL匹配对应的Controller
3. 处理器适配器(HandlerAdapter):内部选择合适的处理器,执行Controller
4. 视图解析器(ViewResolver):将用户返回的页面逻辑名称 index 拼接前缀和后缀 ,最终形成正确的路径 ,如: /WEB-INF/index.jsp
SpringMVC工作原理:
1. 用户发送请求:Url:localhost:8090/add.html 到前端控制器
2. 请求处理器映射器查询与之匹配的Controller
3. 处理器映射器根据url查询匹配的Controller路径返回给前端控制器
4. 请求处理器适配器处理Controller
5. 处理器适配器找到合适的处理器请求执行Url请求操作
6. 处理器处理之后返回ModelAndView对象
Model表示:请求的数据
View表示:给用户展现的页面的逻辑名称 index
7. 将ModelAndView对象返回给前端控制器
8. 请求视图解析器解析View对象
9. 将view对象中的页面逻辑名称转化为具体的页面路径返回 ,如:/WEB-INF/index.jsp
10. 将Model数据填充到View对象中,这时用户才能获取正确的信息,将数据填充到了Request域中.
三. SpringMVC配置过程
springmvc-config.xml:SpringMVC的核心配置文件
web.xml:web容器的核心配置文件
1. 在web容器中(web.xml)配置前端控制器,并把SpringMVC容器交给web容器管理,配置拦截路径为“/”都可被拦截:
<!-- 配置SpringMVC -->
<servlet>
<!-- 配置前端控制器 -->
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 指定SpringMVC配置文件,表示web容器加载SpringMVC容器 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/springmvc-config.xml</param-value>
</init-param>
<!-- "load-on-startup"标签用于标记容器是否在启动的时候就加载这个servlet -->
<!-- 当值为0或者大于0时,表示容器在应用启动时就加载这个servlet;当是一个负数时或者没有指定时,则指示容器在该servlet被选择时才加载 -->
<!-- 正数的值越小,启动该servlet的优先级越高 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
2. 在SpringMVC容器中(springmvc-config.xml)开启MVC注解和包扫描,配置视图解析器,并处理静态资源被“/”所拦截的问题:
<!-- 开启MVC注解形式 -->
<mvc:annotation-driven/>
<!-- 开启包扫描,使得@Controller注解生效 -->
<context:component-scan base-package="cn.gerui.panda.controller" />
<!-- 定义视图解析器 -->
<!-- 内部资源视图解析器规则:前缀+逻辑名+后缀 "/WEB-INF/pages/index.jsp" -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/pages/" />
<!-- 后缀 -->
<property name="suffix" value=".jsp" />
</bean>
<!-- 处理静态资源被“/”所拦截的问题 -->
<mvc:default-servlet-handler />
3. 在相应的Controller上配置映射关系,即可完成SpringMVC的简单配置,如:
@Controller
public class HelloController {
@RequestMapping("/index")
public String HelloWorld() {
return "Hello World ~";
}
}
四. 常见@RequestMapping的几种用法
1. 与RESTful架构一起使用,通过@PathVariable注解来绑定传参:
@RequestMapping("/users/{userId}")
public String findUser(@PathVariable String userId){
System.out.println("Find user with ID: " + userId);
return "Result";
}
2. 使用@ResponseBody注解,返回指定格式的数据:(将返回的java对象自动转换为json字符串)
@RequestMapping("/login")
@ResponseBody
public User login(User user){
return user;
}
User字段:username password,那么在前台接收到的数据为:'{"username":"xxx","password":"xxx"}'
解释:@ResponseBody注解的作用是将controller的方法返回的对象通过适当的转换器转换为指定的格式之后,写入到response对象的body区,通常用来返回JSON数据或者是XML数据。需要注意的是,在使用此注解之后不会再走视图解析器,而是直接将数据写入到输入流中,他的效果等同于通过response对象输出指定格式的数据。
效果等同于如下代码:
@RequestMapping("/login")
public void login(User user, HttpServletResponse response){
response.getWriter.write(JSONObject.fromObject(user).toString());
}
3. 使用@RequestBody注解,与前端页面进行数据交互,以特定格式接收数据:(接收的是一个Json对象的字符串,而不是一个Json对象)
@RequestMapping("/login")
public void login(@RequestBody User user){
System.out.println(user.username+" : "+user.password);
}
解释:@RequestBody注解常用来处理content-type不是默认的application/x-www-form-urlcoded编码的内容,比如说:application/json或者是application/xml等。一般情况下来说常用其来处理application/json类型。这里通过@RequestBody可以将请求体中的JSON字符串绑定到相应的bean上,当然,也可以将其分别绑定到对应的字符串上。
$.ajax({
url:"/login",
type:"POST",
data:'{"username":"admin","password":"admin123"}',
content-type:"application/json charset=utf-8",
success:function(data){
alert("request success !");
}
});
@RequestMapping("/login")
public void login(@RequestBody String username,@RequestBody String password){
System.out.println(username+" : "+password);
}
这种情况是将JSON字符串中的两个变量的值分别赋予了两个字符串,刚好与之对应的User类中包含了这两个变量,因此可以变成@RequestBody User user的形式, 这种形式会将JSON字符串中的值赋予user中对应的属性上。需要注意的是,JSON字符串中的key必须对应user中的属性名,否则是请求不过去的。
4. 使用@RequestParam注解,根据前端指定参数名来传入参数:
@RequestMapping("/register")
public String register(@RequestParam(value="u") String username, @RequestParam(value="p") String password) {
System.out.println(u+" : "+p);
return "index";
}
解释:上面的对传入参数指定为u,如果前端不传u参数名,会出现报错。
注意:当参数中写有required=false时,表示不传值的话会给参数赋值为null,而required=true时就必须要传值。
五. SpringMVC注解方式的工作原理
1. 启动tomcat,加载项目的web.xml创建并初始化spring容器,同时创建springmvc容器。
2. 创建完springmvc容器后,进行包扫描,扫描所有的controller。
3. 找到配置的包路径下的所有含有@Controller的类,然后扫描这个类的所有@RequestMapping的映射,把这个注解形成一个map,这个map的key就是@RequestMapping中的字符串,val就是@RequestMapping所在类的所在方法。
4. 然后当用户在浏览器访问某个链接url时,先经过Servlet映射,如html结尾的,满足要求后进入DispatcherServlet。
5. 按映射规则去掉url中的协议、端口等,最终留下servlet映射后的部分,去map中找有无对应的key,如果有对应的key,就找到了对应类的对应的方法,就执行这个方法;如果没有找到,就抛出404错误。