知識庫 / Spring / Spring Boot RSS 訂閱

React 和 Spring Boot CRUD 應用

REST,Spring Boot
HongKong
4
03:40 AM · Dec 06 ,2025

1. 引言

在本教程中,我們將學習如何創建一個能夠創建、檢索、更新和刪除(CRUD)客户端數據的應用程序。該應用程序將包含一個簡單的 Spring Boot RESTful API 以及使用 React JavaScript 庫實現的用户界面(UI)。

2. Spring Boot

Spring Boot 是一個讓構建獨立、生產就緒 Spring 應用程序變得簡單起來的框架。它通過自動化配置和依賴注入,減少了配置的複雜性,並提供了快速原型設計和開發的功能。

Spring Boot 提供了以下核心特性:

  • Auto-Configuration: Spring Boot 會自動配置 Spring 應用,根據 classpath 中依賴的庫和配置,推斷出合適的配置。
  • Starter Projects: Spring Boot 提供了各種 Starter 項目,可以快速啓動一個特定類型的 Spring 應用,例如 Web 應用、REST API 應用等。
  • Actuator: Spring Boot Actuator 提供了豐富的監控和管理功能,例如健康檢查、指標收集、日誌管理等。
  • Embedded Servers: Spring Boot 可以嵌入 Tomcat、Jetty 等 Servlet 服務器,無需單獨部署 WAR 文件。
  • Simplified Development: Spring Boot 提供了簡化的開發流程,例如自動配置、快速啓動等,提高了開發效率。

Spring Boot 旨在簡化 Spring 應用的開發和部署,讓開發者能夠專注於業務邏輯的實現。

2.1. Maven 依賴

讓我們先在我們的 pom.xml文件中添加幾個依賴:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <version>3.1.5</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
        <version>3.1.5</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <version>3.1.5</version>
        <scope>test</scope>
    </dependency>

    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <version>2.1.214</version>
        <scope>runtime</scope>
    </dependency>
</dependencies>

我們添加了Web、測試和JPA持久化啓動器,以及H2依賴項,因為應用程序將使用H2內存數據庫。

2.2. 創建模型

接下來,讓我們創建一個 Client 實體類,包含 name 和 email 屬性,以表示我們的數據模型:

@Entity
@Table(name = "client")
public class Client {

    @Id
    @GeneratedValue
    private Long id;

    private String name;
    private String email;

    // getter, setters, contructors
}

2.3. 創建倉庫

然後我們將創建我們的 ClientRepository 類,JpaRepository 繼承 以提供 JPA CRUD 功能:

public interface ClientRepository extends JpaRepository<Client, Long> {
}

2.4. 創建 REST 控制器

最後,通過創建控制器來與 ClientRepository 交互,從而暴露一個 REST API

@RestController
@RequestMapping("/clients")
public class ClientsController {

    private final ClientRepository clientRepository;

    public ClientsController(ClientRepository clientRepository) {
        this.clientRepository = clientRepository;
    }

    @GetMapping
    public List<Client> getClients() {
        return clientRepository.findAll();
    }

    @GetMapping("/{id}")
    public Client getClient(@PathVariable Long id) {
        return clientRepository.findById(id).orElseThrow(RuntimeException::new);
    }

    @PostMapping
    public ResponseEntity createClient(@RequestBody Client client) throws URISyntaxException {
        Client savedClient = clientRepository.save(client);
        return ResponseEntity.created(new URI("/clients/" + savedClient.getId())).body(savedClient);
    }

    @PutMapping("/{id}")
    public ResponseEntity updateClient(@PathVariable Long id, @RequestBody Client client) {
        Client currentClient = clientRepository.findById(id).orElseThrow(RuntimeException::new);
        currentClient.setName(client.getName());
        currentClient.setEmail(client.getEmail());
        currentClient = clientRepository.save(client);

        return ResponseEntity.ok(currentClient);
    }

    @DeleteMapping("/{id}")
    public ResponseEntity deleteClient(@PathVariable Long id) {
        clientRepository.deleteById(id);
        return ResponseEntity.ok().build();
    }
}

2.5. 啓動我們的 API

完成上述步驟後,我們現在可以開始啓動我們的 Spring Boot API。我們可以使用 spring-boot-maven-plugin 來實現:

mvn spring-boot:run

然後,我們就可以通過訪問 http://localhost:8080/clients 獲取客户列表。

2.6. 創建客户端

此外,我們還可以使用 Postman 創建一些客户端:

curl --location 'http://localhost:8080/clients' \ --header 'Content-Type: application/json' \ --data-raw '{"name": "John Doe", "email": "[email protected]"}'

3. React

React 是一個用於創建用户界面的 JavaScript 庫。 使用 React 需要安裝 Node.js。 您可以在 Node.js 下載頁面 找到安裝説明。

3.1. 創建 React UI

Create React App 是一個命令行工具,為我們生成 React 項目。 讓我們在我們的 Spring Boot 應用基礎目錄下,通過運行以下命令創建我們的 前端 應用:

npx create-react-app frontend

在完成應用程序創建流程後,我們將安裝 BootstrapReact Routerreactstrap 目錄下的 前端 目錄:

npm install --save [email protected] [email protected] [email protected] [email protected]

我們將使用 Bootstrap 的 CSS 和 reactstrap 的組件來創建一個更美觀的用户界面,並使用 React Router 組件來處理應用程序中的導航。

讓我們在 app/src/index.js 中添加 Bootstrap 的 CSS 文件作為導入。

import 'bootstrap/dist/css/bootstrap.min.css';

3.2. 啓動我們的 React UI

現在我們準備好啓動我們的 前端應用程序:

npm start

當通過瀏覽器訪問 http://localhost:3000 時,我們應該看到 React 示例頁面:

3.3. 調用我們的 Spring Boot API

調用我們的 Spring Boot API 需要在 React 應用的 package.json文件中配置代理,以便在調用 API 時進行配置。

為此,我們將 API 的 URL 添加到 package.json中:

...
"proxy": "http://localhost:8080",
...

接下來,我們編輯 frontend/src/App.js,使其調用我們的 API 以顯示具有 nameemail 屬性的客户端列表:

class App extends Component {
  state = {
    clients: []
  };

  async componentDidMount() {
    const response = await fetch('/clients');
    const body = await response.json();
    this.setState({clients: body});
  }

  render() {
    const {clients} = this.state;
    return (
        <div className="App">
          <header className="App-header">
            <img src={logo} className="App-logo" alt="logo" />
            <div className="App-intro">
              <h2>Clients</h2>
              {clients.map(client =>
                  <div key={client.id}>
                    {client.name} ({client.email})
                  </div>
              )}
            </div>
          </header>
        </div>
    );
  }
}
export default App;

componentDidMount 函數中,我們調用客户端 API 並將響應體設置為 clients 變量。 在我們的 render 函數中,我們返回包含 API 中找到的客户端列表的 HTML。

我們將看到客户端頁面,它將如下所示:

注意:請確保 Spring Boot 應用程序正在運行,以便 UI 能夠調用 API。

3.4. 創建 ClientList 組件

我們可以現在改進我們的UI,以顯示一個 更復雜的組件,該組件用於 列出編輯刪除創建 客户,利用我們的API。稍後,我們將看到如何使用此組件並從 App 組件中刪除客户列表。

讓我們在 frontend/src/ClientList.js 文件中創建一個文件:

import React, { Component } from 'react';
import { Button, ButtonGroup, Container, Table } from 'reactstrap';
import AppNavbar from './AppNavbar';
import { Link } from 'react-router-dom';

class ClientList extends Component {

    constructor(props) {
        super(props);
        this.state = {clients: []};
        this.remove = this.remove.bind(this);
    }

    componentDidMount() {
        fetch('/clients')
            .then(response => response.json())
            .then(data => this.setState({clients: data}));
    }
}
export default ClientList;

如在 App.js 中所示,componentDidMount 函數正在調用我們的 API 以加載客户端列表。

我們還將包含 remove 函數來處理當我們要刪除客户端時,對 API 的 DELETE 調用。此外,我們還將創建 render 函數,該函數將使用 EditDeleteAdd Client 操作渲染 HTML。

async remove(id) {
    await fetch(`/clients/${id}`, {
        method: 'DELETE',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json'
        }
    }).then(() => {
        let updatedClients = [...this.state.clients].filter(i => i.id !== id);
        this.setState({clients: updatedClients});
    });
}

render() {
    const {clients, isLoading} = this.state;

    if (isLoading) {
        return <p>Loading...</p>;
    }

    const clientList = clients.map(client => {
        return <tr key={client.id}>
            <td style={{whiteSpace: 'nowrap'}}>{client.name}</td>
            <td>{client.email}</td>
            <td>
                <ButtonGroup>
                    <Button size="sm" color="primary" tag={Link} to={"/clients/" + client.id}>Edit</Button>
                    <Button size="sm" color="danger" onClick={() => this.remove(client.id)}>Delete</Button>
                </ButtonGroup>
            </td>
        </tr>
    });

    return (
        <div>
            <AppNavbar/>
            <Container fluid>
                <div className="float-right">
                    <Button color="success" tag={Link} to="/clients/new">Add Client</Button>
                </div>
                <h3>Clients</h3>
                <Table className="mt-4">
                    <thead>
                    <tr>
                        <th width="30%">Name</th>
                        <th width="30%">Email</th>
                        <th width="40%">Actions</th>
                    </tr>
                    </thead>
                    <tbody>
                    {clientList}
                    </tbody>
                </Table>
            </Container>
        </div>
    );
}

3.5. 創建 ClientEdit 組件

ClientEdit 組件將負責 創建和編輯我們的客户端

讓我們在 frontend/src/ClientEdit.js 中創建一個文件:

import React, { Component } from 'react';
import { Link, withRouter } from 'react-router-dom';
import { Button, Container, Form, FormGroup, Input, Label } from 'reactstrap';
import AppNavbar from './AppNavbar';

class ClientEdit extends Component {

    emptyItem = {
        name: '',
        email: ''
    };

    constructor(props) {
        super(props);
        this.state = {
            item: this.emptyItem
        };
        this.handleChange = this.handleChange.bind(this);
        this.handleSubmit = this.handleSubmit.bind(this);
    }
}
export default withRouter(ClientEdit);

讓我們添加<em >componentDidMount</em>函數,以檢查我們是否正在處理創建或編輯功能;如果是編輯,它將從API獲取我們的客户端:

async componentDidMount() {
    if (this.props.match.params.id !== 'new') {
        const client = await (await fetch(`/clients/${this.props.match.params.id}`)).json();
        this.setState({item: client});
    }
}

然後,在 handleChange 函數中,我們將更新組件的狀態項屬性,該屬性將在提交表單時使用:

handleChange(event) {
    const target = event.target;
    const value = target.value;
    const name = target.name;
    let item = {...this.state.item};
    item[name] = value;
    this.setState({item});
}

handeSubmit 中,我們將調用我們的 API,通過 PUTPOST 方法將請求發送出去,具體取決於我們正在調用的功能。為此,我們可以檢查 id 屬性是否已填充:

async handleSubmit(event) {
    event.preventDefault();
    const {item} = this.state;

    await fetch('/clients' + (item.id ? '/' + item.id : ''), {
        method: (item.id) ? 'PUT' : 'POST',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json'
        },
        body: JSON.stringify(item),
    });
    this.props.history.push('/clients');
}

最後,但並非最不重要的,我們的 渲染 函數將處理我們的表單:

render() {
    const {item} = this.state;
    const title = <h2>{item.id ? 'Edit Client' : 'Add Client'}</h2>;

    return <div>
        <AppNavbar/>
        <Container>
            {title}
            <Form onSubmit={this.handleSubmit}>
                <FormGroup>
                    <Label for="name">Name</Label>
                    <Input type="text" name="name" id="name" value={item.name || ''}
                           onChange={this.handleChange} autoComplete="name"/>
                </FormGroup>
                <FormGroup>
                    <Label for="email">Email</Label>
                    <Input type="text" name="email" id="email" value={item.email || ''}
                           onChange={this.handleChange} autoComplete="email"/>
                </FormGroup>
                <FormGroup>
                    <Button color="primary" type="submit">Save</Button>{' '}
                    <Button color="secondary" tag={Link} to="/clients">Cancel</Button>
                </FormGroup>
            </Form>
        </Container>
    </div>
}

注意:我們還提供一個 鏈接,該鏈接在點擊 取消按鈕時,將返回 /clients 目錄。

3.6. 創建 AppNavbar 組件

為了使我們的應用程序具有更好的可導航性,讓我們在 frontend/src/AppNavbar.js 中創建一個文件:

import React, {Component} from 'react';
import {Navbar, NavbarBrand} from 'reactstrap';
import {Link} from 'react-router-dom';

export default class AppNavbar extends Component {
    constructor(props) {
        super(props);
        this.state = {isOpen: false};
        this.toggle = this.toggle.bind(this);
    }

    toggle() {
        this.setState({
            isOpen: !this.state.isOpen
        });
    }

    render() {
        return <Navbar color="dark" dark expand="md">
            <NavbarBrand tag={Link} to="/">Home</NavbarBrand>
        </Navbar>;
    }
}

渲染 函數中,我們將使用 react-router-dom 的功能來創建一個 Link,以便將應用程序路由到我們的 Home 頁面。

3.7. 創建我們的 Home 組件

該組件將是我們的應用程序的 Home 頁面,並且將有一個按鈕指向我們之前創建的 ClientList 組件。

讓我們在 frontend/src/Home.js 中創建一個文件:

import React, { Component } from 'react';
import './App.css';
import AppNavbar from './AppNavbar';
import { Link } from 'react-router-dom';
import { Button, Container } from 'reactstrap';

class Home extends Component {
    render() {
        return (
            <div>
                <AppNavbar/>
                <Container fluid>
                    <Button color="link"><Link to="/clients">Clients</Link></Button>
                </Container>
            </div>
        );
    }
}
export default Home;

注意:在本組件中,我們還擁有一個從 react-router-dom 提供的 鏈接,它將引導我們前往 /clients 路由。 此路由將在下一步中進行配置。

3.8. 使用 React Router

現在我們將使用 React Router 在我們的組件之間進行導航。

讓我們修改我們的 <em >App.js</em >>

import React, { Component } from 'react';
import './App.css';
import Home from './Home';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import ClientList from './ClientList';
import ClientEdit from "./ClientEdit";

class App extends Component {
  render() {
    return (
        <Router>
          <Switch>
            <Route path='/' exact={true} component={Home}/>
            <Route path='/clients' exact={true} component={ClientList}/>
            <Route path='/clients/:id' component={ClientEdit}/>
          </Switch>
        </Router>
    )
  }
}

export default App;

如我們所見,我們為我們創建的每個組件都定義了應用程序路由。

通過訪問localhost:3000,我們現在擁有帶有主頁客户鏈接的頁面:

點擊客户鏈接,我們現在擁有客户列表,以及編輯刪除添加客户功能:

4. 構建與打包

為了使用 Maven 構建和打包我們的 React 應用程序,我們將使用 frontend-maven-plugin

該插件將負責將我們的 前端應用程序打包並複製到我們的 Spring Boot API 構建文件夾中:

<properties>
    ...
    <frontend-maven-plugin.version>1.6</frontend-maven-plugin.version>
    <node.version>v14.8.0</node.version>
    <yarn.version>v1.12.1</yarn.version>
    ...
</properties>
...
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-resources-plugin</artifactId>
            <version>3.1.0</version>
            <executions>
                ...
            </executions>
        </plugin>
        <plugin>
            <groupId>com.github.eirslett</groupId>
            <artifactId>frontend-maven-plugin</artifactId>
            <version>${frontend-maven-plugin.version}</version>
            <configuration>
                ...
            </configuration>
            <executions>
                ...
            </executions>
        </plugin>
        ...
    </plugins>
</build>

讓我們更詳細地瞭解一下我們的 maven-resources-plugin,它負責將我們的 前端資源複製到應用程序的 目標文件夾中:

...
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-resources-plugin</artifactId>
    <version>3.1.0</version>
    <executions>
        <execution>
            <id>copy-resources</id>
            <phase>process-classes</phase>
            <goals>
                <goal>copy-resources</goal>
            </goals>
            <configuration>
                <outputDirectory>${basedir}/target/classes/static</outputDirectory>
                <resources>
                    <resource>
                        <directory>frontend/build</directory>
                    </resource>
                </resources>
            </configuration>
        </execution>
    </executions>
</plugin>
...

我們的 front-end-maven-plugin 將負責安裝 Node.jsYarn,然後構建和測試我們的 前端 應用程序:

...
<plugin>
    <groupId>com.github.eirslett</groupId>
    <artifactId>frontend-maven-plugin</artifactId>
    <version>${frontend-maven-plugin.version}</version>
    <configuration>
        <workingDirectory>frontend</workingDirectory>
    </configuration>
    <executions>
        <execution>
            <id>install node</id>
            <goals>
                <goal>install-node-and-yarn</goal>
            </goals>
            <configuration>
                <nodeVersion>${node.version}</nodeVersion>
                <yarnVersion>${yarn.version}</yarnVersion>
            </configuration>
        </execution>
        <execution>
            <id>yarn install</id>
            <goals>
                <goal>yarn</goal>
            </goals>
            <phase>generate-resources</phase>
        </execution>
        <execution>
            <id>yarn test</id>
            <goals>
                <goal>yarn</goal>
            </goals>
            <phase>test</phase>
            <configuration>
                <arguments>test</arguments>
                <environmentVariables>
                    <CI>true</CI>
                </environmentVariables>
            </configuration>
        </execution>
        <execution>
            <id>yarn build</id>
            <goals>
                <goal>yarn</goal>
            </goals>
            <phase>compile</phase>
            <configuration>
                <arguments>build</arguments>
            </configuration>
        </execution>
    </executions>
</plugin>
...

注意:要指定不同的 Node.js 版本,只需在我們的 pom.xml 中的 node.version 屬性中進行編輯即可。

5. 運行我們的 Spring Boot React CRUD 應用

最後,通過添加插件,我們可以通過運行以下命令訪問我們的應用程序:

mvn spring-boot:run

我們的React應用程序將在http://localhost:8080/與我們的API完全集成。

6. 結論

在本文中,我們探討了如何使用 Spring Boot 和 React 創建 CRUD 應用程序。為此,我們首先創建了一些 REST API 端點,以便與我們的數據庫進行交互。然後,我們創建了一些 React 組件,用於使用我們的 API 檢索和寫入數據。我們還學習瞭如何將 Spring Boot 應用程序與我們的 React UI 組合成一個單一的應用程序包。

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

發佈 評論

Some HTML is okay.