spring4 mvc 快速入门 - spring boot or not?

来源:互联网 发布:淘宝为什么让马云有钱 编辑:程序博客网 时间:2024/05/16 15:46

spring mvc 简洁易学。但自从 spring 4 开始,案例都采用 spring boot,无论老鸟菜鸟都懵了啊。本文介绍 mvc 的框架原理,使用简单案例分别使用spring boot 容器、其他 servlet 容器(jetty)的xml配置、无 xml 配置实现,讨论 spring4 mvc 不同环境实现程序之间的区别和联系。由于每种方式不是代码兼容的(蛋疼),这对 servlet 程序员来说是痛苦的负担,两套体系的挣扎。但对于新人,则可以选择自己喜欢的方式快速实现 web mvc 编程。 

一、 MVC 的概念

1. 什么是 MVC

    MVC是一种结构风格,就是将程序的输入、处理、输出这个顺序过程抽象为模型、视图、控制三个部分。程序员仅需关注业务的数据、展现、流程三个元素,将 Context,通讯协议、进程与线程、会话等复杂的要素交给框架处理,实现业务为中心的编程理念。在 web 应用中,MVC的含义是:

  • M(模型/Model):业务涉及的数据对象实例。例如,你显示一定订单,它包含用户、订单、订单项、支付等业务对象数据;
  • V(视图/View):展示业务人机交互界面的显示模板。例如,jsp 文件等,它能将模型中的数据填入显示模板,用户可看到界面元素丰富的界面;
  • C(控制器/Controller):用户输入处理单元。它检查输入的合法性,处理输入表单,按流程调用业务函数,生成输出需要的数据模型,选择视图模板,输出。

使用MVC结构编程,使得程序业务逻辑清晰,模块结构好。对提升开发效率,增强可维护性,促进团队内部按技能分工,起到关键作用,因此几乎所有语言都有自己若干不同的 MVC 支持框架实现。

2. MVC 在 web 程序中的位置

    对于新手,常会对某种语言或 MVC 框架产生崇拜,例如学了 MVC 就掌握了 web 编程,喜欢对比 struts 和 spring mvc 的效率。本节的目的是供新手了解 MVC 结构在 web 程序中的位置。附图表示了 MVC 结构在 web 开发中的位置。

    无论是 J2EE 或 轻量级 web 应用,大概分为三个层次:

  • 表示层(present layer): 处理 HTTP 输入(Request),然后调用业务服务,产生输出(Response)。这里,应用开发人员只需要考虑 MVC 三个编程元素。
  • 业务层(business layer): 提供满足 ACID 要求的业务服务。这里,开发人员只需声明对外的业务服务函数,spring 等提供事务(Transaction)支持。
  • 持久化层(persistence layer):提供数据表存取机制,主要是 ORM 框架实现以对象-关系数据库的映射。这里,开发人员只需声明表和对象的映射,特殊的 SQL。

因此,MVC结构仅是 web 开发中表示层技术。

    JAVA 所有框架都尊崇这样的理念,“应用开发人员关注于业务逻辑的实现而不是底层的实现机制”。JAVA 开发一般是最佳实践驱动,即文件名、类名、目录结构、包结构都有自己一套长期形成的默认规则,这是初学者最大的学习障碍(demo 程序不一定遵守这些规则)。最佳的办法就是在看靠谱的项目代码,学习 Java 设计模式。例如:一个类中出现 process(...) 方法,你立马就知道这时命令模式(command pattern)的实现,必定实现了一个 xxxCommand 接口。 

3. MVC 部件执行过程

    各种语言 web MVC 框架不可胜数,但执行过程都是一致的。

  1.  分配/映射:这时框架完成的,它根据客户端 URL 映射到你定义的控制器类(使用 @ 或 外部 xml),做部分 Valid 检查
  2. 控制器:执行业务流程,一般步骤是:
    1. 参数检查
    2. 业务服务访问
    3. 装配数据模型
    4. 选择输出模板
  3. 模板渲染:模板按数据模型提供的数据渲染,生成网页或特定数据格式

因此,你需要一些 HTTP 基础知识;URL 到函数的映射;模板渲染表达式。

4. 选择 MVC 框架

    尽管 spring MVC 的效率是一直被质疑,反射用的越多的框架效率也差一些,但使用会更方便。

    选择 MVC 框架第一准则是团队培训成本,即你团队最优秀的人会什么,大家都用什么。第二准则是简单实用,简单实用是相对感受=没标准,个人喜欢 HTTP Request 与 处理函数的 Mapping 模型(如 spring MVC),这与 Restful Service 开发泛型是一致的,是现代 web 开发最流行的方式。

二、用 spring boot 实现 spring4 mvc

这里使用的 Spring 官方案例,参见:http://spring.io/guides/gs/serving-web-content/  。可直接从 GIT 下载代码。

开发环境 JAVA JDK 1.8, Maven 3.3 +。 如果你使用 Centos 7,可参见:http://blog.csdn.net/pmlpml/article/details/53427164

1. 程序结构

对于刚学 Java 的来说,这与普通 Java 程序几乎没有区别。程序在 /src/main/java 源代码目录下;静态资源与视图模板都 /src/main/resources 目录下;maven 文件 pom.xml。

2. 控制器与模型

@Controllerpublic class GreetingController {    @RequestMapping("/greeting")    public String greeting(@RequestParam(value="name", required=false, defaultValue="World") String name, Model model) {        model.addAttribute("name", name);        return "greeting";    }}

GreetingController 代码非常简单,要点:

  • @Controller 注释申明这个类是控制器
  • @RequestMapping("/greeting") 注释申明 URL 匹配 “/greeting” 给这个函数处理。
  • @RequestParam(value="name", required=false, defaultValue="World") 注释申明如果查询字符串中有 name=value 则 value 转为 String 给 name 这个参数。例如:URL 是  /greeting?name=User 则变量 name = “User” 。
  • model 是一个容器,存放变量供视图模板使用
  • 返回 “greeting” 是模板名称。

3. 视图

greeting.html 在资源文件夹模板目录下:

<!DOCTYPE HTML><html xmlns:th="http://www.thymeleaf.org"><head>     <title>Getting Started: Serving Web Content</title>     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /></head><body>    <p th:text="'Hello, ' + ${name} + '!'" /></body></html>

这是 thymeleaf 风格的模板。其中 th:text=“表达式” 表示修改 <p> 元素的 text 属性。 ${name} 从模型中获取对象 name。

4. 程序启动代码

@SpringBootApplicationpublic class Application {    public static void main(String[] args) {        SpringApplication.run(Application.class, args);    }}

非常简单,要点:

  • @SpringBootApplication 注释申明了这个类,等价于四个注释。
  • main()方法中,SpringApplication.run() 这个配置的程

其实,这个程序有许多默认的配置,如:模板的目录,静态文件的目录。如果要修改这些,可能要读 spring boot 上百页文档。例如,要添加一个过滤器?请阅读:http://docs.spring.io/spring-boot/docs/1.4.2.RELEASE/reference/htmlsingle/#boot-features-embedded-container

这对熟悉 servlet 的程序猿,无疑是痛苦的负担,除非你打算成为 spring boot 的专责程序猿。

5. pom.xml

<?xml version="1.0" encoding="UTF-8"?><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/xsd/maven-4.0.0.xsd">    <modelVersion>4.0.0</modelVersion>    <groupId>org.springframework</groupId>    <artifactId>spring-mvc-demo</artifactId>    <version>0.1.0</version>    <parent>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-parent</artifactId>        <version>1.4.1.RELEASE</version>    </parent>    <dependencies>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-thymeleaf</artifactId>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-devtools</artifactId>            <optional>true</optional>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-test</artifactId>            <scope>test</scope>        </dependency>    </dependencies>    <properties>        <java.version>1.8</java.version>    </properties>    <build>        <plugins>            <plugin>                <groupId>org.springframework.boot</groupId>                <artifactId>spring-boot-maven-plugin</artifactId>            </plugin>        </plugins>    </build></project>

它最大好处就是依赖配置简单,几乎 spring 所有模块都下载了。

6. 运行

一般情况下,使用 maven 运行,debug 除外。在 pom.xml 目录输入命令:

mvn spring-boot:run

程序运行,web 服务器启动在 8080 端口。输入:

curl http://localhost:8080/greeting?name=User

三、用传统 XML 配置实现 spring4 mvc

1. 程序结构

这是一个传统的 Spring MVC 程序,也是标准的 web servlet 2.3 + 的程序结构。java 源代码目录 /src/main/java;默认资源目录变为 /src/main/webapp/WEB-INF,其中 web.xml 是 servlet 应用配置,hello-servlet.xml 是 spring beans 的配置。

2. web servlet 应用启动配置

<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" ><web-app>    <servlet>        <servlet-name>hello</servlet-name>        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>        <load-on-startup>1</load-on-startup>    </servlet>    <servlet-mapping>        <servlet-name>hello</servlet-name>        <url-pattern>/</url-pattern>    </servlet-mapping>           <!-- spring mvc 静态资源 404问题 : http://blog.csdn.net/this_super/article/details/7884383 -->    <servlet-mapping><servlet-name>default</servlet-name><url-pattern>*.html</url-pattern>    </servlet-mapping>      <welcome-file-list>          <welcome-file>index.html</welcome-file>       </welcome-file-list></web-app>

这对新手可能理解困难点。servlet 启动了 DispatcherServlet,它的名字 hello,处理的 url 是 “/” 开头的所有 URL,静态资源使用 default Servlet。

由于没有定义<listener>, DispatcherServlet 默认采用 name-servlet.xml 最为 spring 配置文件,这里是 hello-servlet.xml.

3. spring 配置

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context"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/mvc         http://www.springframework.org/schema/mvc/spring-mvc.xsd        http://www.springframework.org/schema/context         http://www.springframework.org/schema/context/spring-context.xsd">  <context:annotation-config/> <mvc:annotation-driven/>        <context:component-scan base-package="hello"/><!-- SpringResourceTemplateResolver automatically integrates with Spring's own --><!-- resource resolution infrastructure, which is highly recommended.          --><bean id="templateResolver"       class="org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver">  <property name="prefix" value="/WEB-INF/templates/" />  <property name="suffix" value=".html" />  <!-- HTML is the default value, added here for the sake of clarity.          -->  <property name="templateMode" value="HTML" />  <!-- Template cache is true by default. Set to false if you want             -->  <!-- templates to be automatically updated when modified.                    -->  <property name="cacheable" value="true" /></bean>    <!-- SpringTemplateEngine automatically applies SpringStandardDialect and      --><!-- enables Spring's own MessageSource message resolution mechanisms.         --><bean id="templateEngine"      class="org.thymeleaf.spring4.SpringTemplateEngine">  <property name="templateResolver" ref="templateResolver" />  <!-- Enabling the SpringEL compiler with Spring 4.2.4 or newer can speed up  -->  <!-- execution in most scenarios, but might be incompatible with specific    -->  <!-- cases when expressions in one template are reused across different data -->  <!-- ypes, so this flag is "false" by default for safer backwards            -->  <!-- compatibility.                                                          -->  <property name="enableSpringELCompiler" value="true" /></bean>   <bean id="viewResolver" class="org.thymeleaf.spring4.view.ThymeleafViewResolver">  <property name="templateEngine" ref="templateEngine" /></bean></beans>

这里配置略微复杂,要点:

  • <context:annotation-config/> 对应 @configuration
  • <mvc:annotation-driven/> 对应 @EnableWebMvc
  • <context:component-scan base-package="hello"/> 对应 @ComponentScan
  • 后面是 thymeleaf 与 spring4 集成需要的配置。 参见 thymeleaf 官网

这样,Spring Dispatcher 就能使用 @注释,分配/映射 URL 并使用 thymeleaf 引擎渲染页面。

 4. 控制器 

@Controllerpublic class GreetingController {    @RequestMapping("/greeting")    public String greeting(@RequestParam(value = "name", required = false, defaultValue = "World") String name,            Model model) {        model.addAttribute("name", name);        return "greeting";    }    @RequestMapping("/hello")    public void sayHello(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        response.setContentType("text/html");        response.setStatus(HttpServletResponse.SC_OK);        response.getWriter().println("<h1>Hello ,!!!!</h1>");        response.getWriter().println("session=" + request.getSession(true).getId());    }    // http://stackoverflow.com/questions/7415084/spring-welcome-file-list-correct-mapping    @RequestMapping("/")    public String root() {        return "redirect:/index.html";    }}

控制器程序几乎没有变化。只是为了支持 welcome 页面,以及使用传统 servlet 方式给出了映射。

  • /hello 可以让你自由处理输入输出
  • / 可以定位到首页

视图,首页支持文件没有变化,只是改变了位置。

5. pom.xml

<?xml version="1.0" encoding="UTF-8"?><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/xsd/maven-4.0.0.xsd">   <modelVersion>4.0.0</modelVersion>   <groupId>com.my_project</groupId>   <artifactId>spring-mvc-demo-jetty</artifactId>   <version>0.1.0</version>   <properties>      <project.build.sourceEncoding>utf-8</project.build.sourceEncoding>      <jetty.version>9.3.7.v20160115</jetty.version>      <springframework.version>4.3.3.RELEASE</springframework.version>      <thymeleaf.version>3.0.2.RELEASE</thymeleaf.version>      <java.version>1.8</java.version>   </properties>   <dependencies>      <dependency>         <groupId>javax.servlet</groupId>         <artifactId>javax.servlet-api</artifactId>         <version>3.0.1</version>         <scope>provided</scope>      </dependency>      <dependency>         <groupId>org.springframework</groupId>         <artifactId>spring-webmvc</artifactId>         <version>${springframework.version}</version>         <scope>compile</scope>      </dependency>      <dependency>         <groupId>org.thymeleaf</groupId>         <artifactId>thymeleaf-spring4</artifactId>         <version>${thymeleaf.version}</version>      </dependency>   </dependencies>   <build>      <plugins>         <plugin>            <groupId>org.eclipse.jetty</groupId>            <artifactId>jetty-maven-plugin</artifactId>            <version>${jetty.version}</version>         </plugin>      </plugins>   </build></project>
这个程序最大的缺点就是你必须知道 Java 包之间的依赖,并明白依赖的包是否在运行时需要或仅编译时需要。这对入门者非常不友好!!!

6.运行

运行命令:

mvn jetty:run

服务器也启动在 http://localhost:8080 端口。

四、无 XML 配置实现 spring4 mvc 使用 jetty

从 spring3 开始,Spring 实现了无 xml 配置,包括对 servlet 3+ 规范对 WebContext (web.xml)可编程支持。对 Spring3+ mvc 的核心接口是 WebApplicationInitializer 。实现该接口,就可以直接启动 web 应用,并进行 WebContext 的配置。一般继承该接口的 AbstractDispatcherServletInitializer 实现类,配置 web context。 

1. 程序结构

这个程序非常类似 spring-boot 的结构,仅是多了 config 包下两个 Java 类

2. 配置类(Java-based Configuration Class)

SpringServletInitializer 其实就是 web.xml 的 Java 形式。这里仅是比前面多了常用的 UTF-8 汉字支持的过滤器。

public class SpringServletInitializer extends AbstractDispatcherServletInitializer {    public static final String CHARACTER_ENCODING = "UTF-8";    public SpringServletInitializer() {        super();    }    protected WebApplicationContext createServletApplicationContext() {        final AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();        context.register(SpringWebConfig.class);        // context.register(PersistenceJPAConfig.class);        return context;    }    protected WebApplicationContext createRootApplicationContext() {        return null;    }    protected String[] getServletMappings() {        return new String[] { "/" };    }    @Override    protected Filter[] getServletFilters() {        final CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter();        encodingFilter.setEncoding(CHARACTER_ENCODING);        encodingFilter.setForceEncoding(true);        return new Filter[] { encodingFilter };    }}

SpringWebConfig 类就是 hello-servlet.xml 配置形式 

@Configuration@EnableWebMvc@ComponentScan("hello")public class SpringWebConfig        extends WebMvcConfigurerAdapter implements ApplicationContextAware {    private ApplicationContext applicationContext;    public SpringWebConfig() {        super();    }    public void setApplicationContext(final ApplicationContext applicationContext)            throws BeansException {        this.applicationContext = applicationContext;    }    /* ******************************************************************* */    /*  GENERAL CONFIGURATION ARTIFACTS                                    */    /*  Static Resources, i18n Messages, Formatters (Conversion Service)   */    /* ******************************************************************* */    /**     *  Dispatcher configuration for serving static resources     */    @Override    public void addResourceHandlers(final ResourceHandlerRegistry registry) {        super.addResourceHandlers(registry);        registry.addResourceHandler("/images/**").addResourceLocations("classpath:/static/images/");        registry.addResourceHandler("/css/**").addResourceLocations("classpath:/static/css/");        registry.addResourceHandler("/js/**").addResourceLocations("classpath:/static/js/");        registry.addResourceHandler("/**/*.html").addResourceLocations("classpath:/static/");    }    /* **************************************************************** */    /*  THYMELEAF-SPECIFIC ARTIFACTS                                    */    /*  TemplateResolver <- TemplateEngine <- ViewResolver              */    /* **************************************************************** */    @Bean    public SpringResourceTemplateResolver templateResolver(){        // SpringResourceTemplateResolver automatically integrates with Spring's own        // resource resolution infrastructure, which is highly recommended.        SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();        templateResolver.setApplicationContext(this.applicationContext);        //templateResolver.setPrefix("/WEB-INF/templates/");        templateResolver.setPrefix("classpath:/templates/");        templateResolver.setSuffix(".html");        // HTML is the default value, added here for the sake of clarity.        templateResolver.setTemplateMode(TemplateMode.HTML);        // Template cache is true by default. Set to false if you want        // templates to be automatically updated when modified.        templateResolver.setCacheable(true);        return templateResolver;    }    @Bean    public SpringTemplateEngine templateEngine(){        // SpringTemplateEngine automatically applies SpringStandardDialect and        // enables Spring's own MessageSource message resolution mechanisms.        SpringTemplateEngine templateEngine = new SpringTemplateEngine();        templateEngine.setTemplateResolver(templateResolver());        // Enabling the SpringEL compiler with Spring 4.2.4 or newer can        // speed up execution in most scenarios, but might be incompatible        // with specific cases when expressions in one template are reused        // across different data types, so this flag is "false" by default        // for safer backwards compatibility.        templateEngine.setEnableSpringELCompiler(true);        return templateEngine;    }    @Bean    public ThymeleafViewResolver viewResolver(){        ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();        viewResolver.setTemplateEngine(templateEngine());        return viewResolver;    }}

你可能以注意到其中一些 "classpath:" 。这时找静态文件就在 resource 目录下了。

3. 控制器

@Controllerpublic class GreetingController {    @RequestMapping("/greeting")    public String greeting(@RequestParam(value = "name", required = false, defaultValue = "World") String name,            Model model) {        model.addAttribute("name", name);        return "greeting";    }    @RequestMapping("/hello")    public void sayHello(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        response.setContentType("text/html");        response.setStatus(HttpServletResponse.SC_OK);        response.getWriter().println("<h1>Hello ,!!!!</h1>");        response.getWriter().println("session=" + request.getSession(true).getId());    }    // http://stackoverflow.com/questions/7415084/spring-welcome-file-list-correct-mapping    @RequestMapping("/")    public String root() {        return "redirect:/index.html";    }}
你会发现这个程序与前面的控制器一样哦!

4. 运行

pom.xml 同 jetty 版本(XML 配置),资源文件没有变化。所以,运行程序依然是:

mvn jetty:run

服务器也启动在 http://localhost:8080 端口。

五、结论

本文给出了 Spring 官方 Hello world MVC 在 spring boot,jetty 容器中的实现。其中 jetty 中包含 xml 配置 和 无 xml 配置形式。总的感觉,没有常用案例支持,仅依靠官方文档(几乎都有说明,但难找到解决方案),不同形式的配置迁移是比较难配置和调试的。除了 Spring boot 应用, XML 与 java-based 配置几乎都是对应的。

  • XML 配置版本:可以使用 spring 2 - 4 任意版本,部署到任意 servlet 容器中运行,网上中文资料最丰富。缺点,程序变大后配置灾难在所难免;
  • 无 XML 配置版本:可以使用 spring 3 - 4 版本,部署到 servlet3+ 容器中运行,网上中文资料质量好坏难辨。非常合适以前的系统升级,也合适新手学习(配置类不同程序都类似,拷贝来就能用)
  • Spring boot 应用版本: 只能使用 sping 4 和 spring boot,网上中文资料几乎为零。胜在入门容易,几乎零配置,与 Node.js python 等比也算简单的。

【参考】

[1] Web MVC framework http://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html



0 0
原创粉丝点击