【初心者向け 092】Spring入門2(API作成、テスト)
はじめに
Infleanから勉強した内容を紹介するため、画像の場合はスクラップしました。リンクはこちらです。
https://www.inflearn.com/course/스프링-입문-스프링부트/dashboard
API
Springはhtmlを送る方式(static,template html)と直接値を
送る方式があります。今日は値を送る方法について、勉強します。
前回との違いとして、@ResponseBodyというアノテーションが追加になり、ModelとaddAttributeメソッドを活用せずに、そのまま変数をリターンします。
BodyはhtmlのBodyではなく、httpのbody messageです。
@GetMapping("hello-string")
@ResponseBody
public String helloString(@RequestParam("name") String name){
return "hello" + name; //hello spring
}
}
hello JOJO
Objectで送る方法(json)
@GetMapping("hello-api")
@ResponseBody
public Hello helloApi(@RequestParam("name") String name){
Hello hello = new Hello();
hello.setName(name);
return hello;
}
static class Hello{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}
DBアクセス
modeling
全てはオブジェクト(インスタンス化)で処理します!OOP!
コントローラー(컨트롤러):WEB MVCのコントローラーの役割。
サービス(서비스) : ビジネスロジック具象(IDは重複禁止、一人の会員は重複ではいけない)。サービスを利用することにおいてのルールみたいなやつ
ドメイン(도메인) : 会員、注文、クーポンなどDBに保存されるオブジェクト。
レポジトリ(리포지토리) :ドメインオブジェクトを通して、ビジネスロジックが実現できるようにするオブジェクト。また、分からないです!
まだ、DBをRDBにするかNoSQLにするか決まってない状況なので、インターフェースから具象クラスを変更できるように設計します。
MemberというDTO(Java Bean)、InterfaceであるMemberRepositoryを作成します。(long id, String name)
その後、Interface具象したMemoryMemberRepositoryを作成します。
repository
dataの処理を担当するオブジェクト。DBと直接連携するオブジェクトです。
package hello.hellospring.repository;
import hello.hellospring.domain.Member;
import java.util.*;
public class MemoryMemberRepository implements MemberRepository{
private static Map<Long, Member> store = new HashMap<>();
private static long sequence = 0L; //intかlongかを区切る
@Override
public Member save(Member member) {
member.setId(++sequence);
store.put(member.getId(), member);
return member;
}
@Override
public Optional<Member> findById(Long id) {
return Optional.ofNullable(store.get(id));
}
@Override
public Optional<Member> findByName(String name) {
return store.values().stream()
.filter(member -> member.getName().equals(name))
.findAny();
}
@Override
public List<Member> findAll() {
return new ArrayList<>(store.values());
}
}
元々はConcurrentHashMapを利用した方が良いです。
ConcurrentHashMap: 実務で使用するThread-safeのHashMap
AtomicLong: 実務で使用するThread-safeのWrapper class(long)
Test Case
package hello.hellospring.repository;
import hello.hellospring.domain.Member;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class MemoryMemberRepositoryTest {
MemberRepository repository = new MemoryMemberRepository();
@Test
public void save() {
Member member = new Member();
member.setName("spring");
repository.save(member);
Member result = repository.findById(member.getId()).get();
Assertions.assertEquals(member,result);
}
}
package hello.hellospring.repository;
import hello.hellospring.domain.Member;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class MemoryMemberRepositoryTest {
MemberRepositoryTest repository = new MemoryMemberRepository();
@Test
public void save(){
Member member = new Member();
member.setName("spring");
repository.save(member);
Member result = repository.findById(Member.getID()).get();
Assertions.assertEquals(member,"adsdzczxc");
}
}
}
@AfterEach
テストコードは、一気にテストする際には順序がランダムで行われるので、一つのテストコードが終われば、データーをクリアーする必要があります。
そのためには、まず本物のクラスに以下のようなコードを作成します。
public void clearXXX(){
xxx.clear();
}
その後、テストクラスには@AfterEachを付け、同じくclear()するメソッドを作成します。
@AfterEach
public void afterEach(){
repository.clear();
}
Service
サービス(サイト)のロジックをRepository(DAOと似てる)を連携しながら、DBのデーターをチェックし、クライアントからもらったデーターをチッェクします。
会員等億でIDが重複ではいけないのようなルールです。
public class MemberService {
private final MemberRepository memberRepository = new MemoryMemberRepository();
//Repositoryと連携するので、オブジェクトを部品として、使います。(Has関係)
public Long join(Member member) {
validateDuplicateMember(member);
//同じ名前があるかをチェックするメソッド
//(下にExctractしたので、下からご覧ください!)
memberRepository.save(member);
return member.getId();
}
private void validateDuplicateMember(Member member) {
memberRepository.findByName(member.getName())
.ifPresent(m -> {
throw new IllegalStateException("이미 존재하는 회원입니다.");
});
}
public List<Member> findMembers(){
return memberRepository.findAll();
}
public Optional<Member> findOne(Long memberId){
return memberRepository.findById(memberId);
}
}
Repositoryを部品として、呼び出しメソッドを使って、クライアントをチェックするので、積極的に活用していることが分かります。初めて見るパターンです!
Discussion