[AspectJ] LTW시 메이븐에 VM 옵션주기

스프링 @Configurable을 이용하려면 정적인 컴파일 시점 위빙으로는 안 됩니다. 객체가 필요해서 해당 클래스를 처음 클래스로더가 읽어갈 때.. 그 때 aop.xml 정보를 참조해서 위빙을 해야 합니다. 그게 바로 LTW죠.

-javaagent:/path/to/spring-agent.jar

그러려면 이런 옵션을 VM 인자로 넘겨줘야 합니다. 이클립스에서 JUnit 테스트를 실행할 때도 예외는 아닙니다.


이런식으로 모든 테스트에 인자를 줘야 하는데.. 여간 귀찮은 작업이 아닙니다. 프로젝트 별로 VM 인자를 설정하는 방법이 없나. 모르겠습니다.

메이븐으로 빌드를 하면 당연히 실패하겠죠. 그래서 메이븐에도 설정해줘야합니다. test 할 때 위빙 옵션을 다음과 같이 줄 수 있습니다.

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.4.2</version>
                <configuration>
                    <argLine>-javaagent:weaving/spring-agent.jar</argLine>
                </configuration>
            </plugin>

이클립스 + 콘솔창 한개는 기본으로 들고 개발을 해야겠습니다. 안그래도 git와 mvn war:inplace 때문에 거의 항상 콘솔창 하나를 띄워서 같이 작업하는데, mvn clean test도 애용하게 생겼네요.

[AspectJ] 톰캣 6.X에 LTW(load time weaving) 설정하기

1. 톰캣 홈/lib 폴더에 spring-tomcat-weaver.jar를 복사해서 넣어둡니다.

cfile23.uf.204EC9264A32306366AFE4.jar
2.5.6 버전 배포할 때 제공된 녀석인데, 3.0 M3에서 확인해본 결과 잘 동작하더군요.

2. server.xml에서 context 정보를 수정해줍니다.

<Context docBase=”C:\workspace\koma-ddd\webapp” path=”/” reloadable=”false”>
          <Loader loaderClass=”org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader”/>
</Context>

Context 내부에서 위와 같이 loader 엘리먼트를 추가해주면 됩니다.

3. applicationContext.xml에 다음 엘리먼트를 추가합니다.

    <context:load-time-weaver />

끝입니다. @_@ 참.. 쉽죠~

[AspectJ] Extension and Implementation

http://www.eclipse.org/aspectj/doc/released/progguide/semantics-declare.html#extension-and-implementation

    declare parents: EmpDao extends GenericDao<Emp, EmpParams>;
    declare parents: EmpDaoImpl extends HibernateGenericDao<Emp, EmpParams>;
    declare @type: EmpDaoImpl: @org.springframework.stereotype.Repository;

EmpDao 클래스가 GenericDao 클래스를 상속 받도록 설정.
EmpDaoImpl 클래스가 HibernateGenericDao 클래스를 상속 받도록 설정.
EmpDaoImpl 클래스에 @Repository 애노테이션 추가.

문법이 복잡해 보였는데 막상 사용해보니 간단 간단 하네요.

public interface EmpDao {

}

public class EmpDaoImpl implements EmpDao {

}

이런 기초적인 코드만 존재하지만..

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={“/applicationContext.xml”, “/applicationContext-datasource.xml”})
@Transactional
public class EmpDaoImplTest {

    @Autowired
    EmpDaoImpl daoImpl;

    @Test
    public void daoInterface() throws Exception {
        assertNotNull(daoImpl);
        daoImpl = new EmpDaoImpl();
        GenericDao<Emp, EmpParams> gdao = daoImpl;
        HibernateGenericDao<Emp, EmpParams> hgdao = daoImpl;
    }

    @Test
    public void crud() throws Exception {
        Emp emp = new Emp();
        daoImpl.add(emp);
        daoImpl.flush();
        assertEquals(1, daoImpl.getAll().size());
    }

}

이런 테스트를 돌릴 수 있다는거…

[AspectJ] privileged aspect

public class Emp {

    private String name;

    private String email;

}

이런 클래스가 있습니다. 이게 전부입니다.

public class EmpTest {

    @Test
    public void javaBean() throws Exception {
        Emp emp = new Emp();
        emp.setName(“keesun”);
        assertEquals(“keesun”, emp.getName());
        emp.setEmail(“keesun@email.com”);
    }

}

이런 테스트가 돌아갈까요? 훗.. 그럴리가요. 있지도 않은 메서드(게터, 세터)를 마구 썼는데 될리가 없죠. 그러나..  잘 돌아갑니다.


어떻게 된걸까요? privileged aspect를 사용하면 타겟의 private 또는 protected 멤버에도 접근할 수 있습니다.

http://www.eclipse.org/aspectj/doc/released/progguide/semantics-aspects.html#aspect-declaration

스프링 AOP로 이런 일을 하려면 Introduction을 사용 해야겠는데.. 그게 참.. 그리 쉽지 않았던 기억이 납니다. 하지만 AspectJ로는 간단하네요~

privileged aspect EmpAspect {

    //JavaBean
    public String smdis.model.Emp.getName() {
        return this.name;
    }

    public void smdis.model.Emp.setName(String name) {
        this.name = name;
    }

    public String smdis.model.Emp.getEmail() {
        return this.email;
    }

    public void smdis.model.Emp.setEmail(String email) {
        this.email = email;
    }

}

이렇게 추가할 메서드를 넣어주고 마치 자기가 가지고 있는 변수처럼 사용하면 됩니다.

AspectJ의 @DeclareError를 사용해서 컴파일 시점에 아키텍처 에러 검증하자.

참조: http://www.parleys.com/display/PARLEYS/Home#slide=1;title=Spring%20Architectures;talk=20676612

위 발표자료 내용 주에 아주 잼나는 코드를 건졌습니다. 지난 번 KSUG에서 발표한 내용과 겹치는데 아래 코드는 그때 제가 보여드린 코드보다 좀 더 좋은 것 같아서 가져왔습니다.

@Aspect
public class SystemArchitecture {
  @Pointcut(“execution(* configurator.*.*(..))”)
  public void configuratorLogic () {}
  @Pointcut(“execution(* dao.*.*(..))”)
  public void dao() {}
  @Pointcut(“within(*.dao.*)”)
  public void inDaoLayer() {}
  @Pointcut(“call(* *.service.*.*(..))”)
  public void callServiceLayer() {}
}
@Aspect
public class Layering {
  @DeclareError(“SystemArchitecture.inDaoLayer() && “+
   “SystemArchitektur.callServiceLayer() “)
  public static final String DAOsNotFromServices =
   “DAO must not call Service!”;
 @DeclareError(” (call(* java.sql.*.*(..)) && ” +
  “!within(*.dao.*) ) “)
  public static final String JdbcOnlyInDAOs =
   “JDBC only in DAOs!”;
}

좋은 건 이Aspectj를 사용하면 @DecalreError를 사용해서, 컴파일 시점에 아키텍처 에러를 검증할 수 있다는 것입니다. 제가 준비했던 코드는 cflow를 사용해서 런타임에 검증하는 방법이었습니다. 따라서 테스트 하지 않고 그냥 커밋하면 뭐 어떻게 찾아낼 방법이 없었습니다. 그런데 이 방법을 쓰면 코딩할 때 문제되는 코드를 발견할 수 있으니 훨씬 좋은 것 같습니다. 캬.. 귿..