Skip to content

4. 应用程序的运行

现在,我们希望在 IDE、STS(用于服务器)和 Webstorm(用于客户端)之外运行该应用程序。

4.1. 在 Tomcat 服务器上部署 Web 服务

我们在第 2.11.9 节中已了解如何为 Tomcat 创建 WAR 归档文件。此处我们将重复该操作。首先,为了保留现有内容,我们将 Eclipse 项目 [rdvmedecins-webapi-v3] 复制为 [rdvmedecins-webapi-v4]。

  

对文件 [pom.xml] 进行如下修改:


    <modelVersion>4.0.0</modelVersion>
    <groupId>istia.st.spring4.mvc</groupId>
    <artifactId>rdvmedecins-webapi-v4</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>

    <name>rdvmedecins-webapi-v3</name>
    <description>Gestion de RV Médecins</description>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.0.0.RELEASE</version>
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>istia.st.spring4.rdvmedecins</groupId>
            <artifactId>rdvmedecins-metier-dao-v2</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
</dependencies>

需在两个位置进行修改:

  • 第 5 行:需指定将生成一个 WAR 归档文件(Web ARchive);
  • 第 23-27 行:需添加对构建产物 [spring-boot-starter-tomcat] 的依赖。该构建产物将 Tomcat 的所有类引入到项目依赖中;
  • 第26行:该工件为[provided],这意味着相应的归档文件不会被放入生成的WAR包中。实际上,这些归档文件将位于运行该应用程序的Tomcat服务器上;

此外,还需配置 Web 应用程序。若不存在 [web.xml] 文件,则需通过继承 [SpringBootServletInitializer] 的类来实现:

  

[ApplicationInitializer]类的定义如下:


package rdvmedecins.web.config;

import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.web.SpringBootServletInitializer;

public class ApplicationInitializer extends SpringBootServletInitializer {
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(AppConfig.class);
    }

}
  • 第 6 行:类 [ApplicationInitializer] 继承自类 [SpringBootServletInitializer];
  • 第 8 行:方法 [configure] 被重新定义(第 7 行);
  • 第 9 行:提供了用于配置项目的类 [AppConfig];

完成上述操作后,可能需要更新Maven项目(我不得不这样做):[clic droit sur projet / Maven / Update project] 或 [Alt-F5]。

要运行该项目,可以按以下步骤操作:

  • 在 [1] 中,可在 Eclipse 中注册的任意服务器上运行该项目;
  • 在 [2] 中,选择默认存在的 [tc Server Developer]。这是 Tomcat 的一个变体;

结果如下:

 

这是正常的。需要注意的是,该Web服务的方法中不包含URL和[/]。当尝试调用URL和[/getAllMedecins]时,会得到以下响应:

 

这是正常的。Web 服务受保护。

现在在 Webstorm 中启动 [rdvmedecins-angular-v2] 客户端:

在 [1] 中,将新 Web 服务 [http://localhost:8080/rdvmedecins-webapi-v4] 的 URL 填入。结果如下:

Image

若要在 IDE STS 之外运行该应用程序,有多种解决方案。以下提供其中一种。

下载 Tomcat [http://tomcat.apache.org/download-80.cgi] 版本(2014 年 7 月):

在 [1] 中选择压缩包,解压后得到 [2]。返回 STS:

  • 在 [Servers] 选项卡中,右键单击应用程序 [rdvmedecins-webapi-v4],并选择 [Browse Deployment Location] 选项;
  • 在 [4] 中:复制文件夹 [rdvmedecins-webapi-v4];
  • 在 [5] 中,将文件夹 [rdvmedecins-webapi-v4] 粘贴到 Tomcat 的 [webapps] 文件夹中;
  • 在 [6] 中,执行命令文件 [startup.bat](此时 STS 中集成的 Tomcat 服务器必须已停止)。 此时将打开一个名为 DOS 的窗口,用于显示 Tomcat 日志。日志应显示应用程序 [rdvmedecins-webapi-v4] 已成功启动。

为验证此情况,请在 Webstorm 中再次运行 Angular 客户端 [rdvmedecins-angular-v2]:

在 [1] 中,将新 Web 服务 [http://localhost:8080/rdvmedecins-webapi-v4] 的 URL 作为参数传入。结果如下:

Image

4.2. 在 Tomcat 服务器上部署 Angular 客户端

既然Web服务已部署到Tomcat上,接下来我们将把Angular客户端也部署到服务器上。这完全可以是已经托管Web服务的同一台服务器。我们将采用这种方式。

首先,我们将客户端 [rdvmedecins-angular-v2] 复制为 [rdvmedecins-angular-v3],并进行以下修改:

  • 更名为 [1],所有内容已移至 [app] 文件夹 ;
  • 在 [1] 中,删除了包含项目所需各类库(CSS 和 JS)的 [bower-components] 文件夹。 所有这些内容已复制到文件夹 [lib] [2] 中;
  • 在 [1] 中,文件 [app.html] 已重命名为 [index.html];

文件 [index.html] 已进行修改,以适应所用资源路径的变化:


<!DOCTYPE html>
<html ng-app="rdvmedecins">
<head>
  <title>RdvMedecins</title>
...
  <!-- CSS -->
  ...
  <link href="lib/bootstrap-theme.min.css" rel="stylesheet"/>
  <link href="lib/bootstrap-select.min.css" rel="stylesheet"/>
</head>
<!-- 控制器 [appCtrl],模板 [app] -->
<body ng-controller="appCtrl">
<div class="container">
  ...
</div>
<!-- Bootstrap 核心 JavaScript ================================================== -->
<script type="text/javascript" src="lib/jquery.min.js"></script>
<script type="text/javascript" src="lib/bootstrap.min.js"></script>
<script type="text/javascript" src="lib/bootstrap-select.min.js"></script>
<script type="text/javascript" src="lib/footable.js"></script>
<!-- AngularJS -->
<script type="text/javascript" src="lib/angular.min.js"></script>
<script type="text/javascript" src="lib/ui-bootstrap-tpls.min.js"></script>
<script type="text/javascript" src="lib/angular-route.min.js"></script>
<script type="text/javascript" src="lib/angular-translate.min.js"></script>
<script type="text/javascript" src="lib/angular-base64.min.js"></script>
<!-- 模块 -->
...
<!-- 服务 -->
...
<!-- 指令 -->
...
<!-- 控制器 -->
....
</body>
</html>

此外,已修改控制器 [loginCtrl] 使其指向正确的服务器,以避免用户手动输入 URL:


// 凭据
app.serverUrl = "http://localhost:8080/rdvmedecins-webapi-v4";
app.username = "admin";
app.password = "admin";

完成上述操作后,执行文件 [index.html]:

然后连接到 Web 服务。应该可以正常运行。确认无误后,停止 Tomcat 服务器。我们将重新使用 STS 中的内置服务器。

在 STS 中,将 [rdvmedecins-angular-v3/app] 文件夹中的所有内容复制到 [rdvmedecins-webapi-v4] 项目的 [webapp] 文件夹中 (Navigator 选项卡)[1]:

完成上述操作后,启动 [2],即 STS 的 VMware 服务器,然后请求 URL [http://localhost:8080/rdvmedecins-webapi-v4/app/index.html]:

[3] 出现了权限问题。这并不奇怪,因为我们已对 Web 服务进行了保护。我们需要声明对文件 [/app/index.html] 的访问是开放的。回到 Eclipse:

  

请注意,访问权限已在类 [SecurityConfig] 中声明。请按以下方式修改该类:


    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // CSRF
        http.csrf().disable();
        // 密码通过 Authorization: Basic xxxx 头传递
        http.httpBasic();
        // 必须为所有人授权 HTTP OPTIONS 方法
        http.authorizeRequests() //
                .antMatchers(HttpMethod.OPTIONS, "/", "/**").permitAll();
        // 文件夹 [app] 对所有人开放
        http.authorizeRequests() //
                .antMatchers(HttpMethod.GET, "/app", "/app/**").permitAll();
        // 仅 ADMIN 角色可使用该应用程序
        http.authorizeRequests() //
                .antMatchers("/", "/**") // 所有 URL
                .hasRole("ADMIN");
}
  • 第 11-12 行:允许所有人读取文件夹 [app] 及其内容。修改时可参考前面的代码行。

现在,重新启动 STS 的 Tomcat 服务器,然后再次请求 URL [http://localhost:8080/rdvmedecins-webapi-v4/app/index.html]:

Image

这次成功了。

4.3. CORS 头部

大家可能还记得,我们曾为处理 CORS 头部信息费尽周折。在前面的示例中:

  • Web 服务位于 URL [http://localhost:8080/rdvmedecins-webapi-v4];
  • 客户端 HTML 位于 URL [http://localhost:8080/rdvmedecins-webapi-v4/app];

因此,客户端 HTML 和 Web 服务位于同一台服务器 [http://localhost:8080] 上。此时不会出现 CORS 冲突,因为此类冲突仅在客户端和服务器不在同一域时才会发生。我们应该能够验证这一点。 我们回到 STS:

  

是否生成 CORS 头部由 [ApplicationModel] 类中定义的一个布尔值控制:


    // 配置数据
    private boolean CORSneeded = true;

我们将上述布尔值设为false,重启Web服务,并再次请求URL [http://localhost:8080/rdvmedecins-webapi-v4/app/index.html]。可以看到应用程序运行正常。

4.4. 在 Android 平板电脑上部署 Angular 客户端

[Phonegap] [http://phonegap.com/] 工具可用于生成移动端可执行文件(Android、IoS、Windows 8、 ...)生成移动端可执行文件。 实现这一目标的方法有多种。我们采用最简单的方式:使用 Phonegap 网站上的在线工具 [http://build.phonegap.com/apps]。

  • 在 [1] 之前,您可能需要创建一个账户;
  • 在 [1] 步骤中,开始操作;
  • 在 [2] 步骤中,选择仅支持一个 Phonegap 应用的免费套餐;
  • 在 [3] 步骤中,下载压缩后的应用程序 [4](第 4.2 节创建的文件夹 [app] 已被压缩);
  • 生成 [5],为应用程序命名;
  • 在 [6] 中,构建该应用程序。此操作可能需要 1 分钟。请耐心等待,直到各移动平台的图标显示构建已完成;
  • 仅生成了 Android 二进制文件 [7] 和 Windows 二进制文件 [8];
  • 点击 [7] 下载 Android 二进制文件;
  • 将下载的二进制文件 [apk] 转换为 [9];

启动适用于 Android 平板电脑的 [GenyMotion] 模拟器(参见第 6.4 节):

 

上图展示了使用 Android 16 系统(API)启动平板模拟器的过程。模拟器启动后,

  • 请滑动侧边的锁定条(如有)将其解锁;
  • 用鼠标拖动您下载的 [PGBuildApp-debug.apk] 文件,并将其拖放到模拟器上。该文件随即会被安装并运行;

需将 URL 更改为 [1]。 为此,请在命令窗口中输入命令 [ipconfig](如下图第 1 行),该命令将显示您机器上的各个 IP 地址:


C:\Users\Serge Tahé>ipconfig

Configuration IP de Windows


Carte réseau sans fil Connexion au réseau local* 15 :

   Statut du média. . . . . . . . . . . . : Média déconnecté
   Suffixe DNS propre à la connexion. . . :

Carte Ethernet Connexion au réseau local :

   Suffixe DNS propre à la connexion. . . : ad.univ-angers.fr
   Adresse IPv6 de liaison locale. . . . .: fe80::698b:455a:925:6b13%4
   Adresse IPv4. . . . . . . . . . . . . .: 172.19.81.34
   Masque de sous-réseau. . . . . . . . . : 255.255.0.0
   Passerelle par défaut. . . . . . . . . : 172.19.0.254

Carte réseau sans fil Wi-Fi :

   Statut du média. . . . . . . . . . . . : Média déconnecté
   Suffixe DNS propre à la connexion. . . :

...

请记下以下地址之一:Wi-Fi 地址 IP(第 6-9 行),或局域网地址 IP(第 11-17 行)。 然后在 Web 服务器的 URL 中使用该地址 IP:

 

完成上述操作后,连接到 Web 服务:

在模拟器上测试应用程序。它应该可以正常运行。在服务器端,可以在类 [ApplicationModel] 中选择是否允许 CORS 头部:


    // 配置数据
    private boolean CORSneeded = false;

这对 Android 应用来说并不重要。该应用并非在浏览器中运行。而 CORS 头的要求来自浏览器,而非服务器。

4.5. 在 Android 智能手机模拟器上部署 Angular 客户端

我们使用智能手机模拟器重复上述操作。目的是验证客户端在小屏幕上的表现:

  • 在 [1] 中,我们启动了一个智能手机模拟器;
  • 在 [2] 和 [3] 中,导航栏已折叠至菜单中;
  • 在 [4] 中,正在登录;
  • 在 [5] 中,列表和日历是上下排列的,而不是并排的;
  • 在 [6] 中,查询日程表;
  • 在 [7] 中,由于屏幕过小,部分时段被遮挡。这是由 [footable] 库完成的;
  • 在 [8] 中,视图与之前相同,但这次显示了一个预约。

最终,我们的应用程序在智能手机上的适配效果相当不错。虽然还有改进空间,但目前已具备实用性。