Spring 5でできることは?4からの変更点や互換性について解説
この記事は「Javaで学習するSpringによるWebアプリ開発」というトレノキャンプのeラーニング講座を基に解説した記事です。
本記事では、Springの学習前に押さえておきたいJavaの基本文法の中でも、最も重要なオブジェクト指向の基本を確認していきましょう。
今回は、前回の記事で実装した「Entity」を操作する「Serviceクラス」について解説します。インターフェース、実装クラスの役割を確認したあと、実際に実装を行っていきましょう。
1. Serviceクラス
Serviceクラスは、Entityインスタンスを取得し実際の操作を行うクラスです。
今回は例として、Serviceクラスを用いてあいさつを返すメソッドや、メンバー情報の一覧を返すメソッドを作っていきます。
Serviceクラスを作成する上で大切なのが、先にインターフェースを作るということです。
ここで、インターフェースの役割について一度おさらいしておきましょう。
2.インターフェース
インターフェースの基本的な役割とは、「機能の概要」を定義することです。
いわゆるToDoリストのように、先にメソッドの名前、および引数のみをインターフェースに定義しておき、実際の処理内容は継承を行うクラスのほうで実装する、というシステムになっています。
インターフェースを利用するメリットは、クラスを抽象的に扱うことができるということだけではなく、Spring Data JPAや、MyBatisなどといったライブラリは、インターフェースからクラスを自動生成する仕組みを持っています。
その理屈を今回の環境を例にとって説明すると、「MemberService」という名前のインターフェースが「getAll()」というメソッドを持っているため、これらの名前より、このメソッドはmemberというテーブルからすべてのデータを取得するためのものであろう、ということが推測できます。
すると、ロジックに関しても、SQL文が「SELECT * FROM member」となることは自明ですから、このようにある一定の規則の下では、実装クラスをわざわざ人間が作る必要がなくなるのです。
以上のことから、インターフェースは非常に重要となりますので、その役割についてしっかりと理解しておく必要があります。ここでは、インターフェースは機能の概要を書くものであるということをまずは抑えておいてください。
それでは、今回実装していくものについて確認しましょう。
今回実装するのは、「MemberService」インターフェースと、その実装クラスである「MemberServiceImpl」クラスです。このクラスには、引数に応じて異なる挨拶文を返すgreet(int i)と、会員の一覧データを返すgetAll()という2つのメソッドを定義します。
Entityインスタンスを操作するにあたり、mainメソッドから直接行うのではなく、Serviceクラスに処理を任せることで、mainメソッドの記述内容を非常に簡素にできます。
構造としては、mainメソッドを社長と考えると、MemberServiceクラスは専門スタッフのようなイメージです。社長が専門スタッフに命令を出し、指示を受けた専門スタッフが実際の作業を行うということになります。
このように、実際のロジックはすべてMemberServiceクラスに実装していくことになるので、反対にmainメソッドの記述はシンプルになります。
それでは、続いてmainメソッド側の記述について詳しく見ていきましょう。
3.スタック領域とヒープ領域
先ほど説明した通り、mainメソッドでは具体的な処理を記述するかわりにServiceクラスを呼び出します。Serviceクラスは、通常のオブジェクトと同じようにnew演算子でインスタンスを生成し、変数に代入することで各メンバへアクセスができるようになります。
ここで、メモリについておさらいしておきましょう。
Javaにおけるメモリは、new演算子などによって動的に領域が確保され、主にオブジェクトのインスタンスが保持される「ヒープ領域」と、主にヒープ領域内に格納されたインスタンスの参照情報を保持する「スタック領域」の大きくふたつに分けられます。
通常、オブジェクトをインスタンス化する際は代入演算子とnew演算子を組み合わせて用いますが、その際まずnew演算子によってヒープ領域内にスペースが確保され、続いて新たに生成されたインスタンスがそのスペースに格納されます。
インスタンスが格納されるとnew演算子は格納先アドレスの情報を返し、最終的に代入演算子によって、スタック領域内の変数にインスタンスの参照情報が格納されます。
ここで、new演算子は生成されたインスタンスそのものを返すわけではないということに注意してください。そのためインスタンスを格納する変数には、実際はインスタンスそのものではなく、ヒープ領域に格納されているインスタンスへの参照情報が代入されています。
以上のことから、インスタンスは「参照型」であるとみなされます。それに対して、プリミティブ型はオブジェクトではないため参照型ではありません。つまり、データそのものがスタック領域に格納されます。
それでは、以上の内容を実際に実装していきましょう。
※本記事で紹介している内容は、実践型eラーニングTRAINOCAMP(トレノキャンプ)にて学習可能です。
4. Serviceクラスの実装
前回の記事ではEntityクラスのMemberを実装したので、今回は続きとしてこのMemberに対してサービスを提供するクラスを実装していきます。
この記事では、実装に統合開発環境のEclipse 2019を使用します。
環境構築の手順は下記の記事でも解説しています。興味のある方は下記のリンクからご覧ください。
ここで重要ポイントです。Serviceクラスは「インターフェース」と「実装クラス」の2種類で構成します。
インターフェースは「機能の概要」なので、メソッドの名前、引数といった定義のみを記述し、処理は実装クラスの方に記述していきます。
① Serviceクラスのパッケージ作成
では、まずServiceクラスのパッケージを作成します。
srcフォルダの上で右クリックし、新規のパッケージを作成します。
名前は「service」とします。
入力ができたら、完了をクリックしてください。以上でServiceクラス用のパッケージが作成されました。
② インターフェースを作成
続いて、serviceパッケージ内に空のインターフェースを作ります。
serviceパッケージの上で右クリックし、「新規」から「インターフェース」を選択してください。
新規インターフェース作成のダイアログが表示されますので、名前を「MemberService」とします。
インターフェースもクラスと同じく、単語の頭文字は大文字になります。
入力ができたら完了を押してください。
MemberServiceインターフェースが作成されました。
今回必要なのは2つのメソッドなので、それぞれメソッド名と引数を決めていきます。
package service;
public interface MemberService {
String greet(int i); //挨拶を返す
ArrayList<Member> getAll(); //全てのMemberを返す
}
メソッド定義の方法は通常のクラスと同様なので、そのまま戻り値「String」、メソッド名「greet」、引数「(int i)」と記述します。
これで、intを引数にとりStringを返すgreetというメソッドとして定義できました。インターフェースにはメソッドの処理は実装しないので、greetメソッドはこれで完成です。波括弧は書かずに、そのままセミコロンを入力します。
続いて、引数なしでArraylist<Member>を返す、getAllメソッドを定義しておきます。ArrayList<Member>はMember型のインスタンスを複数内包するコレクションオブジェクトです。
記述をしていると、エディタ上でクラスを読み込みできないという意味の赤い波線が表示されることがあります。
今回はArrayListとMemberをまだインポートしていなかったので、クラスをインポートしましょう。
ショートカット「Shift + Ctrl + O」を同時に押してください。
すると、インポートウィザードが表示されるので、entity.Memberを選択します。
これでインポートが完了しました。
ソースコードの上部にimport文が追加されています。
package service;
import java.util.ArrayList; //自動的にインポートされる
import entity.Member; //自動的にインポートされる
public interface MemberService {
String greet(int i); //挨拶を返す
ArrayList<Member> getAll(); //全てのMemberを返す
}
このように、「Shift + Ctrl + O」というショートカットで簡単にクラスをインポートできるので、覚えておきましょう。
以上でMemberServiceインターフェースが出来上がりました。
一旦「Ctrl + S」で保存をかけておきます。
③ 実装クラスを作成
それでは、インターフェースが作成できたので、今度はクラスを作り中身を実装していきます。
serviceパッケージの上で右クリックをして、新規のクラスを作成してください。
名前は「MemberServiceImpl」とします。Implは、実装という意味です。
中央部の「追加」というボタンを押して、実装するインターフェースを選択します。
MemberServiceと打ち込むと先ほど作成したインターフェースが出てきますので、こちらを選択しOKを押します。
名前とインターフェースが正しく設定できたら、完了を選択してください。
MemberServiceインターフェースを実装する「MemberServiceImpl」クラスができました。
行番号に注目すると、3の次が6となっていますが、これはimport文が折りたたまれているためです。行頭に表示される「+」マークをクリックすることで、隠れている部分を展開できます。
なお、サンプルコードは全て展開した状態で記述してあります。
クラスを作成する際に同時にインターフェースを指定することで、インターフェースに定義したメソッドのひな形が自動的に作成されています。
package service;
import java.util.ArrayList;
import entity.Member;
public class MemberServiceImpl implements MemberService {
@Override //アノテーション
public String greet(int i) {
// TODO 自動生成されたメソッド・スタブ
return null;
}
@Override //アノテーション
public ArrayList<Member> getAll() {
// TODO 自動生成されたメソッド・スタブ
return null;
}
}
ここで確認ポイントです。今回自動生成されたメソッドに記述されている「@Override」という部分を「アノテーション」といいます。
@Overrideアノテーションは、該当メソッドをインターフェースあるいは親クラスの同名メソッドを上書きして定義するという意味です。
試しに@Overrideアノテーションが付与されているgreetメソッドを「greet2」という名前に変更してみると、赤い波線で警告が出ます。
このように@Overrideアノテーションを付けておくことで、記述ミスなどを検知してくれるようになるので、メソッドをオーバーライドする際は@Overrideアノテーションを付けるようにします。
それでは、メソッドの中身を記述していきましょう。
メソッド内に自動生成されたコメントとreturn文は必要ありませんので、記述を開始する前に削除しておいてください。
package service;
import java.util.ArrayList;
import entity.Member;
public class MemberServiceImpl implements MemberService {
@Override //アノテーション
public String greet(int i) {
String[] greetings = {"Good Morning", "Hello", "Good Evening"};
return greetings[i];
}
@Override //アノテーション
public ArrayList<Member> getAll() {
ArrayList<Member> list = new ArrayList<>();
Member mem1 = new Member(1, "Linda", "linda@example.com");
Member mem2 = new Member(2, "James", "james@example.com");
list.add(mem1);
list.add(mem2);
return list;
}
}
greet(int i)は、引数に応じて対応する挨拶文を返すというメソッドになります。String型配列にあいさつ文を3つ登録しておき、引数iを添え字として配列の要素をreturnで返します。
String型の配列はgreetingsという名前で、要素の0番目に「Good Morning」、1番目に「Hello」、2番目に「Good Evening」という文字列が入っています。
greet(int i)が記述できたら、続いてgetAll()も作っていきます。
getAll()はMember型のインスタンスを複数格納したArrayListを返すメソッドです。通常はデータベースなどから取得したデータをEntityに変換して返す処理になりますが、今回はインスタンスを生成したのち直接データを入力します。
それでは、まず空のArrayList<Member>を作りましょう。名前はlistとします。続いてMemberを2つインスタンス化し、listの中に詰めていきます。
変数名はそれぞれ「mem1」、「mem2」とし、new演算子でMember型のインスタンスを生成、代入しています。
それでは、これをはじめに作ったArrayList<Member>に詰めていきますので、はじめに「list.」と記述してください。
すると、ArrayListに定義されているメソッドが一覧で表示されますので、要素を追加するadd(Member e)を選択し、引数にmem1を指定しています。続いて、同様にmem2もlistに追加しておきます。
以上でlistが完成したので、returnで戻り値として返します。
ここまで記述出来たら、変更を保存しておきましょう。
④ mainメソッドで出力を確認
次に、mainメソッドからこれらの処理を呼び出してみます。
再びsrcの上で右クリックをして、パッケージを作ります。
名前は「demo」とします。
続いて、demoパッケージ内に新規クラスを作成します。
こちらの名前は「Main」とします。
今回は実行可能なクラスを作りたいので、「public static void main」のボックスにチェックを入れておいてください。
確認できたら、完了を選択します。
クラスが作成されました。
先ほどボックスにチェックを入れたことで、mainメソッドが自動的に生成されています。
それでは中身を記述していきます。
メソッド内のコメント部分は必要ないので、先に削除しておきます。
package demo;
import service.MemberServiceImpl;
public class Main {
public static void main(String[] args) {
MemberServiceImpl service = new MemberServiceImpl();
System.out.println(service.greet(2));
}
}
まずは実装クラスMemberServiceImplをインスタンス化し、serviceという変数に代入しています。
続いて、「Shift + Ctrl + O」でMemberServiceImplをインポートします。
ここからは、このServiceクラスのインスタンスを用いる処理を記述していきます。
まずはgreetから試してみます。
System.out.println()を呼び出します。
先に「sysout」と入力しておいて、「Ctrl + Space」のショートカットキーを押下します。
すると、System.out.println();が自動的に記述されます。
greetはStringを返すので、printlnの引数に渡します。
「service.」まで打ち込んでください。
メソッドの一覧が現れるので、greet(int i)を選択します。
int型の引数は「2」にしておきます。
0、1、2まで番号があったと思いますが、今回2を指定したので「Good Evening」が返ってくることになります。
それでは、「Ctrl + S」で保存をかけ、実行してみましょう。
検証ポイントです。Mainクラスを右クリックして、「実行」から「Java アプリケーション」を選択してください。
コンソールに「Good Evening」と出力され、正常に動作しているということが確認できました。
それでは続いてもう1つのメソッドも確認していきます。
public static void main(String[] args) {
MemberServiceImpl service = new MemberServiceImpl();
System.out.println(service.greet(2));
System.out.println(service.getAll()); //ArrayListを直接出力する
}
sysoutと入力して「Ctrl + Space」でSystem.out.println()を記述します。
続いて、先ほどと同じようにgetAllを直接出力してみます。
記述ができたら、保存して実行していきます。
getAllメソッドはArrayList<Member>を返すようになっていましたが、ArrayListを直接出力するとどうなるでしょうか。
それでは、検証ポイントです。早速実行しましょう。
コンソールに出力されましたが、ArrayListは中身を直接出力できる形式ではないため、クラス名とインスタンスのハッシュ値が表示されています。
そこで、今度は中身の値を出力するように修正していきます。
package demo;
import java.util.ArrayList;
import entity.Member;
import service.MemberServiceImpl;
public class Main {
public static void main(String[] args) {
MemberServiceImpl service = new MemberServiceImpl();
System.out.println(service.greet(2));
System.out.println(service.getAll()); //ArrayListを直接出力する
ArrayList<Member> list = service.getAll();
for(Member mem : list) { //拡張for文でlistの中身を一つずつ出力する
System.out.println(mem.getId() + "," + mem.getName() + "," + mem.getEmail());
}
}
}
ArrayListの中身を出力する処理を追記していきます。
まずは、ArrayList<Member>型変数listを用意し、service.getAll()で取得した結果を代入します。加えてインポートも済ませておきましょう。
listはMember型のコレクションなので、拡張for文を使用していきます。
それぞれの要素をMember memとして取り出し、getterを用いてid, name, emailフィールドの値を取得、さらにフィールドを一つの文字列に連結したのちprintln()で出力しています。
以上ですべてのコードが完成しましたので、保存をかけて実行してみましょう。
Main.javaを右クリックし、「実行」の「Java アプリケーション」を選択します。
コンソールを確認すると、id、name、emailの値がちゃんと出力されています。
Serviceクラスの実装および利用方法の解説をしました。
次回はメソッドと引数について、問題を解きながら解説していきます。 お疲れ様でした。
第1章 Springを扱うためのJava知識
Springを学ぶ前に知っておくべきJavaの知識について紹介します。
オブジェクト(Entity)の構成や、Entityクラスの実装方法を学びましょう。
Serviceクラス、インターフェースがどんなものなのか、またその実装方法を見てみましょう。
Javaのメモリ「ヒープ領域」と「スタック領域」についても解説します。
メソッドと引数について、問題に取り組みながら学びます。
作成したインターフェースとServiceクラスを使用して、メソッドの追加を行ってみましょう。
「シングルトン」とは何なのか、インスタンスの化の仕組みについて掘り下げながら紹介します。
実装方法も確認しましょう。