Play! Framework 2.2.0 x google Guice 4.0-beta for Dependency Injection(D.I.)

의존성 주입(Dependency Injection)에 대한 설명은 못 하겠고 어쨌든 계속 써오던 것이라 play framework에서도 사용하도록 한다.
Spring은 종합선물세트라 이런게 다 잘 되어 있지만 play framework는 사실 개념도 좀 다르고 해서 기본으로 탑재되어 있지는 않은 듯. 그래서 google에서 plug-in을 만들어 줬다.

설정은 간단하다. build.sbt 혹은 Build.scala(2.2 이하)에 dependency만 추가해 주면 사용이 가능하다. 현재 3.0까지 있고 3.0이 릴리즈된지 꽤 된 것 같아서 beta지만 4.0을 써보기로 한다.

libraryDependencies ++= Seq(
 "org.mybatis" % "mybatis" % "3.1.1",
 "mysql" % "mysql-connector-java" % "5.1.26",
 "net.sf.flexjson" % "flexjson" % "3.1",
 "com.google.inject" % "guice" % "4.0-beta",
 javaCore,
 javaJdbc,
 cache
)

목표는 DI의 의미에 맞게 컨트롤러에 다 모여 있던 서비스에 해당하는 부분을 분리하고 그 서비스는 다시 인터페이스와 그 구현부로 나눈 후에 그걸 컨트롤러에 주입(Injection)하는 것이므로 우선 서비스를 제조한다. 인터페이스부터 시작.
패키지는 알아서 만들고 인터페이스는 아래와 비슷하게 만든다.

package services;

import models.User;

public interface UsersService { 
 public User getUser(String id);
}

이제 저 인터페이스의 구현체를 만든다. UsersDAO는 신경쓰지 말고 일단 구현.

package services.impl;

import dao.UsersDAO;
import models.User;
import services.UsersService;

public class UsersImpl implements UsersService {
 
 private UsersDAO userDao;

 public UsersImpl() {
  play.Logger.info("Users services constructor");
 }

 @Override
 public User getUser(String id) {
  userDao = new UsersDAO();
  User user = userDao.getUser(id);
  
  return user;
 } 
}

구현체까지 만들었으면 이제 컨트롤러로 이동한다.

package controllers;

import com.google.inject.Inject;

import models.User;
import flexjson.JSONSerializer;
import play.mvc.Controller;
import play.mvc.Result;
import services.UsersService;

public class UsersController extends Controller {

 @Inject
 private UsersService usersService;
 
 public Result getUser(String id) {
  
         JSONSerializer serializer = new JSONSerializer();

         User user = new User();
         user = usersService.getUser(id);
         String jsonResult = serializer.prettyPrint(true).serialize(user);
     
         play.Logger.info(jsonResult);
     
     return ok(jsonResult);
 }
 
}

저기서 @Inject 어노테이션으로 서비스를 컨트롤러에 주입하는 부분이 핵심이다. 그리고 실제 usersService를 사용하는 부분이 있는데 이 다음에 만들 GlobalSetting이 어설프면 저게 안된다. 주입이 안되니 객체도 없으니까.
그럼 이제 후반부 작업. routes를 손봐야 한다. 보다시피 컨트롤러의 메소드가 기존과 달리 static이 아닌데 routes의 url에 매핑된 메소드가 더 이상 static이 아니므로 @ 심볼을 붙여줘야 한다. 그래야 알아먹음.

GET  /users/:id     @controllers.UsersController.getUser(id: String)

이제 마지막. app/Global.java로 injection setting 클래스를 만들어 준다. 파일의 위치에 주의할 것. 분리한답시고 다른 폴더 만들어서 거기 넣어두면 읽지를 못해서 위의 컨트롤러에 주입해 준 서비스를 불러오지 못한다. 그래서 NullpointerException이 발생함.
Injector를 만들고 injector에서 객체를 반환받기 위해 getControllerInstance메소드를 오버라이드 한다. 위의 @ 심볼이 붙은 url로 접근하면 이 메소드를 호출한단다.

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;

import play.Application;
import play.GlobalSettings;
import services.UsersService;
import services.impl.UsersImpl;

public class Global extends GlobalSettings {

 private Injector injector;
 
 @Override
 public void onStart(Application application) {
  
  injector = Guice.createInjector(new AbstractModule() {
   
   @Override
   protected void configure() {
    
    bind(UsersService.class).to(UsersImpl.class);
    
   }
  });
 }
 
 @Override
 public <t> T getControllerInstance(Class<t> aClass) throws Exception {
  
  return injector.getInstance(aClass);
 }
}

이제 다시 테스트를 해 보면 기존과 같이 잘 되는 것을 볼 수 있음.

댓글 없음:

댓글 쓰기