IT_Programming/Dev Libs & Framework

[펌] Spring - GenericController 만들기

JJun ™ 2013. 2. 21. 15:37


 출처: http://whiteship.tistory.com/2699 







CodeController에서 구체적인 정보는 최대한 추상화 시킵니다. 
예를 들어 "code" 같은 문자열은 별로 좋치 않습니다. 

이걸 차라리 "model"이라는 추상적인 이름으로 바꾸면 다른 도메인에서도 사용하기 좋을 겁니다. 
애초에 문자열 자체가 그다지 추상화는데 좋치 않긴 하지만 그렇게라도 해야지요. ㅋ

그렇게 해서 일단 추상화 시킨 코드가 이렇습니다.

@Controller
@RequestMapping("/base/code")
@SessionAttributes("model")
public class CodeController {

    @Autowired CodeService codeService;
    @Autowired CodeRef ref;
    String baseUrl;

    public CodeController() {
        RequestMapping rm = this.getClass().getAnnotation(RequestMapping.class);
this.baseUrl = rm.value()[0];
    }

    @ModelAttribute("ref")
public CodeRef ref() {
return ref;
}

    @RequestMapping(value="/mgt")
    public void mgt(Model model){
        model.addAttribute("searchParam", new CodeSearchParam());
    }

    @RequestMapping(method = RequestMethod.GET)
    public void list(Model model, CodeSearchParam searchParam, PageParam pageParam) {
        model.addAttribute("list", codeService.list(pageParam, searchParam));
    }

    @RequestMapping(value = "/form", method = RequestMethod.GET)
    public String addForm(Model model){
        model.addAttribute("model", new Code());
        return baseUrl + "/new";
    }

    @RequestMapping(method = RequestMethod.POST)
    public String addFormSubmit(Model model, @Valid @ModelAttribute("model") Code code, BindingResult result, SessionStatus status){
        if(result.hasErrors())
            return baseUrl + "/new";
        status.setComplete();
        codeService.add(code);
        return  "redirect:" + baseUrl + "/mgt";
    }

    @RequestMapping(value = "/{id}/form", method = RequestMethod.GET)
    public String editForm(@PathVariable int id, Model model){
        model.addAttribute("model", codeService.getById(id));
        return baseUrl + "/edit";
    }

    @RequestMapping(value = "/{id}", method = RequestMethod.PUT)
    public String editFormSubmit(Model model, @Valid @ModelAttribute("model") Code code, BindingResult result, SessionStatus status){
        if(result.hasErrors())
            return baseUrl + "/edit";
        status.setComplete();
        codeService.update(code);
        return  "redirect:" + baseUrl + "/mgt";
    }

    @RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
    public String delete(@PathVariable int id){
        codeService.deleteBy(id);
        return  "redirect:" + baseUrl + "/mgt";
    }


    @RequestMapping(value = "/{id}", method = RequestMethod.GET)
    public String view(@PathVariable int id, Model model){
        model.addAttribute("model", codeService.getById(id));
        return baseUrl + "/view";
    }

}

이전 코드에 비해서 달라진게 별로 없지만.. 일단은 baseUrl 속성을 둬서 @RequestMapping에 넣어주는 값을 읽어서 사용하도록 코드를 수정했습니다. 그리고 "code"를 "model"로 바꿨고, "codeList"를 "list"로 바꿨습니다. "ref"애초부터 추상화 시킨 이름을 사용했으니 손댈 필요가 없었습니다.

다음에 할 작업은 화면과 연결하는 겁니다. 화면에 전달하는 객체 이름이 바꼈으니 화면에서 EL 부분을 손봐줍니다.

다음은 GenericControlle을 만듭니다.

@SessionAttributes("model")
public abstract class GenericController<GS extends GenericService<E, S>, R, E, S> {

    @Autowired ApplicationContext applicationContext;

    GS service;
    R ref;

    Class<GS> serviceClass;
    Class<R> refClass;
    Class<E> entityClass;
    Class<S> searchParamClass;

    protected String baseUrl;

    public GenericController() {
        RequestMapping rm = this.getClass().getAnnotation(RequestMapping.class);
this.baseUrl = rm.value()[0];

        this.serviceClass = GenericUtils.getClassOfGenericTypeIn(getClass(), 0);
        this.refClass = GenericUtils.getClassOfGenericTypeIn(getClass(), 1);
        this.entityClass = GenericUtils.getClassOfGenericTypeIn(getClass(), 2);
        this.searchParamClass = GenericUtils.getClassOfGenericTypeIn(getClass(), 3);
    }

    @ModelAttribute("ref")
public R ref() {
return ref;
}

    @RequestMapping(value="/mgt")
    public void mgt(Model model){
        try {
            model.addAttribute("searchParam", searchParamClass.newInstance());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @RequestMapping(method = RequestMethod.GET)
    public void list(Model model, S searchParam, PageParam pageParam) {
        model.addAttribute("list", service.list(pageParam, searchParam));
    }

    @RequestMapping(value = "/form", method = RequestMethod.GET)
    public String addForm(Model model){
        try {
            model.addAttribute("model", entityClass.newInstance());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return baseUrl + "/new";
    }

    @RequestMapping(method = RequestMethod.POST)
    public String addFormSubmit(Model model, @Valid @ModelAttribute("model") E e, BindingResult result, SessionStatus status){
        if(result.hasErrors())
            return baseUrl + "/new";
        status.setComplete();
        service.add(e);
        return  "redirect:" + baseUrl + "/mgt";
    }

    @RequestMapping(value = "/{id}/form", method = RequestMethod.GET)
    public String editForm(@PathVariable int id, Model model){
        model.addAttribute("model", service.getById(id));
        return baseUrl + "/edit";
    }

    @RequestMapping(value = "/{id}", method = RequestMethod.PUT)
    public String editFormSubmit(Model model, @Valid @ModelAttribute("model") E e, BindingResult result, SessionStatus status){
        if(result.hasErrors())
            return baseUrl + "/edit";
        status.setComplete();
        service.update(e);
        return  "redirect:" + baseUrl + "/mgt";
    }

    @RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
    public String delete(@PathVariable int id){
        service.deleteBy(id);
        return  "redirect:" + baseUrl + "/mgt";
    }

    @RequestMapping(value = "/{id}", method = RequestMethod.GET)
    public String view(@PathVariable int id, Model model){
        model.addAttribute("model", service.getById(id));
        return baseUrl + "/view";
    }

    @PostConstruct
    public void setUp(){
        this.service = applicationContext.getBean(serviceClass);
        this.ref = applicationContext.getBean(refClass);
    }

}

이전 글에서 만든 GenericUtils를 이용해서 타입 추론을 하고 그렇게 알아낸 타입을 사용해서 new Code() 하던 부분은 codeClass.newInstance()로 바꾸고, GS service = applicationContext.getBean(serviceClass) 이렇게 applicationContext에서 Class 타입으로 빈을 가져올 때 사용합니다.

필요한 타입은 4개 GenericService, Ref, Entity, SearchParam
Code 도메인 기준으로는 : CodeService, COdeRef, Code, CodeSearchParam이 필요합니다.

자 이제 CodeController 코드를 GenericController를 사용하도록 수정합니다.

@Controller
@RequestMapping("/base/code")
public class CodeController extends GenericController<CodeService, CodeRef, Code, CodeSearchParam> {

}

끝입니다. 맨 위에 있는 CodeController 코드와 비교하면.. 뭐. 거의 10배는 코드량을 줄인것 같네요. 
이제 부터 찍어내는 일만... 아... 아니군요;;

뷰 코드까지 정리해야 찍어낼 수 있습니다. 컨트롤러까지만 만들면 뭐하나요. 
화면이 안나오는데.. OTL...