同样的MVC,不同的实现方法(Spring MVC .Net MVC)
由于工作需要,最近将Net的MVC又重新好好的学习了一遍.学习教材是博客园里的大神的作品《ASP.NET MVC5框架揭秘》。
《ASP.NET MVC5框架揭秘》这本书,说了很多东西,但是总觉得太偏于理论,实际的例子太少。当然,框架揭秘 的确就应该是这个样子的,毕竟不是什么MVC的速成书籍。
公司以后接的活,其实不仅限于NET平台,JAVA平台也需要考虑的,所以,当前阶段Java的知识储备也是必须的。这篇文章和语言之争无关,希望不要引起大家的无谓的争论。学习SpringMVC使用的是ITEYE的大神 开涛的MVC系列教程,版本是Spring MVC2.5/3.0(最新版本是 4.1.2)
环境搭建:
VisualStudio不愧是宇宙第一IDE,NET MVC5的HelloWorld,就是傻瓜式的选选项目类型,然后按下F5就一切搞定了。
MVC5不但把后台的东西帮你准备好了,连前端的框架都替你决定了,bootstrap,Modernize,Jquery。然后你会看到package里面有一大堆东西,
ORM用的,前端用的,JSON,所有的东西都Standby了。如果哪位同学不喜欢bootstrap,想用UIKit,好吧,自己重写所有的模板吧。
自动生成的所有模板都是基于bootstrap的。然后Shard的TemplateEditor目录下面就会出现很多类似BooleanExEditor这样子的东西了。
Spring MVC
Java小白,只会用Elcapse,然后看教程,写了个HelloWorld。
国内的教程,大都将初学者的水平想得太高了,大段大段的XML配置文件加上简单的注解,就完事了。
最大的,最主要的目录结构,半句话都没有,你让我这样的小白,将一大堆xml配置文件放在什么地方好呢。最后只能yahoo.co.jp
找到一篇岛国人民的MVC入门文章,搞清楚了目录结构 http://qiita.com/siguremon/items/84c831391a6204079fd2
岛国人民写出来的入门教程,就是将学习对象当小白处理。然后教材写得是变态般的详细。目标是让欧巴和欧巴桑看了都能够编程。
[Maven和Nuget]
.NET有Nuget,Java这里有Maven,Maven的一个功能和Nuget一样,管理依赖项目。
在Maven的帮助下,Spring的MVC所需要的东西,也能够快速的准备齐全。
下面这个pom.xml里面包含了MySql的JDBC的导入
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.shchuwa</groupId> <artifactId>springmvc</artifactId> <packaging>war</packaging> <version>0.0.1-SNAPSHOT</version> <name>springmvc Maven Webapp</name> <url>http://maven.apache.org</url> <properties> <springframework.version>4.1.2.RELEASE</springframework.version> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${springframework.version}</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.0.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.34</version> </dependency> </dependencies> <build> <finalName>springmvc</finalName> </build> </project>
Spring的包,其实东西也不少JUnit,AOP,Log。。。
接管HTTP控制权
NET的MVC是通过HttpHandler在IIS管道中获得控制权的,让Route部件参与IIS管道,如果Route能匹配的话,就让MVC接手后续的所有事件。
这个过程在NETMVC的大神的书的第一章里说明得很好了。大部分人可能会觉得第一章无所谓,对于开发无关痛痒,但是,往往一些根本性的东西就在第一章里面。
Spring,其实也是差不多的思路,dispatcherServlet这个Servlet(org.springframework.web.servlet.DispatcherServlet)通过配置文件将自己作为Http的处理入口,Tomcat也好,JBoss也好,Apache也好,直接将Http请求转交给dispatcherServlet处理了。
掌握入口的感觉真心不错,能处理的处理,不能处理的,交给默认的Servlet处理
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" 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_2_5.xsd "> <display-name>spring-todo</display-name> <!-- (1) --> <servlet> <servlet-name>dispatcherServlet</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value> classpath:/META-INF/spring/beans-webmvc.xml </param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <!-- (2) --> <servlet-mapping> <servlet-name>dispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
关于路由
NET的MVC里面,有一个专门注册路由的地方,博客园都是MVC的高手,这里就不再啰嗦了。
所有的路由规则都统一放在一起管理,直接了当,一目了然。
Spring的Route规则是通过注解(NET的特性)的方式,写在Controller中的。
注意:RequestMapping就是Spring的路由
Spring在初始化的时候,会扫描指定位置下面所有的类,如果有@Controller的注解,就将其记录为Controller
然后会解析@RequestMapping,构建类似于RouteTable这样的东西。
而NET的话,约定了所有的Controller都放在指定的目录下面。都继承于Controller父类/接口。
当然,编码习惯好的Javer,也会把所有Controller放在同一个目录/Package的。
package controller; import static org.springframework.web.bind.annotation.RequestMethod.GET; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.RequestMapping; import model.person; // (1) @Controller public class HelloWorldController { // (2) @RequestMapping(value = "/", method = GET) public String home(ModelMap model) { // (3) person.insert(); model.addAttribute("message", "Spring 3 MVC Hello World"); return "helloworld"; } }
对于Controller的启动条件,除了GET/POST这样的检查之外,Spring的条件貌似更加丰富一些。NET的MVC是否有同样功能,我不知道。(或许可以使用拦截器)
1.可以要求请求的URL里面有某个参数(或者没有某个参数),进而可以控制参数的值是否等于某个值。
2.可以对请求类型进行控制。例如,下面是请求一个Json的例子,这里同时对Url和请求类型进行限制
@RequestMapping(value="/header/test3", headers = "Content-Type=application/json"):
数据绑定和验证
数据绑定这块,两者的处理都差不多,如果一个数据类型可以和字符进行相互转换,那么系统就会自动帮你做绑定。
如果不行的话,自己写一个类型和字符的转换类即可。
NET大神用的是 Point(int x,int y)的例子
Sping 大神用的是 PhoneNumber(int AreaCode,int Number)的例子。
一句话,内置的能转的类型,系统帮你做绑定,不能转的类型,你告诉我一个字符到类型的双向变换规则,然后注册到系统里面去,我也负责给你绑定。
Pattern pattern = Pattern.compile("^(\\d{3,4})-(\\d{7,8})$"); @Override public void setAsText(String text) throws IllegalArgumentException { if(text == null || !StringUtils.hasLength(text)) { setValue(null); //如果没值,设值为null } Matcher matcher = pattern.matcher(text); if(matcher.matches()) { PhoneNumberModel phoneNumber = new PhoneNumberModel(); phoneNumber.setAreaCode(matcher.group(1)); phoneNumber.setPhoneNumber(matcher.group(2)); setValue(phoneNumber); } else { throw new IllegalArgumentException(String.format("类型转换失败,需要格式 [010-12345678],但格式是[%s]", text)); } } @Override public String getAsText() { PhoneNumberModel phoneNumber = ((PhoneNumberModel)getValue()); return phoneNumber == null ? "" : phoneNumber.getAreaCode() + "-" + phoneNumber.getPhoneNumber(); }
内置的验证约束注解:
MVC.NET使用的应该是自己的东西,
Spring 使用的是Hibernate Validator,能够检查的东西都差不多吧。没有仔细研究。
题外话:NET里面的数据模型叫Model,Spring里面貌似叫做命令对象:Spring大神的原文如下
数据绑定:请求参数绑定到一个command object(命令对象,非GoF里的命令设计模式),这里的命令对象是指绑
定请求参数的任何POJO 对象;
拦截器
Spring 和Net一样都提供了拦截器来处理AOP
package org.springframework.web.servlet; public interface HandlerInterceptor { boolean preHandle( HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception; void postHandle( HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception; void afterCompletion( HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception; }
前两个没有什么好说的,处理前,处理后,最后一个NET中不知道怎么处理的。作用如下:
afterCompletion:整个请求处理完毕回调方法,即在视图渲染完毕时回调,如性能监控中我们可以在此记录结束时间 并输出消耗时间,还可以进行一些资源清理,类似于try-catch-finally中的finally,但仅调用处理器执行链中preHandle 返回true的拦截器的afterCompletion。
当然,Spring的拦截器也提供了取消功能,如果取消的话,整个处理就中止了(break)。拦截器的思想都差不多的,大家实现思想也都类似。
由于Spring在学习中,可能有理解错误的地方,希望大家指正。公司现在正在招募前端工程师,有兴趣的可以发个简历。工作地点是上海。
(更多内容以后更新)