Intro

generic controller가 필요한가? 라고 물어본다면 글쎄? 정도로 흘려버립니다. 잘 쓰면 유용하지 않을까요? 어느 날 controller, service, dao, model들을 만들고 있는데 이 모델 클래스 도메인 별로 겹치는게 많은데 굳이 따로 만들 필요가 있나? 하는 생각이 들었고 조금씩 만들어 보게 되었습니다.

generic에 대한 설명은 레퍼런스를 참고하도록 합시다.

1. sources

1.1 controller

package io.github.dotkebiweb.controller;

import io.github.dotkebiservice.hold.AbstractService;
import io.github.dotkebiservice.hold.TransformFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.support.SessionStatus;

import javax.validation.Valid;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

/**
 * Generic Controller
 * @author by dotkebi@gmai.com
 */
public abstract class AbstractController<T, K extends Serializable> {

    protected AbstractService<T> service;

    @Autowired
    public AbstractController(AbstractService<T> service) {
        this.service = service;
    }

    @RequestMapping(value = "", method = RequestMethod.GET)
    public String fetchList(
            @PageableDefault(sort = { "id" }, direction = Sort.Direction.DESC) Pageable page
            , Model model
    ) {
        Page<T> contents = service.fetchAll(page);
        model.addAttribute("dataList", contents.getContent());
        return getPageHeader() + "List";
    }

    @RequestMapping(value = "/{id}", method = RequestMethod.GET)
    public String fetch(
            @PathVariable K id
            , Model model
    ) {
        model.addAttribute("data", service.fetch(id));
        return getPageHeader() + "Detail";
    }

    @RequestMapping(value = "/modify/{id}", method = RequestMethod.GET)
    public String modify(
            @PathVariable K id
            , Model model
    ) {
        model.addAttribute("data", service.fetch(id));
        model.addAttribute("title", getTitle() + " 수정");
        return getPageHeader() + "Modify";
    }

    @RequestMapping(value = "/add", method = RequestMethod.GET)
    public String add(Model model) {
        model.addAttribute("title", getTitle() + " 추가");
        return getPageHeader() + "New";
    }

    @RequestMapping(value = "", method = RequestMethod.POST)
    public String saveOrUpdate(
            @ModelAttribute @Valid T t
            , BindingResult bindingResult
            , SessionStatus sessionStatus
            , Model model
    ) {
        if (bindingResult.hasErrors()) {
            String modifier = "/add";
            return "redirect:" + getAPIUrl() + modifier;
        }
        service.save(t);
        sessionStatus.setComplete();
        return "redirect:" + getAPIUrl();
    }

    @RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
    public @ResponseBody Boolean delete(
            @PathVariable(value = "id") K id
    ) {
        service.delete(id);
        return true;
    }

    protected abstract String getPageHeader();
    protected abstract String getAPIUrl();
    protected abstract String getTitle();

}

1.2. service


package io.github.dotkebi.service;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import java.io.Serializable;

/**
 * generic service
 * @author by dotkebi@gmai.com
 */
public interface AbstractService<T, K extends Serializable> {

    Page<T> fetchAll(Pageable pageRequest);

    T fetch(K id);

    void save(T t);

    void delete(K id);

}

1.3. service implementation

package io.github.dotkebi.service;

import io.github.dotkebi.service.AbstractService;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;

import java.io.Serializable;

/**
 * generic service implementation
 * @author by dotkebi@gmail.com
 */
public abstract class AbstractServiceImpl<T, K extends Serializable> implements AbstractService<T, K> {

    protected JpaRepository<T, K> repository;

    public AbstractServiceImpl(JpaRepository<T, K> repository) {
        this.repository =repositorydao;
    }

    @Override
    public Page<T> fetchAll(Pageable pageRequest) {
        return repository.findAll(pageRequest);
    }

    @Override
    public T fetch(K id) {
        return repository.findOne(id);
    }

    @Override
    public void save(T t) {
        repository.save(t);
    }

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

}

1.4. repository

package io.github.dotkebi.repository;

import io.github.dotkebi.model.Test;
import org.springframework.data.jpa.repository.JpaRepository;

/**
 * @author by dotkebi@gmail.com
 */
public interface TestRepository extends JpaRepository<Test, Long> {
}

2. 예제

package io.github.dotkebi.domain;

import lombok.*;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

/**
 * @author by dotkebi@gmail.com
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
public class Test {

    @Id
    @GeneratedValue
    private Long id;

    @Column
    private String name;

    @Column
    private String addr;

}
package io.github.dotkebi.controller;

import io.github.dotkebi.domain.Test;
import io.github.dotkebi.service.TestServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

import javax.annotation.PostConstruct;
import java.util.List;

/**
 * @author by dotkebi@gmail.com
 */
@SessionAttributes("data")
@Controller
public class TestController extends AbstractController<Test, Long> {

    @Autowired
    public TestController(TestServiceImpl service) {
        super(service);
    }

    @Override
    protected String getPageHeader() {
        return "test";
    }

    @Override
    protected String getAPIUrl() {
        return "/test";
    }

    @Override
    protected String getTitle() {
        return "test";
    }

    @ModelAttribute("data")
    public Test create()  {
        return new Test();
    }
}
package io.github.dotkebi.service;

import io.github.dotkebi.domain.Test;
import io.github.dotkebi.dao.TestDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

/**
 * @author by dotkebi@gmail.com
 */
@Service
public class TestServiceImpl extends AbstractServiceImpl<Test, Long> {

    @Autowired
    public TestServiceImpl(TestRepository repository) {
        super(repository);
    }

}