intellij 12.1.5 버전에서 컴파일과 관련한 문제로 Lombok이 제대로 동작하지 않는 기현상을 보였다. 이에 jetbrain에서는 급히 컴파일 문제를 해결한 12.1.6 으로 업그레이드버전을 제공했다. 그런데 이녀석으로 업그레이드를 한 이후에 계속 컴파일 중에 오류가 발생한다.


로그에서 'ajc: the method ...' 라고 하는 메시지를 출력하며, Lombok annotation을 사용한 클래스가 정상적으로 컴파일 되지 않았다.

Lombok의 annotation이 컴파일시에 제대로 동작하지 않아서 생기는 문제가 아닐까 하고 Lombok plugin 재설치를 몇번해보고 설정에서 Compiler - Annotation Processors 에서 'Enable annotation processing'을 체크했다가 해제했다가를 반복했지만 증상은 똑같았다.

인터넷 검색을 해도 딱히 답이 나오지 않던 상황...

불현듯 '컴파일러!' 가 떠오르면서 'Compiler - Java Compiler' 를 찾아 들어갔다. 

'Use Compiler' 에 보니까 내가 모르는 Ajc가 선택되어 있다.

컴파일러를 'javac' 로 변경하고 나니 정상적으로 컴파일되고 배포까지 완료되었다...!! 두둥!

로그 속에 답이 있다는 진리를 새삼 깨닫다.


 

 정리

 

Intellij 12.1.6 업그레이드 후에도 Lombok 이 정상동작 하지 않을 시,

  • 이동: [Settings...] - [Compiler] - [Java Compiler]
  • 변경: 'User Compiler' - javac 로 변경



... Default는 javac라면서? 난... Compiler 변경한 적 없다!!

얼마 전에 JSON을 사용해서 AJAX 통신 API를 만드는 과정에서 삽질을 한 적이 있다. 기본만 제대로 살폈으면 쉽게 넘어갈 수 있는 일이었는데 나의 얼렁뚱땅 대충대충 ‘필요하면 그때그때 찾아서 쓰면 되지 뭐.’ 라는 안일함이 하루의 시간을 날려먹는 상황을 낳고 말았다. 방법은 정말 간단했다.

헤맸던 소스: Before Source

  • ● TestForm.java

    @Data
    @NoArgsConstructor
    @ToString
    public class TestForm {
      private String id;
      private String name;
      private List<TestTag> testTags;
    
      @Data
      @NoArgsConstructor
      @ToString
      public class TestTag {
          private String id;
          private String tag;
      }
    }
    
  • ● TestController.java

    @Controller
    public class TestController {
      private static final Logger logger = LoggerFactory.getLogger(TestController.class);
    
      @RequestMapping(value="/test", method=RequestMethod.GET)
      public void test(@RequestBody TestForm form, ModelMap map) {
          logger.debug("TestForm : {}", form);
      }
    }
    
  • ● Form JSON

      var form = {
          id: "123",
          name: "123",
          testTags: [{id: "1111", tag: "2222"}]
      };
    
      $.ajax({
          url: "http://localhost:8080/test",
          method: "get",
          type: "json",
          data: form,
          success: function(data) {
              console.log(data);
          }
      });
    

    여러가지 시도를 해봤지만, form의 데이터를 TestController의 test에서 제대로 받아들이지 못하는 문제로 골머리를 썩었다(지금 생각해보면 나의 무식함에 부끄럽지만).

해결 코드: After Source

  • ● TestForm.java

    @Data
    @NoArgsConstructor
    @ToString
    public class TestForm {
      private String id;
      private String name;
      private List<TestTag> testTags;
    
      @Data
      @NoArgsConstructor
      @ToString
      public static class TestTag {
          private String id;
          private String tag;
      }
    }
    
  • ● TestController.java

    @Controller
    public class TestController {
      private static final Logger logger = LoggerFactory.getLogger(TestController.class);
    
      @RequestMapping(value="/test", method=RequestMethod.POST)
      public void test(@RequestBody TestForm form, ModelMap map) {
          logger.debug("TestForm : {}", form);
      }
    }
    
  • ● Form JSON

      var form = {
          id: "123",
          name: "123",
          testTags: [{id: "1111", tag: "2222"}]
      };
    
      $.ajax({
          url: "http://localhost:8080/test",
          method: "post",
          type: "json",
          contentType: "application/json",
          data: JSON.stringify(form),
          success: function(data) {
              console.log(data);
          }
      });
    

Before Source와 After Source의 차이를 눈치챘는가? ㅡ_-)?
RequestMethod가 GET에서 POST로 변경되었다. 이에 대한 설명을 해본다. 토비의 스프링에 @RequestBody, @ResponseBody 를 살펴보기 바란다.

@RequestBody, @ResponseBody

최근 개발하고 있는 방식은 대부분이 프론트엔드와 백엔드를 분리하여 개발을 하고 있다. 프론트엔드의 AJAX요청은 대부분 JSON으로 되어 있고, 이에 맞춰 백엔드에서도 JSON 형태로 응답을 해주는 방식을 취하게 된다. 스프링에서는 이와 관련된 @MVC 관련 애노테이션과 설정을 통해 기능을 제공하고 있다.

  • ● @RequestBody

    이 애노테이션이 붙은 파라미터에는 HTTP 요청의 본문body 부분이 그대로 전달된다.
    AnnotationMethodHandlerAdapter에는 HttpMessageConverter 타입의 메시지 변환기message converter가 여러 개 등록되어 있다. @RequestBody가 붙은 파라미터가 있으면 HTTP 요청의 미디어 타입과 파라미터의 타입을 먼저 확인한다(servlet-context.xml 에서 <annotation-drvien> 태그 내에 선언하는 <message-converter> 에서 확인). 메시지 변환기 중에서 해당 미디어 타입과 파라미터 타입을 처리할 수 있다면, HTTP 요청의 본문 부분을 통째로 변환해서 지정된 메소드 파라미터로 전달해준다.

    내가 헤매던 부분이 바로 이부분이었다. ㅡ_-);;JSON 메시지 변환기에는 MappingJackson2HttpMessageConverter를 사용했다. @RequestBody 애노테이션은 요청에서 Body부분을 살펴 요청된 데이터를 추출하여 파라미터로 변환해주는데, ‘GET’ 메소드 요청의 경우에는 HTTP Body에 요청이 전달되는 것이 아니라, URL의 파라메터로 전달(ex: http://localhost:8080/test?id=123&name=123&testTag=…) 형식으로 전달되기 때문에 @RequestBody로 받으려고 해도 서로 다른 곳을 보며 데이터가 없다는 결과를 던질 수밖에 없다(이 부분도 로그에 대해서 상세하게 설정해서 살펴보면서 확인한 결과. 로그! 개발 중에 문제가 되는 요인들을 찾기 위해서 관심을 가지자).

  • ● @ResponseBody

    @ResponseBody는 @RequestBody와 비슷한 방식으로 동작한다. @ResponseBody가 메소드 레벨에서 부여되면 메소드가 리턴하는 오브젝트는 뷰를 통해 결과를 만들어내는 모델로 사용하는 대신, 메시지 컨버터를 통해 바로 HTTP 응답의 메시지 본문으로 변환된다.

    간단히 이야기 하자면, 요청한 형태에 맞춰서 메시지 변환기를 통해 결과값을 반환한다. ‘콩심은 데 콩나고 팥 심은데 팥난다.’ 랄까? ContentNegotiatingViewResolver 와는 동작방식이 좀 다르다. ContentNegotiatingViewResolver는 등록되어 있는 ViewResolver중에서 controller 메소드의 리턴값을 통해 등록된 ViewResolver 중에서 적합한 형태로 처리해서 반환하는 반면, @ResponseBody는 @RequestBody가 선택한 형식으로 결과값을 변환하여 반환한다고 보면 된다.

  • ● MessageConverter 메시지 변환기의 종류는 Spring API 문서를 참고하자.

정리

해당하는 애노테이션들이 어떻게 동작하는지 내가 했던 인터넷 설정들이 어떻게 반응하는지를 제대로 이해했다면, 별다른 삽질없이 조용히 넘어갈 수 있던 문제였는데, 쉬운 문제였다. 하아!!
요즘 들어서 부쩍 ‘기본을 탄탄히 갖춰야겠다.’라는 생각을 하게되는 일들이 많아지고 있다.

끊임없이 공부하고 공부하라!

  • ● 이와 관련된 내용들은 ‘[토비의 스프링]에서 [스프링 @MVC]’ 관련 내용을 상세하게 설명되어 있다.



프로젝트를 실행시키려고 했는데, 생성된 QueryDSL용 Q도메인(@Entity 애노테이션이 사용된 도메인 객체를 QueryDSL에서 변형가공)을 인식하지 못하는 문제가 발생했다. 이클립스에서는 별다른 문제없이 잘 되는 녀석이 인텔리제이에서는 Q도메인들을 찾지 못하는 문제를 뱉어낸 것이다. 그래서 인텔리제이에서 생성한 서버에서 사용하는 디렉토리를 확인했다. 서버에 배포된 클래스의 상태를 보니 Q도메인들이 빠져있어서 그렇다는 것을 확인했다.

8월 14, 2013 9:29:34 오전 org.apache.catalina.core.ApplicationContext log
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'companyServiceImpl': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private net.slipp.rest.repository.CompanyRepository net.slipp.rest.service.impl.CompanyServiceImpl.companyRepository; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'companyRepositoryImpl' defined in file [/workspace/git-repository/rest-api-study/bin/WEB-INF/classes/net/slipp/rest/repository/CompanyRepositoryImpl.class]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [net.slipp.rest.repository.CompanyRepositoryImpl]: Constructor threw exception; nested exception is java.lang.IllegalArgumentException: Did not find a query class net.slipp.rest.domain.QCompany for domain class net.slipp.rest.domain.Company!
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:288)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1122)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:522)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:461)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:295)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:292)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:626)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:932)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:479)
    at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:389)
    at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:294)
    at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:112)
    at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4887)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5381)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:901)
    at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:877)
    at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:633)
    at org.apache.catalina.startup.HostConfig.manageApp(HostConfig.java:1552)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:301)
    at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)
    at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801)
    at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:622)
    at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:569)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:301)
    at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)
    at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801)
    at javax.management.remote.rmi.RMIConnectionImpl.doOperation(RMIConnectionImpl.java:1487)
    at javax.management.remote.rmi.RMIConnectionImpl.access$300(RMIConnectionImpl.java:97)
    at javax.management.remote.rmi.RMIConnectionImpl$PrivilegedOperation.run(RMIConnectionImpl.java:1328)
    at javax.management.remote.rmi.RMIConnectionImpl.doPrivilegedOperation(RMIConnectionImpl.java:1420)
    at javax.management.remote.rmi.RMIConnectionImpl.invoke(RMIConnectionImpl.java:848)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:322)
    at sun.rmi.transport.Transport$1.run(Transport.java:177)
    at sun.rmi.transport.Transport$1.run(Transport.java:174)
    at java.security.AccessController.doPrivileged(Native Method)
    at sun.rmi.transport.Transport.serviceCall(Transport.java:173)
    at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:553)
    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:808)
    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:667)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:724)
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private net.slipp.rest.repository.CompanyRepository net.slipp.rest.service.impl.CompanyServiceImpl.companyRepository; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'companyRepositoryImpl' defined in file [/workspace/git-repository/rest-api-study/bin/WEB-INF/classes/net/slipp/rest/repository/CompanyRepositoryImpl.class]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [net.slipp.rest.repository.CompanyRepositoryImpl]: Constructor threw exception; nested exception is java.lang.IllegalArgumentException: Did not find a query class net.slipp.rest.domain.QCompany for domain class net.slipp.rest.domain.Company!
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:514)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:285)
    ... 56 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'companyRepositoryImpl' defined in file [/workspace/git-repository/rest-api-study/bin/WEB-INF/classes/net/slipp/rest/repository/CompanyRepositoryImpl.class]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [net.slipp.rest.repository.CompanyRepositoryImpl]: Constructor threw exception; nested exception is java.lang.IllegalArgumentException: Did not find a query class net.slipp.rest.domain.QCompany for domain class net.slipp.rest.domain.Company!
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1013)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:959)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:490)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:461)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:295)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:292)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:285)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:910)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:853)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:768)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:486)
    ... 58 more
Caused by: org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [net.slipp.rest.repository.CompanyRepositoryImpl]: Constructor threw exception; nested exception is java.lang.IllegalArgumentException: Did not find a query class net.slipp.rest.domain.QCompany for domain class net.slipp.rest.domain.Company!
    at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:163)
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:87)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1006)
    ... 71 more
Caused by: java.lang.IllegalArgumentException: Did not find a query class net.slipp.rest.domain.QCompany for domain class net.slipp.rest.domain.Company!
    at org.springframework.data.querydsl.SimpleEntityPathResolver.createPath(SimpleEntityPathResolver.java:63)
    at net.slipp.rest.repository.querypredicate.AbstractJPAQueryPredicateExecutor.<init>(AbstractJPAQueryPredicateExecutor.java:47)
    at net.slipp.rest.repository.CompanyRepositoryImpl.<init>(CompanyRepositoryImpl.java:12)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
    at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:148)
    ... 73 more
Caused by: java.lang.ClassNotFoundException: net.slipp.rest.domain.QCompany
    at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1714)
    at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1559)
    at org.springframework.util.ClassUtils.forName(ClassUtils.java:260)
    at org.springframework.data.querydsl.SimpleEntityPathResolver.createPath(SimpleEntityPathResolver.java:53)
    ... 80 more

8월 14, 2013 9:29:37 오전 org.apache.catalina.core.ApplicationContext log

인터넷을 통해 어떻게 처리해야할까 하고 잠시 검색한다.

그러다가 찾았다.

Unable to use Intellij with a generated sources folder :

http://stackoverflow.com/questions/5170620/unable-to-use-intellij-with-a-generated-sources-folder



 

 해결방법 

 

1. [File] -> [Project structure] -> [Module] -> [Sources] 탭으로 이동

2. (오른쪽 창에 있는) QueryDSL 소스디렉토리 선택

3. 마우스 우클릭

4. [Source] 선택
  Source로 선택된 디렉토리는 파란색으로 변경되고, 왼쪽에 등록된다. 

이걸 어제 찾아놓고 바로 확인 안하고 이제서야 확인을 했군요. Orz..

얼마 전까지 SpringSource.org 에서 제공하는 STS 를 이용해서 개발해왔다.


그러다가 아는 분이 저렴하게 구매하신 인텔리제이intelliJ 라이센스를 넘겨받아 사용하기 시작하고 있다. 처음에는 인텔리제이의 강력한 리팩토링 기능이 마음에 들어서 STS에서 작성한 코드들에 대해서 리팩토링을 할 때에만 주로 사용해왔었는데, STS(현재 우분투 13.04에서 openjdk-7-sdk 를 사용하고, STS 3.2.0 에서 3.3.0으로 업그레이드하여 사용)에서 Interface 에 정의되어 있는 메소드를 자동완성시켜주지 않는 버그가 발생하면서 인텔리제이로 마음이 확 기울어져버렸다.


인텔리제이를 사용하면서 가장 아쉬웠던 것이 하나 있었는데, 이클립스에서 별다른 설정없이 사용했던 'Auto publicsing'이었다. 웹 프로젝트를 열어놓은 상태로 로컬 웹서버에 배포하여 테스트하면서 개발할 때, 클래스 파일이나 JSP 파일 등을 수정했을 때, 자동으로 컴파일되고 서버에 배포되는 기능이 인텔리제이에서 제공하지 않는다고 아쉬워했었다(다 방법이 있었다!).

위에서 보는 것처럼, 모듈의 Run/Debug Configurations 에서  'On frame deactivation' 항목을 'Update classess and resources'를 설정해 놓으면, 애플리케이션이 잠시 대기하고 있는 동안에 변경사항을 확인하여 변경된 클래스와 리소스를 갱신시켜준다. 

그리고 실행할 때는 디버그Debug 모드로 실행시킨다.

혹은


왼쪽 하단에 있는 서버 항목 중에서 'Update Resources On Frame Deaction'을 클릭해서 활성화시켜도 된다. 이렇게 설정해두면, 애플리케이션이 대기상태에 들어가면 주기적으로 변동사항들을 확인하는 작업을 하면서 자동 배포가 된다.

Helloworld를 살펴보자! 

public class Helloworld {
	public static void main(String[] args) {
		System.out.println("Hello world.");
	}
}

javac Helloworld.java

생성된 Helloworld.class를 살펴보자.

javap -c -l -v Helloworld.class

javap [번역] 는 클래스(.class) 파일 역어셈블러(역컴파일러는 이상한가? ㅡ_-)?)이다. 컴파일되어서 바이트코드가 된 클래스파일을 살펴본 적이 없었다. ㅡ0-);;

Classfile /ihoneymon/Documents/Helloworld.class
  Last modified 2013. 6. 26; size 426 bytes
  MD5 checksum 1c34705a72fff1557ef69c977ce53552
  Compiled from "Helloworld.java"
public class Helloworld
  SourceFile: "Helloworld.java"
  minor version: 0
  major version: 51
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #6.#15         //  java/lang/Object."<init>":()V
   #2 = Fieldref           #16.#17        //  java/lang/System.out:Ljava/io/PrintStream;
   #3 = String             #18            //  Hello world.
   #4 = Methodref          #19.#20        //  java/io/PrintStream.println:(Ljava/lang/String;)V
   #5 = Class              #21            //  Helloworld
   #6 = Class              #22            //  java/lang/Object
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               main
  #12 = Utf8               ([Ljava/lang/String;)V
  #13 = Utf8               SourceFile
  #14 = Utf8               Helloworld.java
  #15 = NameAndType        #7:#8          //  "<init>":()V
  #16 = Class              #23            //  java/lang/System
  #17 = NameAndType        #24:#25        //  out:Ljava/io/PrintStream;
  #18 = Utf8               Hello world.
  #19 = Class              #26            //  java/io/PrintStream
  #20 = NameAndType        #27:#28        //  println:(Ljava/lang/String;)V
  #21 = Utf8               Helloworld
  #22 = Utf8               java/lang/Object
  #23 = Utf8               java/lang/System
  #24 = Utf8               out
  #25 = Utf8               Ljava/io/PrintStream;
  #26 = Utf8               java/io/PrintStream
  #27 = Utf8               println
  #28 = Utf8               (Ljava/lang/String;)V
{
  public Helloworld();
    Signature: ()V
    flags: ACC_PUBLIC
    LineNumberTable:
      line 1: 0
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0       
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return        
      LineNumberTable:
        line 1: 0

  public static void main(java.lang.String[]);
    Signature: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    LineNumberTable:
      line 3: 0
      line 4: 8
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #3                  // String Hello world.
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return        
      LineNumberTable:
        line 3: 0
        line 4: 8
}

이 Helloworld 클래스의 경우에는 main() 메소드에 의해서 실행되다 보니 더욱 절차적인 형태를 띄게 되지 않았을까? 

역컴파일된 클래스파일을 보면, 실행순서와 참조하는 라이브러리들을 확인할 수가 있다. 이런거였군~!


이제 조금은 자바를 대하는 태도가 변하고 있는 듯 하다. 얼마 전까지만 해도 구현하는 것에만 집착했었는데, 최근에는 개발환경 구축, CI, 변경이력관리, 아키텍처 등에 대해서 조금씩 시선이 돌아가고 있다. 그만큼, 내 역량이 성장했다는 증거가 되길 바란다.

+ Recent posts