JavaRush /Java Blog /Random-JA /コンクエスト スプリング ブーツ
Surplus
レベル 37
Москва

コンクエスト スプリング ブーツ

Random-JA グループに公開済み
親愛なる読者の皆さん、こんにちは!Spring Boot 開発との出会いについてのささやかな話題を取り上げる主な理由が尊大な名前だったとしても、初めまして。JavaRush ポータルでインターンシップの導入課題を完了した私の経験を共有したいと思います。これは、蓄積された知識の強さをテストしたいと考えているまったく普通の工業大学生の側から概要を示したいと思います。 コンクエスト スプリング ブーツ - 1添付されたコードや思考法に失礼な部分が含まれる可能性を私は決して否定しません。また、「こぶと傷」のおかげで専門的な方向に発展することが可能であるため、建設的な批判を歓迎します。さらに、私は与えられた条件を解決するための「万能薬」であるふりをまったくしておらず、プログラムの個々の断片を意図的に省略し、神経系にわずかな影響を与えることなく比較的複雑なトピックに入るという重要な点を残しています。確かに、明白なことを否定するのは無謀です。それは私にとって困難でしたし、ある瞬間まではまったく何も明らかではありませんでした。そして、その仕事に初めて出会ったときから同じような感情を抱いたなら、「ようこそ!」と言ってください。Thymeleafテンプレート エンジンとqueryローカル MySQL サーバーへのクエリを使用して、受信する情報の配列をフィルタリングするインターンシップ入学テストの単純化したアナロジーを使用して、Spring Boot で Web アプリケーションを作成してみましょう。それでは始めましょう!

スプリングブーツ。何の動物で、どうやって調理するのですか?

簡単に簡潔に言うと、これはアプリケーション作成プロセスの貴重な時間を節約するためのPivotelの優れたツールであり、サードパーティのライブラリに直接接続したり、印象的なマッピング キャンバスやサーブレットを作成したりする必要がなくなります。IntelliJ IDEA Ultimate Edition (ファイル - 新規 - プロジェクト... - Spring Initializr)に統合されているか、 start.spring.io Web サービス上にあるSpring Initializrビルダーを使用して、幅広い範囲から含めるパッケージを指定するだけで十分です。オファーします。
コンクエスト スプリング ブーツ - 2
提示された技術仕様に従って、 MySQLデータベースを使用して単純な Web アプリケーションを作成するための標準である紳士セットを使用します。
  • WEBは、標準アドレス localhost:8080 のローカル Apache Tomcat サーバーとユニバーサルSpring MVC フレームワークを含む、Web アプリケーションを開発するための主要コンポーネントです。

  • DevTools - コンパイルされたコードまたはテンプレートで変更が検出されたときに、ホット JVM でアプリケーションを迅速に再起動するために使用されます。さらに、選択したエンジンがプロジェクトに含まれている場合、Thymeleaf はキャッシュをクリアしなくても済みます。

  • JPA はデータベースを操作するために必要なテクノロジであり、Java オブジェクトのオブジェクト リレーショナル マッピングを提供し、エンティティを管理、保存、取得するための API (この場合はHibernate ) を提供します。

  • Thymeleaf (Mustache、AngularJS、Vaadin など) - アプリケーション視覚化のためのテンプレート エンジン。HTML の原則に比較的精通していたおかげで、私はこの言語を世界の基礎に押し上げた Thymeleaf を選びました。

  • MySQL - Java Database Connectivity ドライバーを接続して、データベースに対して SQL クエリを実行します。
コンポーネントを最終的に選択して作成すると、ディレクトリをさらに追加する準備ができた通常の Web アプリケーション アーキテクチャが得られます。CSS グラフィック スタイル、標準 HTML ページ、または JavaScript 機能など、ビジュアル部分と対話するためのフラグメントは「リソース」に配置する必要があり、バックエンド コンポーネントはそれに応じて「java」に配置することになります。ルート範囲にある pom.xml ファイルにも注意を払う必要があります。このファイルには、プロジェクトの構造とコンポーネント間の依存関係が保存されます。パッケージを追加してさらに機能を拡張したい場合や、不要なものを削除したい場合は、<dependencies></dependencies>同様の方法でタグ間の操作を行う必要があります。
コンクエスト スプリング ブーツ - 3

素晴らしい未来への第一歩

次に、かなり興味深く、非常に論理的な質問が生じます。これはどのように機能するのでしょうか? このプログラムは、モデル - ビュー - コントローラーの原則に基づいて構築されています。接続されたデータベース (モデル) からのエンティティの読み取りを整理し、コントロール (ビュー) を備えたユーザー インターフェイスに表示されます。コンポーネント間の通信と、送信されたリクエストに応じたアクションの実行は、コントローラーのおかげで実行されます。継続的な開発の基準点として機能する重要な要素の作成です。滑りやすい状況を避け、作業現場で同僚からの敬意を維持するには、コンポーネントを適切なディレクトリに配置し (たとえば、コントローラ ファイルを「java」ブランチのコントローラ フォルダに配置します)、慎重に保管する必要があります。職場での注文。

本質は大きなメカニズムの中の小さな部分です

言い換えれば、問題で設定された条件に従ったモデルです。議論のテーマから離れて、入門プロジェクトに戻りますが、タスク間には最小限の違いがあり、さらなるレビューでは平均的な概念に従うと自信を持って断言できます。たとえば、ノートブックに次のようなメモがあるとします。
  • 一般的なフロー内の位置を決定するための識別番号。
  • 特定の文字数のテキスト メッセージ。
  • ユーザーが一般リストに追加した日付。
  • 「完了または未完了」(「読み取りまたは未読み取り」)を決定するブール変数。
したがって、「entity」というディレクトリに Note クラスを作成し、適切なフィールドを追加しましょう。
@Entity
public class Note {

   @Id
   @GeneratedValue
   private int id;
   private String message;
   private Date date;
   private boolean done;

   public Note() {
   }

   public Note(String message) {
       this.message = message;
       this.date = new Date();
       this.done = false;
   }
}
理論的立場から何が起こっているかをより深く理解するための、議論のテーマからのもう 1 つの逸脱。Spring のコンポーネント間の接続は、アノテーション(オブジェクトの前にある特別なポインタ) によって指定されます。各ポインタはメカニズム内で特定の役割を果たし、「@」記号で始まります。@Entity アノテーションは、後続のクラスデータが「Entity」に属することを Spring Boot に示し、@Id と @GeneratedValue は、情報の配列を処理するときにイテレーターが自動生成される識別子として選択されたフィールドを指定します。ビジュアル形式のコンパクトさを高めるために、標準の Getter と Setter の追加を意図的に省略しています。次に、レコードを保存するためのデータベースの使用を考慮して、アプリケーション開発の次のステップに進みます。交換チェーンの接続要素である NoteRepository インターフェイスを「リポジトリ」ディレクトリに作成し、ほとんどの機能を継承します。さらなる作業に適したリポジトリ。アクセスする格納されたエンティティと整数イテレータを示します。
public interface NoteRepository extends JpaRepository<Note, Integer> {
}
実はそれだけです。簡潔かつ簡潔。これで、Spring Boot は作成されたコンポーネントを使用してデータベースとの対話を整理します。レガシー リポジトリには比較的多くの種類があり、さまざまなアクションの可能性があります。JpaRepository ははしごの最上部にあり、その下の CrudRepository と PageAndSortingRepository を含め、最も可能性が高くなります。微妙な点の一部は Pivotel の Web サイトの技術文書に記載されているため、これ以上先に進んでトピックから逸​​れることはありません。データ イメージを実装し、アプリケーション側で通信方法を指定した後、公式開発者からのアセンブリでデスクトップ プラットフォームにプレインストールされている適切な外部環境「MySQL Workbench」で MySQL データベースを作成することに注意する必要があります。ローカルサーバーを作成するための追加パッケージを使用:
コンクエスト スプリング ブーツ - 4
次に、メイン ウィンドウで現在のローカル サーバーのアイコンをクリックした後、環境の指示に従って、エンティティ (注) のフィールドに従ってテーブル図を作成し、適切なデータを入力します。MySQL 方言の微妙な点を個別に明確にする必要があります。望ましい結果を正常に達成するには緊急に注意が必要です。
  • 独立したブール型自体はありません。リクエスト処理アクションはすべて、「true」または「false」をビット値「1」または「0」にそれぞれ変換します。
  • 日付は完全に Timestamp 型で保存されます。根幹に馴染みのある Date を使用する場合は、カレンダー内の位置のみに制限する必要があります。
スプリングブーツの征服 - 5
準備手順が最終的に完了したら、ツールバーの「稲妻」アイコンをクリックして、「MySQL Workbench」を指定してローカル サーバーにデータを送信します。情報の追加が正しく完了したら、現在のデータベース構成を application.properties (通常は「resources」ディレクトリにあります) に追加することで、自信を持ってネイティブ IDE に戻って開発を続行できます。
spring.datasource.url=jdbc:mysql://localhost:3306/test?useSSL=false
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update
最後に、注釈を使用して Note エンティティを MySQL にバインドします。@Table は、選択した名前とスキーマを持つテーブルの使用を示し、@Column は変数が特定のフィールドに属することを示します。
@Entity
@Table(name = "test", schema = "test", catalog = "")
public class Note {

   @Id
   @GeneratedValue
   private int id;
   @Column(name = "message")
   private String message;
   @Column(name = "date")
   private Date date;
   @Column(name = "done")
   private boolean done;

   public Note() {
   }

   public Note(String message) {
       this.message = message;
       this.date = new Date();
       this.done = false;
   }
}

ビューまたはユーザーインターフェイス

悲しいことに、次のように言えるでしょう。「理論的または実践的な知識が少しでもなければ、アプリケーションの視覚化は主な障害になります。」正直に言うと、フロントエンド コンポーネントは総作業量のうち驚くほどの量を占め、長期間にわたって確実に私の神経をすり減らしました。しかし、Thymeleaf の驚くべきシンプルさのおかげで、一連の魅惑的な敗北の後に適切な妥協点を見つけることができました。一般的な概念は同様の立場にありますが、選択したエンジンの使用の複雑さについてはさらに説明します。主なテクニックは、最も純粋な HTML を使用し、同じセクションの複数の繰り返しを避けるために個々のフラグメントから最終的な表示を組み立てる機能です。UI アーキテクチャが、コントロール (新しいエントリの追加、メイン ページに戻る) を備えたナビゲーション バーと、メモが追加された時刻によって昇順 (ASC) で並べ替えられたエンティティを表示する動的テーブルで構成されるメイン ページで構成されていると仮定します。またはデクリメント (DESC) 方向の意味。すべてのレコードを昇順で表示することを標準とします。選択したテンプレート エンジンの階層ポリシーに従って、コンポーネントの視覚化要素は「resources」ディレクトリの「templates」ブランチに配置する必要があります。したがって、コンポーネントを使用したさらなる操作では、提示された条件が考慮されます。html5 テンプレート上に「index」という名前 (または個人の好みに応じて他の名前) でメイン ページを作成しましょう。例えば:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
     xmlns:th="http://www.thymeleaf.org">
<head th:replace="fragments/head :: head"></head>
<body>
<div class="container">
   <div th:replace="fragments/header :: header"></div>
   <div th:if="${not #lists.isEmpty(notes)}">
       <div th:replace="operations/list :: notebook"></div>
   </div>
   <div th:replace="fragments/footer :: footer"></div>
</div>
</body>
</html>
それでは、最終的なアプリケーションの主要なコンポーネントを分析してみましょう。Thymeleaf は、プロシージャの使用を示す別の構文を使用し、キーワード「th:」で始まります。ライブラリへのリンクは、必ず開始 <html> タグに含まれます。
<div th:if="${not #lists.isEmpty(notes)}">
「if」操作は通常の処理方法とまったく変わりません。受信した「notes」属性をチェックして、さらに表示するオブジェクトの存在を確認します。モデルと視覚化の相互作用を整理するためのコントローラの使用を考慮して、このトピックとコントローラの使用の重複については個別に言及する価値があります。多くの漠然とした瞬間が将来形になります。必要に応じて戻ってください。
<head th:replace="operations/list :: notebook"></head>
「置換」操作は、「スタブ」またはアクティブなブロックを、現在のページまたは別のページから選択されたフラグメントで置き換えることを示します。後者の場合は、この例で明確に観察されます。「notebook」というフラグメントを「operations」ディレクトリの「list.html」から「index」ファイルの<div></div>にコピーし、最終的な宛先のコンテンツを完全に置き換えます。送信されるものには次の内容が含まれます。
<!DOCTYPE html>
<!--suppress ALL -->
<html xmlns="http://www.w3.org/1999/xhtml"
     xmlns:th="http://www.thymeleaf.org">

<div th:fragment="notebook">
   <table class="table table-bordered table-hover horizontal-align">
       <thead>
       <tr>
           <th style="width: 5%">#</th>
           <th style="width: 60%">Message</th>
           <th class="dropdown" style="width: 20%">Date
               <a th:href="@{'/sort/{sortDate}' (sortDate = 'ASC')}"><i class="fa fa-chevron-circle-up"></i></a>
               <a th:href="@{'/sort/{sortDate}' (sortDate = 'DESC')}"><i class="fa fa-chevron-circle-down"></i></a>
           </th>
           <th style="width: 5%">Done</th>
           <th style="width: 5%">Edit</th>
           <th style="width: 5%">Delete</th>
       </tr>
       </thead>
       <tbody>
       <tr th:each="note : ${notes}">
           <td th:text="${note.id}" style="text-align: center">#</td>
           <td th:text="${note.message}">Message</td>
           <td th:text="${#dates.format(note.date, 'EEE, d MMM yyyy HH:mm')}" style="text-align: center">Date</td>
           <td style="text-align: center">
               <i th:if="${note.done} == true" class="fa fa-plus-square-o" style="font-size:20px;color:#337ab7"></i>
               <i th:if="${note.done} == false" class="fa fa-minus-square-o" style="font-size:20px;color:#337ab7"></i>
           </td>
           <td style="text-align: center"><a th:href="@{'/edit/{id}'(id=${note.id})}"><i class="fa fa-edit" style="font-size:20px"></i></a></td>
           <td style="text-align: center"><a th:href="@{'/delete/{id}'(id=${note.id})}"><i class="fa fa-trash" style="font-size:20px"></i></a></td>
       </tr>
       </tbody>
   </table>
</div>
</html>
構成的な概要に戻り、使用される Thymeleaf 関数を順番に見ていきましょう。標準的な HTML 構文や使用されるグラフィック スタイルは省略し、特にテンプレート エンジンのメカニズムを理解することに重点を置きます。
<div th:fragment="notebook">
「フラグメント」操作ではフラグメントの名前を指定し、ブロックの内容を「置換」コマンドに使用できるようにします。さらに!単一ページ内での複数の使用が決して排除されるわけではなく、ここでもプログラミング言語のプロシージャまたは関数との類似性が生じます。
<a th:href="@{'/sort/{sortDate}' (sortDate = 'ASC')}">
@PostMapping アノテーションの呼び出しは、マッピング「/sort/{sortDate}」を使用してコントローラーで使用されます。ここで、{sortDate} は送信ソート方向属性です。同様のことが次のブロックにも見られます。このブロックでは、反復ループ内のユーザーが選択した要素の位置に応じて動的な変更が追加されます。
<a th:href="@{'/edit/{id}'(id=${note.id})}">
<tr th:each="note : ${notes}">
値を列挙することは、Java 構文でのよく知られた for ブロックの使用法に非常に似ています。変数「note」は、入力属性配列 ${notes} (エンティティの配列) から現在の要素を取得し、値を変更するために使用されます。後で。率直に言って、Thymeleaf の広範な機能と実際のアプリケーションの例をリストするために別の記事を捧げることもできます。テンプレート エンジンは非常にシンプルであり、膨大な追加構文を学習する必要はまったくありません。上記の機能は、開発者の公式 Web サイトにある技術文書で概要が説明されており、バックエンドとの通信を組織する上で非常に重要です。したがって、自信を持って次の最後の部分に進むことができます。もちろん、ビジュアライゼーションの残りのコンポーネントを、記事の最後にある完成したアプリケーションへのリンクに添付します。

中小企業のコントローラー、管理者

「Web アプリケーションのアーキテクチャの基礎」 - プログラムの作業を整理する際のコントローラー コンポーネントの重要性をこれ以上正確に説明する方法はおそらくありません。ほとんどの操作は、コントローラー コンポーネント間の接続要素によって正確に実行されます。モデルとビュー。Spring Boot のアクション メカニズムのおかげで、わずかな問題もなくマッピングと GET/POST リクエスト メソッドを自信を持って使用でき、データ リポジトリに自動的に接続できます。NoteController クラスを「controllers」ディレクトリ内の別のファイルに作成しましょう。ここでも適切なアノテーションの使用を参照します。
@Controller
public class NoteController {

   private NoteService service;

   @Autowired
   public void setNoteService(NoteService service) {
       this.service = service;
   }

   @GetMapping("/")
   public String list(Model model) {
       return "index";
   }
}
注意深い人は、ビジネス ロジックをデータベース管理サービスとの連携から分離するサービスの追加に関連して、アプリケーション アーキテクチャの設計に重要な変更が加えられていることに気づくかもしれません。完了したアクションは、最終製品の多用途性を高め、データベースとの通信方法を変更することなくユーザー インターフェイスの機能を変更するための広い範囲を提供するために必要です。標準的な表現は、類似したものの中から決して目立つものではありません。インターフェイスは別のディレクトリにあり、Spring Boot 検出用の @Service アノテーションを持つクラスによって実装されます。
public interface NoteService {
   Note getNoteById(Integer id);
   void saveNote(Note note);
   void updateNote(Integer id, String message, boolean done);
   void deleteNote(Integer id);
   List<Note> findAll();
}

@Service
public class NoteServiceImpl implements NoteService{

   private NoteRepository repository;

   @Autowired
   public void setProductRepository(NoteRepository repository) {
       this.repository = repository;
   }

   @Override
   public Note getNoteById(Integer id) {
       return repository.findOne(id);
   }

   @Override
   public void saveNote(Note note) {
       repository.save(note);
   }

   @Override
   public void updateNote(Integer id, String message, boolean done) {
       Note updated = repository.findOne(id);
       updated.setDone(done);
       updated.setMessage(message);
       repository.save(updated);
   }

   @Override
   public void deleteNote(Integer id) {
       repository.delete(id);
   }

   @Override
   public List<Note> findAll() {
       return repository.findAll();
   }
}
コントローラーのレビューに戻り、Spring Boot メソッドを使用して作業を整理する複雑さを見てみましょう。@Autowired アノテーションは、サービスを適切なタイプの指定された変数に自動的にバインドし、データベースとの接続を確立する必要があることを示します。@GetMapping("/") アノテーションで示されるビューの通信方法にさらに注意を払う必要があります。このアノテーションは、localhost:8080 への呼び出しを受信したときに「index」という名前のページを返します。別のアプローチを使用して、拡張記述 @RequestMapping(value = "/", method = RequestMethod.GET) を指定するか、戻り値の型を既製の ModelAndView に置き換えることができます。ただし、実際のアプリケーションでの現在の経験によれば、最終結果に基本的な違いは見られず、通常のオプションを使用します。追加のタブを使用して新しい要素を追加して、コントローラーを拡張しましょう。ユーザーがナビゲーション バー要素をクリックすると、@GetMapping("/new") が呼び出され、「operations」ディレクトリから「new」ページにリダイレクトされ、ボタンを使用して入力されたデータを確認するときに「message」という名前のパラメータが返されます。そしてメインブロックにリダイレクトします。入力ウィンドウ内の変数名と転送された値の名前が完全に一致する必要があることについては、特に言及する必要があります。
<input type="text" class="form-control" id="message" th:name="message" placeholder="Enter your note." maxlength="100"/>
@GetMapping("/new")
public String newNote() {
   return "operations/new";
}

@PostMapping("/save")
public String updateNote(@RequestParam String message) {
   service.saveNote(new Note(message));
   return "redirect:/";
}
同様の手法を使用してレコードを更新します。コントロールをクリックすると、@GetMapping("/edit/{id}") マッピングが呼び出され、URL 文字列からの識別子が転送され、さらに編集するためのエントリとともに「note」属性が追加されます。@RequestParam(value = "done", required = false) booleanned) 特定の値の指定は、Thymeleaf テンプレート エンジンを使用する場合のチェックボックスの使用において重要な役割を果たし、デフォルトで「false」に設定されます。
@GetMapping("/edit/{id}")
public String edit(@PathVariable Integer id, Model model) {
   Note note = service.getNoteById(id);
   model.addAttribute("note", note);
   return "operations/edit";
}

@PostMapping("/update")
public String saveNote(@RequestParam Integer id, @RequestParam String message,
                      @RequestParam(value = "done", required = false) boolean done) {
   service.updateNote(id, message, done);
   return "redirect:/";
}
データベースからの項目の削除は非常に簡単で、渡された値を使用して適切なサービス関数を呼び出すだけで大幅な操作は必要ありません。
@GetMapping("/delete/{id}")
public String delete(@PathVariable Integer id) {
   service.deleteNote(id);
   return "redirect:/";
}
次に、完成したフラグメントに小さな調整を加え、Spring Data JPA のクエリクエリを使用した MySQL とのエキサイティングな通信に進みます。コントローラーを閉じる前に、単純なフィルタリングを管理する関数を別途追加します。
@Controller
public class NoteController {

   private String sortDateMethod = "ASC";

   @GetMapping("/")
   public String list(Model model) {
       List<Note> notebook = filterAndSort();
       model.addAttribute("notes", notebook);
       model.addAttribute("sort", sortDateMethod);
       return "index";
   }

private List<Note> filterAndSort() {
   List<Note> notebook = null;
   switch (sortDateMethod) {
       case "ASC":
           notebook = service.findAllByOrderByDateAsc();
           break;
       case "DESC":
           notebook = service.findAllByOrderByDateDesc();
           break;
   }
   return notebook;
}

とても小さいですが、とても重要なクエリです。

恥ずかしいことに、値のフィルタリングは、予想に反して、ページネーションによって確立された複雑さのしきい値を自信を持って克服するという技術的なタスクを達成する上で、さらなる障害となることが判明しました。つまり、データ配列を特定のサイズの個別のページに分割してさらに表示するということです。おそらく、蓄積された疲労が影響を及ぼしていたのでしょうが...インスピレーションは、まったく偶然に Query クエリに遭遇した後に生まれました。
public interface NoteRepository extends JpaRepository<Note, Integer> {
   List<Note> findAllByOrderByDateAsc();
   List<Note> findAllByOrderByDateDesc();
}
Spring Data JPA は、受信した情報を並べ替える必要性を排除する非常に粒度の高いデータベース クエリを作成する機能を提供し、幅広いアプリケーションの可能性を備えています。例えば:
List<Note> findAllByOrderByDateAsc();
このメソッドは SQL クエリに変換され、すべてのレコード (findAll) が日付 (byDate) で昇順 (Asc) で並べ替え (byOrder) 表示されます。さらに、複雑な組み合わせを作成し、単一の要件で多くのフィールドをサンプリングすることができます。たとえば、すべての (findAll) 完了した (byDoneTrue) レコードを、日付値 (byDate) の降順 (byOrder) で降順 (Decs) で選択します。
Page<Note> findAllByDoneTrueOrderByDateDesc(Pageable pageable);

初心者プログラマーの結論または別の告白

全て!Shift+F10 の組み合わせを使用するか、対応するアイコンをクリックすると、Web アプリケーションを安全に起動できます。Spring Boot は Apache Maven 上にプログラムをビルドし、ローカルの Apache Tomcat サーバーを localhost:8080 にインストールします。あとは、ブラウザでリンクをたどるだけです。
コンクエスト スプリング ブーツ - 6
そしてもちろん、他のビジネス要件を満たすための方法論も開発します。アプリケーションの可能性は、開発者の努力、機知、想像力によって制限されます。
コンクエスト スプリング ブーツ - 7
率直に言って、これまでの道のりに注意を払っているので、私は選択した方向の正しさを何度も確信し、JavaRush 教育ポータルで学習する利点を実感しています。同様の方向性の高等教育機関の時代遅れで驚くほど退屈なプログラムでは完全に抑圧されていたプログラミング学習への魅惑的な興味を、さまざまな実践課題のおかげで取り戻すことができた。バックエンド テクノロジー スタックの内容を 4 か月間積極的に学習することで、講義や実験室の授業に丸々 1 年間参加した場合と比べて、はるかに多くの知識を投資することができました。信じようと信じまいと。複雑な内容に取り組む難しさに負けないでほしいと思います。なぜなら、障害を克服することで私たちはより良くなり、職業的にも個人的にも成長できるからです。この小さな話が、SpringBoot という素晴らしいツールを使用するための新しいアイデアを発見するのに役立つことを願っています。PSギットハブ
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION