知識庫 / Spring / Spring Boot RSS 訂閱

構建基於 Spring Boot 和 Angular 的 Web 應用程序

Spring Boot,Spring Web
HongKong
7
01:26 PM · Dec 06 ,2025

1. 概述

Spring Boot 和 Angular 構成一個強大的組合,非常適合開發具有最小足跡的 Web 應用程序。

在本教程中,我們將使用 Spring Boot 來實現 RESTful 後端,以及 Angular 來創建基於 JavaScript 的前端。

2. Spring Boot 應用

我們的演示 Web 應用的功能將非常簡化。它將被限制為從內存中的 H2 數據庫中檢索和顯示一個 List JPA 實體,並通過簡單的 HTML 表單持久化新的實體。

2.1. Maven 依賴項

以下是我們的 Spring Boot 項目的依賴項:

<dependency> 
    <groupId>org.springframework.boot</groupId> 
    <artifactId>spring-boot-starter-web</artifactId> 
</dependency>
<dependency> 
    <groupId>org.springframework.boot</groupId> 
    <artifactId>spring-boot-starter-data-jpa</artifactId> 
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>runtime</scope>
</dependency>

請注意,我們包含了 spring-boot-starter-web,因為我們將使用它來創建 REST 服務,以及 spring-boot-starter-jpa 來實現持久化層。

H2 數據庫的版本也由 Spring Boot 父項目管理。

2.2. JPA 實體類

為了快速原型我們的應用程序領域層,我們定義一個簡單的 JPA 實體類,該類將負責建模用户:

@Entity
public class User {
    
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;
    private final String name;
    private final String email;
    
    // standard constructors / setters / getters / toString
}

2.3. UserRepository 接口

由於我們需要對 User 實體進行基本的 CRUD 功能,因此我們也必須定義一個 UserRepository 接口:

@Repository
public interface UserRepository extends CrudRepository<User, Long>{}

2.4. REST 控制器

現在我們來實施 REST API。在這種情況下,它只是一個簡單的 REST 控制器:

@RestController
@CrossOrigin(origins = "http://localhost:4200")
public class UserController {

    // standard constructors
    
    private final UserRepository userRepository;

    @GetMapping("/users")
    public List<User> getUsers() {
        return (List<User>) userRepository.findAll();
    }

    @PostMapping("/users")
    void addUser(@RequestBody User user) {
        userRepository.save(user);
    }
}

UserController 類的定義本身並沒有什麼內在的複雜性。

當然,這裏值得注意的實現細節是 使用 @CrossOrigin 註解。正如其名稱所示,該註解允許在服務器端啓用跨源資源共享 (CORS)。

這一步並非總是必要的,但由於我們正在將 Angular 前端部署到 http://localhost:4200,以及 Boot 後端部署到 http://localhost:8080瀏覽器否則會拒絕來自一個到另一個的請求。

關於控制器方法,getUser() 方法從數據庫中檢索所有 User 實體。 類似地,addUser() 方法將新實體持久化到數據庫中,該實體通過請求體傳遞。

為了保持簡單,我們故意省略了在持久化實體之前觸發 Spring Boot 驗證的控制器實現。 然而,在生產環境中,我們不能僅依賴用户輸入,因此服務器端驗證應成為一項強制性功能。

2.5. 啓動 Spring Boot 應用程序

最後,讓我們創建一個標準的 Spring Boot 啓動類,並使用幾個 User 實體填充數據庫:

@SpringBootApplication
public class Application {

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

    @Bean
    CommandLineRunner init(UserRepository userRepository) {
        return args -> {
            Stream.of("John", "Julie", "Jennifer", "Helen", "Rachel").forEach(name -> {
                User user = new User(name, name.toLowerCase() + "@domain.com");
                userRepository.save(user);
            });
            userRepository.findAll().forEach(System.out::println);
        };
    }
}

現在讓我們運行該應用程序。正如預期的那樣,在啓動時,我們應該在控制枱中看到一個 User 實體列表:

User{id=1, name=John, [email protected]}
User{id=2, name=Julie, [email protected]}
User{id=3, name=Jennifer, [email protected]}
User{id=4, name=Helen, [email protected]}
User{id=5, name=Rachel, [email protected]}

3. The Angular Application

With our demo Spring Boot application up and running, we can now create a simple Angular application capable of consuming the REST controller API.

3.1. Angular CLI 安裝

我們將使用 Angular CLI,一個強大的命令行工具,來創建我們的 Angular 應用。

Angular CLI 是一種極其有價值的工具,因為它 允許我們從頭開始創建整個 Angular 項目,只需幾個命令即可生成組件、服務、類和接口

在安裝了 npm(Node 包管理器)之後,我們將打開命令行控制枱並輸入以下命令:

npm install -g @angular/[email protected]

這就是全部。上述命令將安裝 Angular CLI 的最新版本。

3.2. 使用 Angular CLI 進行項目腳手架搭建

我們可以從零開始生成 Angular 應用的結構,但誠然,這是一種容易出錯且耗時的任務,我們應該儘量避免。

相反,我們讓 Angular CLI 為我們完成繁重的工作。因此,我們可以打開命令行控制枱,然後導航到希望創建應用程序的文件夾,並輸入以下命令:

ng new angularclient

命令將生成整個應用程序結構,位於 angularclient 目錄下。

3.3. Angular 應用的入口點

如果我們在 angularclient 文件夾中查看,我們會發現 Angular CLI 實際上為我們創建了一個完整的項目。

Angular 應用的文件使用 TypeScript,這是一種對 JavaScript 進行類型推斷的超集,編譯後生成為普通 JavaScript。然而,任何 Angular 應用的入口點都是一個簡單的 index.html 文件。

讓我們編輯這個文件:

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Spring Boot - Angular Application</title>
  <base href="/">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" type="image/x-icon" href="favicon.ico">
  <link rel="stylesheet" 
    href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" 
    integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"
    crossorigin="anonymous">
</head>
<body>
  <app-root></app-root>
</body>
</html>

如上所示,我們包含了 Bootstrap 4,以便為我們的應用程序 UI 組件提供更精緻的外觀。當然,我們還可以從眾多可用的 UI 套件中選擇一個。

請注意,在 <body> 部分中,自定義的標籤 <app-root></app-root> 看起來有些奇怪,因為 <app-root> 不是標準的 HTML5 元素。

我們將保留這些標籤,因為 <app-root> 是 Angular 用於渲染應用程序根組件的根選擇器

3.4. app.component.ts 根組件

為了更好地理解 Angular 如何將 HTML 模板綁定到組件,請進入 src/app 目錄並編輯 app.component.ts TypeScript 文件,即根組件:

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {

  title: string;

  constructor() {
    this.title = 'Spring Boot - Angular Application';
  }
}

出於明顯原因,我們不會深入學習 TypeScript。即便如此,我們注意到該文件定義了一個 AppComponent 類,它聲明瞭一個 title 字段,類型為 string (小寫)。 毋庸置疑,這仍然是 JavaScript。

此外,構造函數將該字段初始化為一個 string 值,這與我們在 Java 中所做的事情非常相似。

最相關的部分是 @Component 元數據標記或裝飾器,它定義了三個元素:

  1. selector – 用於將組件與 HTML 模板文件綁定所使用的 HTML 選擇器
  2. templateUrl – 與組件關聯的 HTML 模板文件
  3. styleUrls – 與組件關聯的一個或多個 CSS 文件

正如預期的那樣,我們可以使用 app.component.htmlapp.component.css 文件來定義根組件的 HTML 模板和 CSS 樣式。

最後,selector 元素將整個組件綁定到 <app-root> 選擇器中,該選擇器包含在 index.html 文件中。

3.5. <em >app.component.html</em > 文件

由於 <em >app.component.html</em > 文件允許我們定義根組件的 HTML 模板,因此我們將使用它來創建一個基本的導航欄,其中包含兩個按鈕。

如果單擊第一個按鈕,Angular 將顯示一個包含數據庫中存儲的 <em >User</em > 實體列表的表格。 類似地,如果單擊第二個按鈕,它將渲染一個 HTML 表單,我們可以使用它來向數據庫添加新實體:

<div class="container">
  <div class="row">
    <div class="col-md-12">
      <div class="card bg-dark my-5">
        <div class="card-body">
          <h2 class="card-title text-center text-white py-3">{{ title }}</h2>
          <ul class="text-center list-inline py-3">
            <li class="list-inline-item">
              <a routerLink="/users" class="btn btn-info">List Users</a>
                </li>
            <li class="list-inline-item">
              <a routerLink="/adduser" class="btn btn-info">Add User</a>
                </li>
          </ul>
        </div>
      </div>
      <router-outlet></router-outlet>
    </div>
  </div>
</div>

文件的大部分內容是標準 HTML,需要注意一些事項。

首先,有 {{ title }} 表達式。雙花括號 {{ variable-name }} 是 Angular 用於變量插值的佔位符。

請記住,AppComponent 類將 title 字段初始化為 Spring Boot – Angular Application。因此,Angular 將在模板中顯示該字段的值。同樣,在構造函數中更改該值將反映在模板中。

其次,需要注意的是 routerLink 屬性。

Angular 使用此屬性通過路由模塊路由請求 (稍後會詳細介紹)。 至少要知道,模塊將將請求分發到 /users 路徑到特定組件,以及將請求到 /adduser 到另一個組件。

在每種情況下,與匹配組件關聯的 HTML 模板將在 <router-outlet></router-outlet> 佔位符中進行渲染。

3.6. 用户類

由於我們的 Angular 應用將從數據庫中獲取和持久化 User 實體,我們來實施一個簡單的領域模型,使用 TypeScript。

請打開終端控制枱並創建一個 model 目錄:

ng generate class user

Angular CLI 將生成一個空 User 類,我們來填充一些字段:

export class User {
    id: string;
    name: string;
    email: string;
}

3.7. UserService 服務

有了我們客户端領域中的 User 類已經設置好,現在我們可以實現一個服務類,該類執行 GET 和 POST 請求到 http://localhost:8080/users 端點。

這將允許我們封裝對 REST 控制器的訪問到一個單一的類中,我們可以在整個應用程序中重用它。

打開一個控制枱終端,然後創建一個 service 目錄,並在該目錄中執行以下命令:

ng generate service user-service

現在讓我們打開 Angular CLI 創建的 user.service.ts 文件並進行重構:

import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { User } from '../model/user';
import { Observable } from 'rxjs/Observable';

@Injectable()
export class UserService {

  private usersUrl: string;

  constructor(private http: HttpClient) {
    this.usersUrl = 'http://localhost:8080/users';
  }

  public findAll(): Observable<User[]> {
    return this.http.get<User[]>(this.usersUrl);
  }

  public save(user: User) {
    return this.http.post<User>(this.usersUrl, user);
  }
}

我們無需紮實的 TypeScript 基礎就能理解 UserService 類的工作原理。簡單來説,它在可重用組件中封裝了 所有用於消費我們之前在 Spring Boot 中實現的 REST 控制器 API 所需的功能

findAll() 方法通過 http://localhost:8080/users 端點,向其發起一個 GET HTTP 請求。該方法利用 Angular 的 HttpClient,並返回一個 Observable 實例,該實例包含一個 User 對象數組。

同樣,save() 方法也執行一個 POST HTTP 請求到 http://localhost:8080/users 端點。

通過在 HttpClient 的請求方法中指定類型 User,我們可以更輕鬆、更有效地消費後端響應。

最後,請注意使用 @Injectable() 元數據標記。這表明服務應該通過 Angular 的依賴注入器 創建和注入。

3.8. <em UserListComponent</em>> 組件

在這一方案中,<em UserService</em>> 類是 REST 服務與應用程序呈現層之間的薄中層。因此,我們需要定義一個負責渲染數據庫中存儲的 <em User</em> 實體列表的組件。

請打開終端控制枱,然後創建一個名為 <em user-list</em>> 的目錄,並生成用户列表組件:

ng generate component user-list

Angular CLI 將生成一個空組件類,該類實現了 ngOnInit 接口。該接口聲明瞭一個鈎子 ngOnInit() 方法,Angular 在完成實例化該實現類以及調用其構造函數之後會調用該方法。

讓我們重構該類,使其可以在構造函數中接受一個 UserService 實例:

import { Component, OnInit } from '@angular/core';
import { User } from '../model/user';
import { UserService } from '../service/user.service';

@Component({
  selector: 'app-user-list',
  templateUrl: './user-list.component.html',
  styleUrls: ['./user-list.component.css']
})
export class UserListComponent implements OnInit {

  users: User[];

  constructor(private userService: UserService) {
  }

  ngOnInit() {
    this.userService.findAll().subscribe(data => {
      this.users = data;
    });
  }
}

<p><em>UserListComponent</em> 類的實現相當直觀。它僅僅使用<em>UserService</em> 的 <em>findAll()</em> 方法來檢索數據庫中持久化的所有實體,並將它們存儲在 <em>users</em> 字段中。</p> <p>此外,我們需要編輯組件的 HTML 文件,<em>user-list.component.html</em>,以創建顯示實體的表格:</p>

<div class="card my-5">
  <div class="card-body">
    <table class="table table-bordered table-striped">
      <thead class="thead-dark">
        <tr>
          <th scope="col">#</th>
          <th scope="col">Name</th>
          <th scope="col">Email</th>
        </tr>
      </thead>
      <tbody>
        <tr *ngFor="let user of users">
          <td>{{ user.id }}</td>
          <td>{{ user.name }}</td>
          <td><a href="mailto:{{ user.email }}">{{ user.email }}</a></td>
        </tr>
      </tbody>
    </table>
  </div>
</div>

我們應注意使用 ngFor 指令。該指令被稱為循環器,我們可以使用它來迭代變量的內容並迭代渲染 HTML 元素。在本例中,我們將其用於動態渲染表格的行。

此外,我們還使用變量插值來顯示每個用户的idnameemail

3.9. UserFormComponent 組件

類似於地,我們需要創建一個組件,以便在數據庫中持久保存新的 User 對象。

讓我們創建一個 user-form 目錄,並輸入以下內容:

ng generate component user-form

接下來,我們打開 user-form.component.ts 文件,併為 UserFormComponent 類添加一個用於保存 User 對象的類方法:

import { Component } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { UserService } from '../service/user.service';
import { User } from '../model/user';

@Component({
  selector: 'app-user-form',
  templateUrl: './user-form.component.html',
  styleUrls: ['./user-form.component.css']
})
export class UserFormComponent {

  user: User;

  constructor(
    private route: ActivatedRoute, 
      private router: Router, 
        private userService: UserService) {
    this.user = new User();
  }

  onSubmit() {
    this.userService.save(this.user).subscribe(result => this.gotoUserList());
  }

  gotoUserList() {
    this.router.navigate(['/users']);
  }
}

在這種情況下,UserFormComponent 也接受一個 UserService 實例作為構造函數中的參數,而 onSubmit() 方法則利用該實例來保存提供的 User 對象。

由於我們需要在保存新實體後重新顯示實體列表,因此在插入新實體後,我們調用 gotoUserList() 方法,該方法將用户重定向到 users 路徑。

此外,還需要編輯 user-form.component.html 文件,並創建用於在數據庫中持久化新用户的 HTML 表單:

<div class="card my-5">
  <div class="card-body">
    <form (ngSubmit)="onSubmit()" #userForm="ngForm">
      <div class="form-group">
        <label for="name">Name</label>
        <input type="text" [(ngModel)]="user.name" 
          class="form-control" 
          id="name" 
          name="name" 
          placeholder="Enter your name"
          required #name="ngModel">
      </div>
      <div [hidden]="!name.pristine" class="alert alert-danger">Name is required</div>
      <div class="form-group">
        <label for="email">Email</label>
        <input type="text" [(ngModel)]="user.email" 
          class="form-control" 
          id="email" 
          name="email" 
          placeholder="Enter your email address"
          required #email="ngModel">
        <div [hidden]="!email.pristine" class="alert alert-danger">Email is required</div>
      </div>
      <button type="submit" [disabled]="!userForm.form.valid" 
        class="btn btn-info">Submit</button>
    </form>
  </div>
</div>

一 glance,表單看起來相當標準,但它在幕後封裝了大量的 Angular 功能。

請注意使用 ngSubmit 指令,該指令在表單提交時調用 onSubmit() 方法。

接下來,我們定義了模板變量 #userForm,因此 Angular 會自動添加 NgForm 指令,允許我們跟蹤整個表單。

NgForm 指令持有我們為表單元素創建的控制,並帶有 ngModel 指令和 name 屬性。它還監控它們的屬性,包括它們的狀態。

ngModel 指令為我們提供了 雙向數據綁定 功能,在表單控件和客户端領域模型(User 類)之間。

這意味着表單輸入字段中輸入的數據會流向模型,反之亦然。兩個元素中的更改將通過 DOM 操作立即反映出來。

此外,ngModel 允許我們跟蹤每個表單控件的狀態,並通過向每個控件添加不同的 CSS 類和 DOM 屬性來執行 客户端驗證

在上面的 HTML 文件中,我們使用了應用於表單控件的屬性,僅用於在值發生更改時顯示警報框。

3.10. <em app-routing.module.ts</em> 文件

儘管組件在隔離環境中功能正常,但我們仍然需要使用機制來在用户點擊導航欄按鈕時調用它們。

這正是 <a href="https://angular.io/api/router/RouterModule" rel="noopener noreferrer">RouterModule</a> 發揮作用的地方。讓我們打開 <em app-routing.module.ts</em> 文件並配置模塊,以便它可以將請求分發到匹配的組件:

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { UserListComponent } from './user-list/user-list.component';
import { UserFormComponent } from './user-form/user-form.component';

const routes: Routes = [
  { path: 'users', component: UserListComponent },
  { path: 'adduser', component: UserFormComponent }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

如上所示,Routes 數組指示路由器在用户點擊鏈接或在瀏覽器地址欄中指定 URL 時應顯示哪個組件。

一個路由由兩部分組成:

  1. 路徑 – 一個與瀏覽器地址欄中 URL 匹配的 字符串
  2. 組件 – 在路由激活(導航)時創建的組件

如果用户點擊 列出用户 按鈕,該按鈕鏈接到 /users 路徑,或在瀏覽器地址欄中輸入 URL,路由器將渲染 UserListComponent 組件的模板文件,並將其放置在 <router-outlet> 佔位符中。

同樣,如果他們點擊 添加用户 按鈕,它將渲染 UserFormComponent 組件。

3.11. app.module.ts 文件

我們需要編輯 app.module.ts 文件,以便 Angular 可以導入所有必需的模塊、組件和服務。

此外,我們需要指定用於創建和注入 UserService 類的提供者。 否則,Angular 將無法將其注入到組件類中:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { AppComponent } from './app.component';
import { UserListComponent } from './user-list/user-list.component';
import { UserFormComponent } from './user-form/user-form.component';
import { UserService } from './service/user.service';

@NgModule({
  declarations: [
    AppComponent,
    UserListComponent,
    UserFormComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    HttpClientModule,
    FormsModule
  ],
  providers: [UserService],
  bootstrap: [AppComponent]
})
export class AppModule { }

4. 運行應用程序

最後,我們準備好運行我們的應用程序。

要實現這一點,首先運行 Spring Boot 應用程序,以便 REST 服務處於活動狀態並監聽請求。

在 Spring Boot 應用程序啓動後,我們打開命令行控制枱並輸入以下命令:

ng serve --open

這將啓動 Angular 的實時開發服務器,並同時在 http://localhost:4200 打開瀏覽器。

我們應該看到帶有列出現有實體和添加新實體的按鈕的導航欄。 如果我們點擊第一個按鈕,我們應該在導航欄下方看到一個表格,其中包含數據庫中持久化的實體的列表:

類似地,點擊第二個按鈕將顯示用於持久化新實體的 HTML 表單:

結論

在本文中,我們學習瞭如何使用 Spring Boot 和 Angular 構建一個基本的 Web 應用程序

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.