Spring Frameworkを使用したMVCアプリケーションの構築:初心者向けチュートリアル
公開: 2022-03-11Javaは、単純なアプリケーションを構築するには複雑すぎて時間がかかりすぎるとよく言われます。 それにもかかわらず、Javaは、その周りに非常に成熟したエコシステムを備えた安定したプラットフォームを提供します。これにより、Javaは堅牢なソフトウェアを開発するための素晴らしいオプションになります。
Javaエコシステムの多くの強力なフレームワークの1つであるSpringFrameworkには、Javaでのパフォーマンスが高くテスト可能なアプリケーションの開発を簡素化することを目的としたプログラミングおよび構成モデルのコレクションが付属しています。
このチュートリアルでは、SpringFrameworkとJavaPersistence API(JPA)を使用して、ソフトウェア開発者のデータベースとして機能する単純なアプリケーションの構築に挑戦します。
アプリケーションは標準のMVCアーキテクチャに従います。 これには、コントローラー(ContractsControllerクラス)、ビュー(Thymeleafテンプレートに基づく)、およびモデル(Javaマップオブジェクト)が含まれます。 簡単にするために、JPAの背後にあるメモリ内データベースを使用して、アプリケーションの実行中にデータを永続化します。
SpringFrameworkチュートリアル入門
Springベースのアプリケーションをビルドするには、次のビルドツールのいずれかを使用する必要があります。
- Maven
- Gradle
このチュートリアルでは、Mavenを使用します。 これらのツールのいずれかに慣れていない場合は、SpringToolSuiteをダウンロードするのが簡単な方法です。 このスイートはSpringFramework専用であり、独自のEclipseベースのIDEが付属しています。
Spring Tool Suiteでは、「ファイル>新規」メニューから「SpringStarterProject」を選択して新しいプロジェクトを作成します。
新しいプロジェクトが作成されたら、Maven構成ファイル「 pom.xml 」を編集し、次の依存関係を追加する必要があります。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-commons</artifactId> </dependency>
これらのリストされた依存関係は、Spring Boot Web、Thymeleaf、JPA、およびH2(インメモリデータベースとして機能します)をロードします。 必要なすべてのライブラリが自動的にプルされます。
エンティティクラス
開発者とそのスキルに関する情報を保存できるようにするには、「開発者」と「スキル」の2つのエンティティクラスを定義する必要があります。
これらは両方とも、いくつかのアノテーションが付いたプレーンなJavaクラスとして定義されています。 クラスの前に「@Entity」を追加することで、それらのインスタンスをJPAで使用できるようにします。 これにより、必要に応じて永続データストアからインスタンスを簡単に保存および取得できるようになります。 さらに、「@ Id」および「@GeneratedValue」アノテーションを使用すると、エンティティの一意のIDフィールドを示し、データベースに保存されたときにその値が自動的に生成されます。
開発者は多くのスキルを持つことができるため、「@ManyToMany」アノテーションを使用して単純な多対多の関係を定義できます。
デベロッパー
@Entity public class Developer { @Id @GeneratedValue(strategy=GenerationType.AUTO) private long id; private String firstName; private String lastName; private String email; @ManyToMany private List<Skill> skills; public Developer() { super(); } public Developer(String firstName, String lastName, String email, List<Skill> skills) { super(); this.firstName = firstName; this.lastName = lastName; this.email = email; this.skills = skills; } public long getId() { return id; } public void setId(long id) { this.id = id; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public List<Skill> getSkills() { return skills; } public void setSkills(List<Skill> skills) { this.skills = skills; } public boolean hasSkill(Skill skill) { for (Skill containedSkill: getSkills()) { if (containedSkill.getId() == skill.getId()) { return true; } } return false; } }
スキル
@Entity public class Skill { @Id @GeneratedValue(strategy=GenerationType.AUTO) private long id; private String label; private String description; public Skill() { super(); } public Skill(String label, String description) { super(); this.label = label; this.description = description; } public long getId() { return id; } public void setId(long id) { this.id = id; } public String getLabel() { return label; } public void setLabel(String label) { this.label = label; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } }
リポジトリ
JPAを使用すると、非常に便利なDeveloperRepositoryインターフェースとSkillRepositoryインターフェースを定義できます。これにより、CRUD操作が簡単になります。 これらのインターフェースにより、次のような単純なメソッド呼び出しを通じて、保存されている開発者やスキルにアクセスできます。
- 「respository.findAll()」:すべての開発者を返します
- 「repository.findOne(id)」:指定されたIDの開発者を返します
これらのインターフェースを作成するには、CrudRepositoryインターフェースを拡張するだけです。
開発者リポジトリ
public interface DeveloperRepository extends CrudRepository<Developer, Long> { }
スキルリポジトリ
public interface SkillRepository extends CrudRepository<Skill, Long> { public List<Skill> findByLabel(String label); }
ここで宣言されている追加メソッド「 findByLabel 」の機能は、JPAによって自動的に提供されます。
コントローラ
次に、このアプリケーションのコントローラーで作業できます。 コントローラーはリクエストURIをマップしてテンプレートを表示し、その間に必要なすべての処理を実行します。
@Controller public class DevelopersController { @Autowired DeveloperRepository repository; @Autowired SkillRepository skillRepository; @RequestMapping("/developer/{id}") public String developer(@PathVariable Long id, Model model) { model.addAttribute("developer", repository.findOne(id)); model.addAttribute("skills", skillRepository.findAll()); return "developer"; } @RequestMapping(value="/developers",method=RequestMethod.GET) public String developersList(Model model) { model.addAttribute("developers", repository.findAll()); return "developers"; } @RequestMapping(value="/developers",method=RequestMethod.POST) public String developersAdd(@RequestParam String email, @RequestParam String firstName, @RequestParam String lastName, Model model) { Developer newDeveloper = new Developer(); newDeveloper.setEmail(email); newDeveloper.setFirstName(firstName); newDeveloper.setLastName(lastName); repository.save(newDeveloper); model.addAttribute("developer", newDeveloper); model.addAttribute("skills", skillRepository.findAll()); return "redirect:/developer/" + newDeveloper.getId(); } @RequestMapping(value="/developer/{id}/skills", method=RequestMethod.POST) public String developersAddSkill(@PathVariable Long id, @RequestParam Long skillId, Model model) { Skill skill = skillRepository.findOne(skillId); Developer developer = repository.findOne(id); if (developer != null) { if (!developer.hasSkill(skill)) { developer.getSkills().add(skill); } repository.save(developer); model.addAttribute("developer", repository.findOne(id)); model.addAttribute("skills", skillRepository.findAll()); return "redirect:/developer/" + developer.getId(); } model.addAttribute("developers", repository.findAll()); return "redirect:/developers"; } }
URIのメソッドへのマッピングは、単純な「@RequestMapping」アノテーションを介して行われます。 この場合、コントローラーのすべてのメソッドがURIにマップされます。

これらのメソッドのモデルパラメータを使用すると、データをビューに渡すことができます。 本質的に、これらはキーから値への単純なマップです。
各コントローラーメソッドは、ビューとして使用されるThymeleafテンプレートの名前、または特定のパターンのURLを返します(「リダイレクト:
コントローラ内で、「@ Autowired」アノテーションは、対応するフィールドに定義されたリポジトリの有効なインスタンスを自動的に割り当てます。 これにより、多くの定型コードを処理しなくても、コントローラー内から関連データにアクセスできます。
ビュー
最後に、生成するビューのテンプレートをいくつか定義する必要があります。 このために、単純なテンプレートエンジンであるThymeleafを使用しています。 コントローラメソッドで使用したモデルは、テンプレート内で直接利用できます。つまり、モデルの「 contract 」キーに契約を入力すると、テンプレート内から「contract.name」として名前フィールドにアクセスできるようになります。
Thymeleafには、HTMLの生成を制御するいくつかの特別な要素と属性が含まれています。 それらは非常に直感的で簡単です。 たとえば、スパン要素のコンテンツにスキルの名前を入力するには、次の属性を定義するだけです(モデルでキー「スキル」が定義されていると仮定します)。
<span th:text="${skill.label}"></span>
同様に、アンカー要素の「 href 」属性を設定するには、特別な属性「 th:href 」を使用できます。
このアプリケーションでは、2つの単純なテンプレートが必要になります。 わかりやすくするために、埋め込みテンプレートコードでは、すべてのスタイル属性とクラス属性(つまり、ブートストラップ属性)をスキップします。
開発者リスト
<!DOCTYPE HTML> <html xmlns:th="http://www.thymeleaf.org"> <head> <title>Developers database</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> </head> <body> <h1>Developers</h1> <table> <tr> <th>Name</th> <th>Skills</th> <th></th> </tr> <tr th:each="developer : ${developers}"> <td th:text="${developer.firstName + ' ' + developer.lastName}"></td> <td> <span th:each="skill,iterStat : ${developer.skills}"> <span th:text="${skill.label}"/><th:block th:if="${!iterStat.last}">,</th:block> </span> </td> <td> <a th:href="@{/developer/{id}(id=${developer.id})}">view</a> </td> </tr> </table> <hr/> <form th:action="@{/developers}" method="post" enctype="multipart/form-data"> <div> First name: <input name="firstName" /> </div> <div> Last name: <input name="lastName" /> </div> <div> Email: <input name="email" /> </div> <div> <input type="submit" value="Create developer" name="button"/> </div> </form> </body> </html>
開発者の詳細
<!DOCTYPE HTML> <html xmlns:th="http://www.thymeleaf.org"> <head> <title>Developer</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> </head> <body> <h1>Developer</h1> Name: <b th:text="${developer.firstName}" /> <b th:text="${developer.lastName}" /><br/> Email: <span th:text="${developer.email}" /><br/> Skills: <span th:each="skill : ${developer.skills}"> <br/> <span th:text="${skill.label}" /> - <span th:text="${skill.description}" /> </span> <form th:action="@{/developer/{id}/skills(id=${developer.id})}" method="post" enctype="multipart/form-data" > <select name="skillId"> <option th:each="skill : ${skills}" th:value="${skill.id}" th:text="${skill.description}">Skill</option> </select> <input type="submit" value="Add skill"/> </form> </body> </html>
サーバーの実行
Springにはブートモジュールが含まれています。 これにより、コマンドラインからコマンドラインJavaアプリケーションとしてサーバーを簡単に起動できます。
@SpringBootApplication public class Application implements CommandLineRunner { @Autowired DeveloperRepository developerRepository; @Autowired SkillRepository skillRepository; public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
インメモリデータベースを使用しているため、起動時に事前定義されたデータを使用してデータベースをブートストラップすることは理にかなっています。 そうすれば、サーバーが稼働しているときに、データベースに少なくともいくつかのデータがあります。
@Override public void run(String... args) throws Exception { Skill javascript = new Skill("javascript", "Javascript language skill"); Skill ruby = new Skill("ruby", "Ruby language skill"); Skill emberjs = new Skill("emberjs", "Emberjs framework"); Skill angularjs = new Skill("angularjs", "Angularjs framework"); skillRepository.save(javascript); skillRepository.save(ruby); skillRepository.save(emberjs); skillRepository.save(angularjs); List<Developer> developers = new LinkedList<Developer>(); developers.add(new Developer("John", "Smith", "[email protected]", Arrays.asList(new Skill[] { javascript, ruby }))); developers.add(new Developer("Mark", "Johnson", "[email protected]", Arrays.asList(new Skill[] { emberjs, ruby }))); developers.add(new Developer("Michael", "Williams", "[email protected]", Arrays.asList(new Skill[] { angularjs, ruby }))); developers.add(new Developer("Fred", "Miller", "[email protected]", Arrays.asList(new Skill[] { emberjs, angularjs, javascript }))); developers.add(new Developer("Bob", "Brown", "[email protected]", Arrays.asList(new Skill[] { emberjs }))); developerRepository.save(developers); }
結論
Springは、MVCアプリケーションの構築を可能にする用途の広いフレームワークです。 Springを使用して単純なアプリケーションを構築するのは迅速で透過的です。 このアプリケーションは、JPAを使用してデータベースと簡単に統合することもできます。
このプロジェクト全体のソースコードはGitHubで入手できます。