Whiteship's Note

'2010/03'에 해당되는 글 30건

  1. 2010.03.31 [ASM] 클래스 - 구조
  2. 2010.03.30 KSUG 제10회 한국 스프링 사용자 모임 세미나 (4)
  3. 2010.03.29 EJ2E Item 24. unchecked 경고를 제거하자 (1)
  4. 2010.03.29 [ActionScript 3.0] 클래스
  5. 2010.03.26 EJ2E Item 23. 새로 작성하는 코드에서는 raw type 쓰지 말자
  6. 2010.03.26 [Spring 3.0 & Atlassian] RestTemplate으로 Confluence의 Space 목록 가져오기
  7. 2010.03.24 [Flex] 플래시 개발 시작
  8. 2010.03.24 [Flex] 플렉스의 북치기 박치기, ActionScript와 MXML
  9. 2010.03.24 [구글 크롬] 스프링 소스 한글 사이트
  10. 2010.03.24 EJ2E Item 22. nonstatic 보다는 static 멤버 클래스를 선호하라
  11. 2010.03.24 EJ2E Item 21. 전략을 표현할 때는 함수 객체를 사용하라
  12. 2010.03.19 [Spring Security] Method Security Test
  13. 2010.03.17 나는 null 중대 소속이다.. 너는 어디냐.. (13)
  14. 2010.03.17 봄싹 개발자 모집합니다. (완료됨) (4)
  15. 2010.03.16 [스프링 3.0] @Value 이용해서 기본값 설정하기
  16. 2010.03.15 [톰캣] 톰켓 7의 메모리 누수 방지
  17. 2010.03.12 Visitor 패턴 (2)
  18. 2010.03.12 [Agile 번역] 어떻게 하면 번역을 기민하게 할 수 있을까? (4)
  19. 2010.03.11 [ClassLoader] Thread의 getContextClassLoader() (2)
  20. 2010.03.11 [구글 Visualization API] Fluent Interface 적용하기
  21. 2010.03.10 [거꾸로 읽는 세계사] 드레퓌스 사건을 보고서...
  22. 2010.03.09 Class.forName()와 ClassLoader.loadClass() 차이점 (2)
  23. 2010.03.07 [Google Visualization API] 캬.. 멋지구나~ (6)
  24. 2010.03.05 톰캣 메일링 리스트 가입하기
  25. 2010.03.04 [스프링 3.0] @Async 테스트
  26. 2010.03.03 [MySQL Administrator] Row 갯수 제대로 못세나? (2)
  27. 2010.03.03 우편번호 검색&입력기 만들기
  28. 2010.03.02 톰캣 6.0 클래스로더 구조
  29. 2010.03.02 null은 캐스팅이 되는건야 안 되는거야? (10)
  30. 2010.03.02 봄싹은 마치 경차같은... (4)

[ASM] 클래스 - 구조

Good Tools : 2010.03.31 17:32


참조: http://download.fr.forge.objectweb.org/asm/asm-guide.pdf

컴파일 된 클래스 구성요소
- 클래스의 접근자, 이름, 상위 클래스, 인터페이스, 애노테이션
- 클래스에 선언한 필드당 한 섹션. 각 섹션에는 필드의 접근자, 이름, 타입, 애노테이션
- 클래스의 메서드와 생성자 당 한 섹션. 각 섹션에는 접근자, 이름, 반환 타입, 매개변수 타입, 애노테이션, 컴파일된(바이트) 코드

소스와 컴파일된 클래스와 차이
- 컴파일된 클래스는 오직 한 클래스에 대한 정보만 나타내지만 소스는 여러 클래스 가질 수 있다.
- 컴파일된 클래스는 주석을 가지고 있지 않다. 하지만 애노테이션에 부가 정보를 담을 수 있다. 그 전에는 클래스, 메서드, 필드, 코드의 attibutes 속성에 부가 정보를 전달했지만 이젠 별로 쓸 일 없다.
- 컴파일된 클래스는 package나 import 섹션이 없다. 모든 타입 이름이 전체 경로를 가지고 있다.
- 컴파일된 클래스는 constatnt pool 섹션을 가지고 있다.

internal name
- 클래스 또는 인터페이스 타입의 이름
- String -> java/lang/String

Type descriptor
- 자바 타입 서술자

boolean->  Z
char-> C
byte-> B
short-> S
int-> I
float-> F
long-> J
double-> D
Object-> Ljava/lang/Object;
int[]-> [I
Object[][]-> [[Ljava/lang/Object;

Method descriptor
- 메서드 서술자

void m(int I, float F) -> (IF)V
int m(Object o) -> (Ljava/lang/Object;)I
int[] m(int i, String s) -> (ILjava/lang/String;)[I
Object m(int[] i) -> ([I)Ljava/lang/Object;








저작자 표시
신고
top

TAG ASM

KSUG 제10회 한국 스프링 사용자 모임 세미나





저도 발표자로 참석합니다.
봄싹에서 사용한 스프링 코드를 보여드릴 예정입니다.
설명만 하면 지겨우실테니,  스프링을 사용하여 어떤 간단한 서비스를 구현하는 모습도 보여드릴까 합니다.
많이 참석해 주세요~
저작자 표시
신고
top


EJ2E Item 24. unchecked 경고를 제거하자

Java : 2010.03.29 16:16


참조: Effective Java 2nd Edition. Item 24: Eliminate unchecked warnings

Set<Lark> exaltation = new HashSet();

다음과 같은 코드는 컴파일 시 unchecked 경고를 발생시킨다. 

Set<Lark> exaltation = new HashSet<Lark>();

이렇게 수정하면 경고가 사라진다. 가능한 모든 unchecked 경고를 없애자. 모든 경고를 제거했다면 코드의 타입 안전성을 확보하는 것이다. 즉 실행시에 ClassCastException을 보지 않을 것이다.

만약 경고를 없앨 수 없지만 타입 안전성을 확신한다면 @SupressWarnings("unchecked") 애노테이션을 사용하여 경고를 무시할 수 있다.

SupressWarning 애노테이션을 사용할 때는 항상 최소 단위에 적용하도록 하자. 만약 클래스위에 붙여버리면 @_@.. 메서드에 붙여도 @_@. 메서드 내부에 코드가 많다면 그 중 어디선가 또 다른 타입 안전성 문제가 발생할 수 있다. 메서드 내부에 변수를 만들고 해당 변수에다가 붙이자. 단 한 줄짜리 메서드라면 뭐.. 

SupressWarning 애노테이션을 사용할 때마다 왜 타입 안전성이 보장되는지 주석을 달아두도록 하자. 그래야 다른 사람들도 이해할 수 있다.

요약: 경고는 중요하다. 무시하지 말자. 모든 unchecked 경고는 잠재적으로 런타임시의 ClassCastException에 해당한다. 이 경고들을 없애는데 주력하자. 만약 없앨 수 없지만 타입 안전성을 확신한다면 가장 최소한의 규모에 @SuppressWarning("unchecked") 애노테이션을 붙이자. 그리고 그렇게 결정한 이유를 주석으로 남기자.
 
저작자 표시
신고
top


[ActionScript 3.0] 클래스

Adobe : 2010.03.29 15:55


public class Shape
{
    var visible:Boolean = true;
}

true 값을 가지고 있는 visible이라는 Boolean 타입 변수를 가지고 있는 Shape 클래스.

// ActionScript 2.0
class flash.display.BitmapData {}

// ActionScript 3.0
package flash.display
{
public class BitmapData {}
}

3.0 부터는 package문을 사용하기 때문에 클래스 이름에 패키지 이름을 선언할 필요 없다.

클래스 속성(class attributes)

dynamic: 실행 중에 인스턴스에 추가적인 속성을 추가할 수 있다.
final: 다른 클래스가 확장할 수 없다.
internal(default): 같은 패키지에서만 참조할 수 있다.
public: 모든 곳에서 참조할 수 있다.

생략하면 internal 사용.

클래스 본문

변수, 상수, 메서드 등을 정의할 수 있다. 

function hello():String
{
    trace("hola");
}
class SampleClass
{
    hello();
    trace("class created");
}

3.0 부터는 명령문도 포함시킬 수 있다.

class StaticTest
{
static var message:String = "static variable";
var message:String = "instance variable";
}
// In your script
var myST:StaticTest = new StaticTest();
trace(StaticTest.message); // output: static variable
trace(myST.message); // output: instance variable

3.0 부터는 static 변수와 인스턴스 변수 이름가 같은 이름을 사용할 수 있다.

클래스 프로퍼티 속성(class property attributes)

클래스의 프로퍼티들이 가질 수 있는 속성

internal(default): 같은 패키지에서만 참조 가능
private: 같은 클래스 내부에에서만 참조 가능
protected: 같은 클래스와 상속 받는 클래스에서 참조 가능.
public: 어디서나 참조 가능
static: 해당 프로퍼티가 클래스에 속한다는 것을 명시.
사용자 정의 네임스페이스: 사용자가 정의한 커스텀 네임스페이스

2.0에서는 private으로 해놔도 실행 중에 []를 사용해서 접근할 수 있는데. 3.0부터는 안 됨. 대신 클래스가 dynamic으로 선언되어 있으면 접근은 되지만 undefined가 반한됨.

static 특성

상송되지 않음. 
클래스 범위에 속함. 
인스턴스가 아니라 클래스 이름으로 접근해야 함.

사용자 정의 네임스페이스

패스 나중에 자세히

변수

var 또는 const를 사용하여 선언할 수 있다.
const 키워드로 선언한 것을 상수라고 한다.

정적 변수

class StaticVars
{
public static var totalCount:int = 0;
public static const MAX_NUM:uint = 16;
}

var또는 const와 static을 추가하여 선언한다.
클래스의 인스턴스가 아니라 클래스에 연결된다.
외부에서는 반드시 클래스 이름으로 접근해야 한다. 인스턴스로 접근하면 에러 난다.
선언할 때 초기화 해야한다. 안그러면 에러난다.

인스턴스 변수

static 키워드 없이 var 또는 const로 선언한 것.
인스턴스 마다 고유한 값을 가진다.
하위 클래스에서 재정의 할 수 없다. getter나 setter를 재정의하면 된다.

메서드

클래스 정의에 포함되는 함수.
function이라는 키워드로 정의한다.

함수 명령문으로 함수 정의
public function sampleFunction():String {}

함수 표현식으로 함수 정의
public var sampleFunction:Function = function () {}

함수 명령문 사용하는 이유
- 간결하고 쉽게 읽을 수 있다.
- override나 final 키워드 사용가능

생성자 메서드

클래스 이름과 같은 이름을 가진 함수
new 키워드로 해당 클래스의 인스턴스를 만들 떄 사용한다.

class Example
{
public var status:String;
public function Example()
{
status = "initialized";
}
}
var myExample:Example = new Example();
trace(myExample.status); // output: initialized

생성자에는 public만 사용가능. 생략하면 기본으로 public. 사용자 정의 네임스페이스도 사용 못함.
super()를 사용하여 상위 클래스의 생성자를 명시적으로 호출 가능.
super()를 명시적으로 호출하지 않으면 자동으로 제일 먼저 호출해줌.
return 문을 사용할 수는 없지만 값을 반환하지는 못한다.

정적 메서드

static 키워드로 선언한 메서드.
인스턴스가 아니라 클래스에 연결된다.

인스턴스 메서드

static 키워드 없이 선언한 메서드

getter와 settter 메서드

class GetSet
{
    private var privateProperty:String;
    public function get publicAccess():String
    {
        return privateProperty;
    }
    public function set publicAccess(setValue:String):void
    {
        privateProperty = setValue;
    }
}

getXXX, setXXX 라는 이름을 사용하지 않아도 된다.

var myGetSet:GetSet = new GetSet();
trace(myGetSet.publicAccess); // output: null
myGetSet.publicAccess = "hello";
trace(myGetSet.publicAccess); // output: hello

이렇게 사용할 수 있다.

바운드 메서드(메서드 클로저)

함수의 인자로 전달되는 메서드
함수의 반환값으로 전달되는 메서드
바운드 메서드 내부에서 this는 해당 메서드를 구현하는 인스턴스에 연결된다.
함수 클로저의 this는 호출 시점에 함수와 연결된 객체를 가리킨다. 이게 차이점이다.

class ThisTest {
    private var num:Number=3;

    function foo():void // bound method defined
    {
        trace("foo's this: " + this);
        trace("num: " + num);
    }

    function bar():Function
    {
        return foo; // bound method returned
    }
}

var myTest:ThisTest = new ThisTest();
var myFunc:Function = myTest.bar();
trace(this); 
// output: [object global]
myFunc();
/* output: 
foo's this: [object ThisTest]
output: num: 3 */

myFunc()는 바운드 메서드라 this가 여전히 자신이 속한 원래 인스턴스를 가리키고 있다.

클래스와 enum

enum을 지원하는게 딱히 없음.
그냥 java 5 이전처럼 static const 사용해서 정의.

public final class Day
{
public static const MONDAY:Day = new Day();
public static const TUESDAY:Day = new Day();
public static const WEDNESDAY:Day = new Day();
public static const THURSDAY:Day = new Day();
public static const FRIDAY:Day = new Day();
public static const SATURDAY:Day = new Day();
public static const SUNDAY:Day = new Day();
}

더보기

저작자 표시
신고
top

Adobe : 2010.03.29 15:55 Trackback. : Comment.

EJ2E Item 23. 새로 작성하는 코드에서는 raw type 쓰지 말자

Java : 2010.03.26 17:44


참조: Effective Java 2nd Edition. Item 23:Don't use raw types in new code

먼저 용어 정리

class List<E> {
...
}

List<String> stList = ~~~~;

이런 코드가 있을 때...
- List<E>: Generic type
- List<String>: Parameterized type
- E: Formal type parameter
- String: Actual type parameter
- List: Raw type (제목에 쓴게 이거임)
- List<?>: Unbounded wildcard type
- 나머진 패스.

Generic 장점
- 컴파일 시에 타입 안전성을 보장한다.
- 부가적인 장점으로 컬렉션에 들어있는 것을 꺼낼 때 캐스팅할 필요가 없다.

그럼 Generic을 꼭 쓰게 하지 왜 Raw Type으로 쓸 수 있게 해뒀냐?
- 호환성 때문에. 이미 1.5전 버전 기준으로 만들어 놓은 코드가 많으니까.

Generic 특성
- List<String>은 List의 하위 타입이지만 List<Object>의 하위 타입은 아니다.
- 다음 item에서 더 자세히 설명 나옴.


Raw Type을 사용했을 때 발생할 수 있는 문제

    public static void main(String[] args) {
        List<String> strings = new ArrayList<String>();
        unsafeAdd(strings, new Integer(42));
        String s = strings.get(0); // Compiler-generated cast
    }

    private static void unsafeAdd(List list, Object o) {
        list.add(o);
    }

굵은 글씨에서 타입을 명시하지 않았기 때문에
list.add(o); 여기서 일단 컴파일 경고가 발생하지만 일단 무시하고 실행할 수는 있다 막상 실행하면 ClassCastException 발생한다. 하지만 만약 저기서 List<String>이라고 타입을 명시했다면 애초에 컴파일도 못했을 것이다.

어떤 종류가 들어있는지 모르는 컬렉션을 처리하는 메서드에서는 unbounded wildcard type을 사용하자.
   
    //위험
    static int numElementsInCommon(Set s1, Set s2) {
        int result = 0;
        for (Object o1 : s1)
            if (s2.contains(o1))
                result++;
        return result;
    }

    //안전
    static int numElementsInCommon(Set<?> s1, Set<?> s2) {
        int result = 0;
        for (Object o1 : s1)
            if (s2.contains(o1))
                result++;
        return result;
    }

위에껀 왜 위험하고 아래껀 왜 안전할까? 안전한 녀석에는 무언가 추가할 수가 없다. 위험한 녀석에는 아무 객체나 넣을 수 있지만 unbounded wildcard type을 사용한 경우에는 컬렉션에 아무것도 추가할 수 없다. 컴파일 에러다.

왜? Actual type parameter가 뭔지 모르는데 뭘 집어 넣을 수 있을까.. 생각해보면 당연하다.

Type parameter를 사용할 수 없는 경우
- List<String>.class
- instanceof


저작자 표시
신고
top

TAG Generic, Java
Java : 2010.03.26 17:44 Trackback. : Comment.

[Spring 3.0 & Atlassian] RestTemplate으로 Confluence의 Space 목록 가져오기

모하니?/Coding : 2010.03.26 00:51


public class ConfluenceRestTest {

    public static final String SPACE_LIST_URL = "http://dev.springsprout.org/wiki/rest/prototype/1/space?os_authType=basic";

    /**
     */
    @Test
    public void testGetWikiSpaceList() {
        RestTemplate restTemplate = new RestTemplate();
        String result = restTemplate.execute(SPACE_LIST_URL, HttpMethod.GET,
                new JsonRestRequestCallBack(),
                new BodyToStringRestResponseExtractor());
        System.out.println(result);
    }

    static class JsonRestRequestCallBack implements RequestCallback {
        public void doWithRequest(ClientHttpRequest clientHttpRequest) throws IOException {
            HttpHeaders headers = clientHttpRequest.getHeaders();
            headers.add("Authorization", "Basic XXXXXXXX(base64 encoded username:passwd)");
            headers.add("Accept", "application/json");
        }
    }

    static class BodyToStringRestResponseExtractor implements ResponseExtractor<String> {

        public String extractData(ClientHttpResponse clientHttpResponse) throws IOException {
            return convertStreamToString(clientHttpResponse.getBody());
        }
        ....
    }

}

봄싹 위키를 컨플루언스로 바꾸었기 때문에 기존 메뉴를 컨플루언스에 있는 정보를 끌어다 보여주는 것으로 바꾸려고 합니다. 그래서 파일럿(?) 겸사해서 코딩을 해봤는데 잘 됐네요.

{"expand":"space","space":[{"name":"개발툴","key":"DTL","link":{"rel":"self","href":"http://dev.springsprout.org/wiki/rest/prototype/1/space/DTL"},"description":"개발툴 관련 내용을 정리하는 공간입니다."},{"name":"베타리딩","key":"BTR","link":{"rel":"self","href":"http://dev.springsprout.org/wiki/rest/prototype/1/space/BTR"},"description":"봄싹 베타리딩 관련 공간입니다."},{"name":"봄싹 Modules","key":"BSM","link":{"rel":"self","href":"http://dev.springsprout.org/wiki/rest/prototype/1/space/BSM"},"description":"봄싹 Module과 관련된 정보를 정리하는 공간입니다."},{"name":"봄싹 개발","key":"SSD","link":{"rel":"self","href":"http://dev.springsprout.org/wiki/rest/prototype/1/space/SSD"},"description":"봄싹에서 진행하는 개발과 관련된 정보를 정리하는 공간입니다."},{"name":"스프링 3.0 레퍼런스 번역","key":"SRK","link":{"rel":"self","href":"http://dev.springsprout.org/wiki/rest/prototype/1/space/SRK"},"description":"스프링 3.0 레퍼런스를 번역하는 공간입니다."},{"name":"스프링 시큐리티 3.0 레퍼런스 번역","key":"springsecurity","link":{"rel":"self","href":"http://dev.springsprout.org/wiki/rest/prototype/1/space/springsecurity"},"description":""},{"name":"스프링소스 블로그 번역","key":"SBK","link":{"rel":"self","href":"http://dev.springsprout.org/wiki/rest/prototype/1/space/SBK"},"description":"스프링소스 블로그를 번역하는 공간입니다."},{"name":"연습장","key":"ds","link":{"rel":"self","href":"http://dev.springsprout.org/wiki/rest/prototype/1/space/ds"},"description":"봄싹 위키 사용법을 익히는 공간입니다."},{"name":"자바","key":"JAVA","link":{"rel":"self","href":"http://dev.springsprout.org/wiki/rest/prototype/1/space/JAVA"},"description":"자바 학습 공간입니다."}]}

막상 해보고 나니 고민이 생기네요. "저 작업을 뷰에서 할 것이냐 서버단에서 할 것이냐?"

뷰에서 하면 저 페이지로 이동 할 때 마다 요청을 보낼테니 Confluence 응답이 늦어지면 문제고 Confluence 입장에서 부담이 될 수도 있고, Confluence가 죽었을땐 위키 페이지까지 마비가 되지만 구현하는 입장에선 뷰 코드만 지지고 볶으면 되니까 간단할 것 같은데..

서버단에서 하면 DB에 저장해두거나 캐싱을 해둘 수 있으니 Confluence의 부담도 덜고 Confluence가 죽어도 위키 정보는 보여줄 수 있지만 구현하면서 이것 저것 신경쓸게 많아지네;; 정보를 읽어올 주기, 배치로 돌릴 것이냐? JSON말고 XML로 받아온다음 OXM을 이용해서 XML->Object로 바로 변환해버리는 ResponseExtracter를 만들까나.. 캐싱은 이미 Ehcache를 하이버 땜시 쓰고 있으니 캐싱도 해보고?

흠.. 서버단에서 할까나 어쩔까나.. Confluence가 죽어있으면 어차피 봄싹 Wiki에서 목록 본다음에 링크 클릭해도 이동을 못하니까 그냥.. 뷰에서 읽을까나.. +_+ 그럼 나 머하러 RestTemplate 사용한거지.. @_@.. 기왕에 썼으니까 OXM도 적용해서 서버 단에서 해버렷?ㅋ 
저작자 표시
신고
top


[Flex] 플래시 개발 시작

Adobe : 2010.03.24 15:30


<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
    <mx:Script><![CDATA[
        import mx.controls.Alert;

        protected function hello():void
        {
            Alert.show("hi! " + nameTextInput.text);
        }
        ]]></mx:Script>
    <mx:TextInput id="nameTextInput" />
    <mx:Button id="helloButton" click="hello()" label="Say Hello"/>
</mx:Application>

이전에 인텔리J에서 만들어 둔 hello world 예제를 약간 수정해서 만들어 봤다. 막상 만들려고 하니 액션 스크립트와 mxml 컴포넌트가 궁금했고 그래서 API와 문법을 참조할 수 있는 문서들을 모아두었다. 

이걸로 봄싹 플레이어나 만들어벌까. 줄여서 '봄플'인가. '봄TV'라고 할까나.




저작자 표시
신고
top

Adobe : 2010.03.24 15:30 Trackback. : Comment.

[Flex] 플렉스의 북치기 박치기, ActionScript와 MXML

Adobe : 2010.03.24 14:55


플렉스를 해보려고 삽질(?) 중인데 일단 두 가지가 걸린다. mxml과 actionscript. 
이 두 가지만 알면 대충 만들 수 있을 것 같다.

일단 mxml 파일에서 어떤 컴포넌트가 있고 각각의 속성들이 무엇인지 무엇인지는 이전에 올렸던 글에서 확인할 수 있다.

http://whiteship.me/2546

mxml 파일은 거의 html이랑 비슷하게 구성 요소를 그리는데 필요한 것 같고, 그 구성 요소를 가지고 놀 때 실행할 기능들은 actionscript로 만들어야겠다. 그래서 actionscript 문법과 API를 파악하면 될 것 같다.

API
http://livedocs.adobe.com/flex/3/langref/

레퍼런스
http://livedocs.adobe.com/flex/3/progAS_flex3.pdf
http://flexdocs.kr/download/flash_as3_programming.pdf




저작자 표시
신고
top

Adobe : 2010.03.24 14:55 Trackback. : Comment.

[구글 크롬] 스프링 소스 한글 사이트

Good Tools : 2010.03.24 11:23



ㅋㅋㅋㅋ그럴싸 하지 않나요. 구글 크롬 최신버전은 사이트를 통째로 번역해주네요. 
구글 기능들을 브라우저에 마구 접목 시키는군요. 스프링 레퍼런스를 봐볼까요.


흠.. 의존성 주입이 아니라 '종속성 주입', 제어의 역전 대신. '반전'을 사용하고 있군요. '애플 리케이션'이라..ㅋ
저작자 표시
신고
top


EJ2E Item 22. nonstatic 보다는 static 멤버 클래스를 선호하라

Java : 2010.03.24 11:16


참조: Effective Java 2nd Edition. Item 22: Favor static member class over nonstatic

중첩 클래스(nested class)에는 네 종류가 있다.
- static member class
- nonstatic member class
- anonymous class
- local class

Static Member Class

public class SpringSprout {
   static class Whiteship {
        private String name; 
        ...
    }
}

- 다른 클래스 내부에 선언된 클래스로 가장 많이 사용된다.
- 자신을 가지고 있는 클래스의 모든 멤버에 접근할 수 있다.
- 보통 helper 클래스로 사용된다.
- enclosing instance 유무와 상관없이 독립적으로 사용될 수 있다.

Nonstatic Member Class

public class SpringSprout {
   class Whiteship {
        private String name; 
        ...
    }
}

- 암묵적으로 자신을 감싸는 클래스의 인스턴스(enclosing instance)와 연관을 맺는다.
- nontatic member class와 enclosing instance와의 연관 관계는 enclosing instance에서 nonstatic member class의 객체를 만들 때 생긴다.
- 이 연관 관계로 인해 nonstatic member class 인스턴에 필요한 공간과 생성 시간이 소비된다.
- 보통 Adapter를 정의할 때 사용된다.
- Map의 keySet, entrySet, values 메서드가 반환하는 콜렉션 뷰들과 Set, List에서 Iterator를 구현할 때 nonstatic member class를 사용한다.

AbstractList 코드

    public Iterator<E> iterator() {
return new Itr();
    }

    private class Itr implements Iterator<E> {
    ...
    }

=> enclosing instance에 접근할 필요가 없는 member class는 static으로 선언하자.

Anonymous Class
- 이름도 없고 enclosing class의 멤버도 아니다.
- 표현식을 사용할 수 있는 곳이라면 어디서든 익명 클래스를 사용할 수 있다.
- nonstatic 문맥에서만 enclosing instance를 가진다.
- static 문맥에서는 static 멤버를 가질 수 없다.
- 주로 함수 객체를 만들 때 사용한다.

Local Class
- 익명 클래스랑 거의 같은데 클래스 이름 줄 수 있고 인스턴스 여러 번 만들어 사용할 수 있다.
- 걍 생략;

요약
- 중첩 클래스가 특정 메서드 외부에서도 사용될 피룡가 있거나 메서드 내부에 있기에는 너무 길다면 멤버 클래스로 만든다.
- 만약 멤버 클래스의 인스턴스가 enclosing instance를 참조할 필요가 있다면 nonstatic으로 만든다. 그렇지 않으면 static으로 만든다.
- 클래스가 메서드 내부에 속해 있으며 딱 한번만 사용할거면 익명 클래스로 만들고, 그렇지 않으면 로컬 클래스로 만든다.
저작자 표시
신고
top

Java : 2010.03.24 11:16 Trackback. : Comment.

EJ2E Item 21. 전략을 표현할 때는 함수 객체를 사용하라

Java : 2010.03.24 10:30


참조: Effective Java 2nd Edition. Item 20: Use function objects to represent strategies

자바는 함수 포인터를 제공하지 않는다. 하지만 객체 레퍼러스를 함수 객체와 비슷하게 사용할 수 있다. 

메서드를 호출하면 해당 객체에 대한 기능을 수행하는 것이고 함수 객체는 다른 객체들에서 해당 기능을 수행하게 해준다.

(사족. 메서드는 특정 객체에 종속적인 것이고, 함수 객체는 독립적으로 여러 다른 객체에서 사용할 수 있는 것으로 구분할 수 있겠다.)

함수 객체들은 Concrete Stratege 역할을 하기 때문에 보통 상태 정보가 없고(stateless), 어디서나 동일하게 동작하기 때문에 불필요한 객체 생성 비용을 줄이기 위해 싱글톤으로 사용되기 좋다.

(사족. 스프링을 사용한다면 굳이 싱글톤 구현하느라 애쓰지 말고 그냥 싱글톤 스코프 빈으로 등록하면 되겠다.)

대표적인 예로 Comparator 인터페이스 구현체들이 있다.

// Strategy interface
public interface Comparator<T> {
    public int compare(T t1, T t2);
}

이 구현체는 종종 익명 클래스로 사용되기도 한다. 하지만 익명 클래스를 사용하면 매번 새로운 인스턴스를 만들게 된다. 반복해서 사용할 꺼라면 차라리 함수 객체를 priate static final field에 저장해 놓고 재사용하는 것이 좋겠다. 그렇게 하면 명시적인 이름을 줄수도 있다.

전략이 여러개를 제공하는 "Host class"에서 외부로 공개할( public static final) strategy의 반환 타입은 Strategey 인터페이스 타입이어야 하고 구체적인 Stategy 구현체들은 굳이 public 일 필요가 없다. 

요약
- 전략 패턴을 구현할 때 함수 객체를 주로 사용한다.
- 자바에서 이 패턴을 구현할 때는 전략 인터페이스를 만들고, 각 전략들은 그 인터페이스의 구현체로 만든다.
- 만약 어떤 전략을 한번만 사용할 거면 익명 클래스로 만들고 반복해서 사용할거면 prviate static member 클래스로 만든 다음 전략 인터페이스의 public sttic final field로 제공해준다.
- 대표적인 클래스 String


저작자 표시
신고
top

Java : 2010.03.24 10:30 Trackback. : Comment.

[Spring Security] Method Security Test

모하니?/Coding : 2010.03.19 21:15


    @PreAuthorize("hasRole('ROLE_MEMBER')")
    public boolean write(String contents) {
        while (graffitiRepository.getTotalRowCount() >= GRAFFITI_LIMIT_COUNT) {
            graffitiRepository.deleteFirstGraffiti();
        }
        Graffiti graffiti = new Graffiti(contents, securityService.getCurrentMember());
        graffitiRepository.add(graffiti);
        return true;
    }

위 코드는 봄싹 낙서장 서비스의 코드이다. 봄싹 메인 화면에 있는 낙서장은 최대 100개를 유지하며 ROLE_MEMBER 권한을 가지고 있는 사용자(이메일 인증 절차를 거친 기본 회원)라면 누구나 낙서를 추가할 수 있다.

이 코드의 내용 대부분은 단위 테스트로 커버가 가능하다. 하지만 스프링 시큐리티 애노테이션이 제대로 동작하는지는 어떻게 테스트 할 것인가?

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"/testContext.xml", "/testContext-security.xml"})
@Transactional
public class MethodSecurityTest extends DBUnitSupport{

    @Autowired GraffitiService graffitiService;
    @Autowired GraffitiRepository graffitiRepository;

    @Before
    public void login() throws Exception {
        insertXmlData("testData.xml");
        SecurityContext securityContext = new SecurityContextImpl();
        Authentication authentication = new UsernamePasswordAuthenticationToken("whiteship@email.com", "passwd");
        securityContext.setAuthentication(authentication);
        SecurityContextHolder.setContext(securityContext);
    }

    @Test
    public void methodAuth(){
        assertThat(graffitiRepository.getAll().size(), is(0));
        graffitiService.write("hi");
        assertThat(graffitiRepository.getAll().size(), is(1));
    }

    @After
    public void after(){
        SecurityContextHolder.clearContext();
    }

}


이런식으로 테스트 할 수 있다.

1. 시큐리티 설정 파일을 테스트에서 만들 AC(ApplicationContext)용 설정에 추가한다.
2. DB에 xml로 작성한 가짜 사용자, 권한 정보를 넣어야 하니까 DBUnit을 편하게 사용할 수 있게 해주는 클래스를 이용한다.
3. 테스트 실행하기 전에 로그인 한다. (SecurityContextHolder에 SecurityContext를 넣는다.)
4. 테스트 실행한 뒤에 로그오프 한다. (SecurityContextHolder를 비워버린다.)


저작자 표시
신고
top


나는 null 중대 소속이다.. 너는 어디냐..




어느덧 5년차... 하지만 여전히 귀찮은...

비도 오고.. 젠장
날씨도 추운데.. 젠장
시간 아깝게 젠장
저작자 표시
신고
top


봄싹 개발자 모집합니다. (완료됨)

모하니?/Coding : 2010.03.17 11:00


모집이 완료되었습니다.
참가 신청 해주셔서 감사합니다.
- updated March, 22th. 2010
================================
봄싹 공통 개발팀: http://dev.springsprout.org/wiki/pages/viewpage.action?pageId=1114148
봄싹 세미나 개발팀: http://dev.springsprout.org/wiki/pages/viewpage.action?pageId=1114158

두 팀에서 개발자를 모집하고 있습니다. 이건 일자리가 아니라 취미 개발입니다. 개발 기간은 4~5주 정도로 잡고 있습니다. 자세한건 팀을 모집한 뒤에 논의하겠습니다.

가급적이면 서울 중심지에 사시는 대학생 또는 스프링과 하이버네이트에 관심있는 개발자 분들이 참여해주시면 좋겠습니다.1주일에 한번 정도는 오프라인으로 만나서 할거라서 서울 중심지에 사시는 분들이 이동하기 편할 것 같구요. 프로젝트가 스프링, 하이버네이트로 되어있어서 경험이 없지만 이 둘을 사용하는 프로젝트를 하고 싶으신 분들이 참여해주시기 바랍니다. 물론 경험자여도 상관없습니다. 같이 플젝을 해보고 싶으신 분들이라면 누구나 참여 가능합니다.

한 프로젝트당 최대 수용 인원은 4명 ~ 5명이구요. 제 메일(이 블로그 제일 하단에 있습니다.)로 신청해주시기 바랍니다. 메일을 보내주실 때 다음 유의사항을 지켜주세요.

- 제목 머릿말에 [봄싹]을 넣어주세요. 예) [봄싹] 개발 참가 신청합니다.
- 이름 또는 아이디 (뭐라고 불러드릴까요??)
현재 하시는 일 (무슨 플젝에 참여했었고 어디서 몇년째 근무중인게 궁금한게 아니랍니다. 학생일 경우에는어느 대학 무슨 학부 몇 학년인지 궁금한게 아니랍니다. 그런 피상적인 것들이 아니라 실제로 하고 계신 공부나 일을 적어주세요.)
- 참여하고 싶은 팀 (둘 중에 하나만 선택해 주세요.)
- 토요일 오전 모임 참석 가능 여부(중요합니다. 주로 신촌에서 토요일 오전에 모입니다.)
- 기타 (블로그, 하고싶은 이야기, 기타 덩덩덩)

기다리겠습니다.
저작자 표시
신고
top


[스프링 3.0] @Value 이용해서 기본값 설정하기

Spring/3.0 : 2010.03.16 18:04


@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class ValueTest {

    @Autowired Whiteship whiteship;

    @Test
    public void defaultName(){
        assertThat(whiteship.getName(), is("keesun"));
    }

}

@Component
public class Whiteship {

    @Value("keesun")
    private String name;

    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
}

악.. 저녁 약속이 있어서 길게 정리는 못하겠네요. 


이것도 DI용 애노테이션이라는거..


저작자 표시
신고
top


[톰캣] 톰켓 7의 메모리 누수 방지

Good Tools : 2010.03.15 17:04


http://java.dzone.com/articles/memory-leak-protection-tomcat

웹 애플리케이션을 릴로딩 할 때 어떤 메모리 누수가 발생하며 그 징후가 어떤지...

PermGen (Permanent Generation) 메모리 영역에 클래스들이 제대로 GC (Gabage collect)에 의해 회수되지 않아서 OOME (OutOfMemoryObejct)가 발생하게 된다.

톰캣은 릴로딩 할 때 기존의 WebApp CL을 버리고 새로운 WebApp CL을 만들어서 애플리케이션을 다시 로딩하는데 만약 이때 누군가 WebApp CL에 대한 레퍼런스를 가지고 있다면 GC 대상이 되지 않고 그렇게 되면 WebApp CL이 로딩한 클래스들이 PermGen에서 사라지지 않기 때문에 계속해서 릴로딩 하다가 이 영역이 꽉차게 되고 그럼 OOME가 발생합.

어떤 웹 애플리케이션, 라이브러리, JVM 코드가 메모리 누수를 일으킬 수 있는가..

1. WebApp CL에서 만든 어떤 객체를 다른 클래스로더(예. BootStrap CL)가 로딩한 클래스에 등록한다. - 여기까진 괜찮다.
2. 그 뒤 등록한 객체를 언젠가 꺼내서 제대로 반환하지 않는다. - 이게 문제의 시작이다.
3. 톰캣이 릴로딩을 시도한다. - 기존 WebApp CL을 버리고 GC가 처리해 주겠거니 기대한다. 하지만 그렇게 되지 않는다.

BootStrap CL에는 아직 WebApp CL에서 로딩한 클래스의 인스턴스를 들고 있으며 해당 인스턴스는 WebApp CL을 참조하고 있기 때문에 WebApp CL과 그 CL이 로딩한 클래스는 제대로 GC가 자원을 반환하지 못하고 PermGen에 쌓이고 결국 언젠가는 OOME를 보게 된다.

Application or library code that can trigger this situation include:
  • JDBC driver registration
  • Some logging frameworks
  • Storing objects in ThreadLocals and not removing them
  • Starting threads and not stopping them

There are also a number of places that using the standard Java API can trigger a similar issue. These include:
  • Using the javax.imageio API (the Google Web Toolkit can trigger this)
  • Using java.beans.Introspector.flushCaches() (Tomcat does this to prevent memory leaks caused by this caching)
  • Using XML parsing (the root cause is unknown due to a bug in the JRE)
  • Using RMI (somewhat ironically, causes a leak related to the garbage collector)
  • Reading resources from JAR files

ThreadLocal에 객체를 등록한 뒤 제거하지 않은 경우를 생각해보자.


1. ThreadLocal은 BootStrap+Extention 클래스로더가 가지고 있을테고 만약에 웹 애플리케이션의 클래스패스에 있는 Whiteship의 객체를 만든 다음 ThreadLocal에 넣어뒀다.

2. remove()를 하지 않았다.

3. 톰캣을 릴로딩 한다.

하지만.. ThreadLocal에 Whiteship이 담겨 있고 Whiteship은 WebApp CL을 참조(Whiteship.class.getClassLoader()로 접근할 수 있다.)하고 있기 때문에 WebApp CL이 제대로 GC에 의해 처리되지 않는다. 

웹 애플리케이션을 릴로딩 할 때 Whiteship을 ThreadLocal에서 제거해주면 문제는 해결된다.

톰캣 새 버전은 이 문제를 어떻게 해결했는가?

애플리케이션과 라이브러리 코드 문제 해결 방법
- 자원을 반납해주고 개발자에게 경고해준다.

자바 라이브러리 관련 문제 해결 방법
- 웹 애플리케이션이 안전하게 호출할 수 있도록 톰캣 코어에서 미리 호출한다.


저작자 표시
신고
top


Visitor 패턴

Design Pattern : 2010.03.12 21:26


http://c2.com/cgi/wiki?VisitorPattern
http://en.wikipedia.org/wiki/Visitor_pattern

Composition 패턴을 구현한 구조물에 어떤 기능을 수행하고 싶을 때 해당 기능을 별도의 인터페이스로 분리해낸것이 Visitor 패턴. 이렇게 하여 기존의 구조물에 해당하는 코드는 변경하지 않고 그것들을 사용하는 새로운 기능을 새로 추가할 수 있으니 Open-Closed 원칙을 달성했다고 볼 수 있다.


생각해보니 이런 패턴이 적용된 것들이 꽤 많으며, 적용할 수 있는 곳도 많은 것 같다. 윈도우에서 폴더나 파일을 우클릭한 다음에 저장, 복사, 이동 등을 하는 것들이 모두 이 패턴을 이용해서 구현하지 않았을까..

게다가 ASM은 이 패턴을 사용하여 BCEL이나 SERP 같은 코드 조작 툴보다 훨씬 뛰어난 성능의 라이브러리를 제공하고 있다.

그렇다 사실은 ASM 공부하다가 맨 첨에 등장하는게 이 패턴이라 잠깐 보고 넘어가기로 한 것이다. ㅋㅋ

흠... 하지만 ASM이 조작하는 클래스 처럼 클래스 아래 변수 쫙! 메서드 쫙! 변수와 메서드에는 어트리뷰터가 쫙! 있는 단순구조가 아니라, 파일이나 폴더 처럼 파일 안에 파일 쫙! 그 안에 또 파일 쫙! 이런 트리구조라면 Visitor 패턴을 개조한 Hierarchical Visitor 패턴이 더 효율적이고 유용하겠다.

http://c2.com/cgi/wiki?HierarchicalVisitorPattern

자 어서 ASM도 보도록 하자. ㄱㄱㄱㄱ
저작자 표시
신고

'Design Pattern' 카테고리의 다른 글

[OOAD] 객체지향 원칙 1. SRP  (2) 2010.05.07
[Tell, Don't Ask] 물어보지 말고 시켜라  (8) 2010.04.07
Visitor 패턴  (2) 2010.03.12
Holub on Pattern 좋은데요~  (4) 2008.12.07
프로토타입 패턴(Prototype Pattern)  (2) 2008.10.31
데코레이터(Decorator) 패턴  (0) 2008.10.01
프록시(Proxy) 패턴  (0) 2008.09.26
JUnit 공부하자.  (0) 2008.09.01
H.F.OOAD 5장 문제  (0) 2007.11.20
상위 클래스 보다는 인터페이스를...  (4) 2007.08.31
Singletons and lazy loading  (2) 2007.01.27
top


[Agile 번역] 어떻게 하면 번역을 기민하게 할 수 있을까?

모하니?/Thinking : 2010.03.12 11:09


번역은 쉽지 않다. 하지만 나름 재미는 있다. 돈은 되지 않지만, 하고 나면 뿌듯하다. 책으로 치면 지금하고 있는 책까지 두 권째다. 난 번역을 잘하지 못한다. 한글 실력이 엉망이고 책읽기를 별로 좋아하지 않아서 인지 문학 도서에 비하면 비교적 쉬운 영어를 사용하는 서적들 임에도 불구하고 한글 문장으로 옮기는 일이 쉽지 않다. 그래도 최소한 내가 다시 읽었을 때 이해할 수 있을 정도의 수준 만큼은 달성하고자 노력한다.

그렇게 어렵사리 번역을 하고나면 사실 다른 사람의 시각에서 읽었을 때 어떠한지 궁금하다. 그래서 베타리딩을 한다. 베타리딩을 하면서 내가 미처 감지하지 못했던 어색한 부분과 이해 못할 만한 부분이 드러난다. 그 부분을 개선하는 작업은 중요하다. 내가 읽었을 때 이해할 수 있었던 건 난 원문의 내용을 읽었기 때문이다. 그래서 원문을 보지 않고도 이해할 수 있는지 알려면 반드시 베타리딩이 필요하다. 코딩으로 치면 일종의 테스트다. 그런데 베타리딩도 쉽지 않다. 하이버네이트 같은 경우 한 챕터당 워드로 100페이지 정도가 된다. 넘는 것도 있고 덜 되는 것도 있지만 대충 그렇다. 그걸 바쁜 개발자들에게 읽어달라고 부탁하기가 참 뭐하다. 그 분들이 받는 댓가라고는 책을 미리 읽어볼 수 있다는 것 정도?

그래서 이런 저런 이유로 고민 중에 번역에도 Agile 기법을 도입하면 어떨까 하는 생각이 들었다. 그래서 상상을 해봤다.

페어 번역 

두 명이 앉아서 번역을 한다. 한명은 부르고 한명은 타이핑 하거나 그 반대로 한명이 읊으면서 동시에 타이핑도 하고 옆 사람은 보고 있다가 이상한 문장이나 틀린걸 봐주면 될 것 같다. 하지만 이건 번역으로 먹고 사는 사람이 아닌 이상 힘들어 보인다. 그렇게 30분 정도 번역을 한 다음 교대한다.

이런식으로 하루에 2시간을 번역한다고 쳤을 때 두 사람은 각각 1시간 번역 1시간 리뷰를 하게 된다. 한 사람이 2시간을 번역할 때와 2사람이 각각 1시간 번역/1시간 리뷰를 했을 때의 생산량과 품질을 확인해보고 싶다.

리뷰 퍼스트 번역

타이핑을 하기 전에 먼저 말로 리뷰를 한다. 아 이 문장은 대충 요러 요러한 이야기 같네요. 이렇게 옮기겠습니다. 라고 말을 한 뒤에 타이핑을 한다. 그리고 타이핑이 끝나면 옆 사람과 리팩토링을 진행한다. 어설프거나 한글 어법에 어긋나면 교정한다. 나중에 QA 팀 겪인 출판사의 검수자가 봐주겠지만 최소한의 품질을 보장하기 위한 수단이라고 보면 되겠다. 

페어 번역을 진행할 때 타이핑만 하지 않고 리뷰를 함으로써 대화를 유도할 수 있다. 하지만 대화가 목적은 아니기에 잡담으로 새는 일이 없게 주의해야겠다. 리뷰를 최소화하고 바로 타이핑을 한다. 그 다음 리팩토링에 해당하는 작업도 반드시 단락 단위나 문자 단위로 하는게 좋겠다. 이런 과정이 없다면 페어 번역을 하는 의미가 없으니 이렇게 하지 않을꺼면 페어 번역을 하지 않는게 좋겠다.

점진적인 베타리딩

베타리더에게 전달하는 과정을 일종의 배포로 간주하고 Agile 기법인 여러번 그리고 점진적으로 배포하는 수단을 번역에도 도입하면 어떨까. 예를 들어 베타리딩 주기를 1주일로 잡고 1주일치 번역한 내용을 베타리더에게 공개한다. 그리고 피드백을 받은 다음 그 주 초에 지난 배포의 개선 작업을 한 다음에 주 중~말까지 새로운 번역 작업을 진행하는 식이다.

베타리더에게 한번에 100 페이지씩 읽어야 하는 부담을 줄일 수 있고 피드백을 초기에 받음으로써 나중에 뭉탱이로 중복적인 피드백을 받는 것을 개선할 수 있을 것 같다. 하지만 베타리더도 번역자 못지않게 기민해야 할 것이다.

다음 번역은 봄싹에서 진행할 계획인데 이 세가지를 시도해 봐야겠다. 
과연.. 어떨까.. 후훗.. 재밌을 것 같다.
저작자 표시
신고
top

TAG agile, 번역

[ClassLoader] Thread의 getContextClassLoader()

Java : 2010.03.11 17:57


pdf: http://www.theserverside.com/tt/articles/content/dm_classForname/DynLoad.pdf

먼저 간단한 퀴즈로 시작하는.. 긴 문서..


이 코드가 문제를 일으킬 수 있는 상황은 저 클래스를 자바 Extentions 폴더 (<JDK>/jre/lib/ext 또는 <JRE>/lib/ext)에 놓았을 경우입니다. 그렇지 않고 그냥 CLASSPATH에 둘었다면 별 문제가 없을 가능성이 큽니다. 대체 저런식으로 동적으로 로딩하려는 클래스들이 App CL의 CLASSLOADER에 있기 마련일 테니까요.

하지만, Extention 폴더로 이동하면 무슨일이 벌어질까요. ClassNotFoundExcepion이 발생합니다. 그 원인을 정확히 모르신다면 ClassLoader에 대한 아주 기본적인 개념이 부족한거라 봐도 무방하겠습니다.

이 글에서 그에 대한 해결책 두 개를 제공해주며, 그 중 하나가 바로 쓰레드 컨텍스트 클래스로더를 사용하는 것입니다.

http://java.sun.com/javase/6/docs/api/java/lang/Thread.html#getContextClassLoader%28%29

Returns the context ClassLoader for this Thread. The context ClassLoader is provided by the creator of the thread for use by code running in this thread when loading classes and resources. If not set, the default is the ClassLoader context of the parent Thread. The context ClassLoader of the primordial thread is typically set to the class loader used to load the application.

First, if there is a security manager, and the caller's class loader is not null and the caller's class loader is not the same as or an ancestor of the context class loader for the thread whose context class loader is being requested, then the security manager's checkPermission method is called with a RuntimePermission("getClassLoader") permission to see if it's ok to get the context ClassLoader.

별짓을 하지 않는다면 쓰레드 컨텍스트 클래스로더는 System(또는 Application) CL이 됩니다. 따라서 저 위의 코드를 자바 extentions 폴더에 넣고 실행하더라도 쓰레드 컨텍스트 클래스로더를 가져와서 loadClass를 호출하면 아무런 문제없이 동작합니다.

안그래도 대체 Thread Context ClassLoader가 왜 생긴건지 궁금했는데 이 글을 통해서 짐작할 수 있게 됐습니다. 클래스로더 계층 구조를 만들 때, 예를 들어 톰캣의 Common CL 같은 녀석은 자기 하위의 CL인 WebAPP CL에 어떤 클래스들이 들어올지 모를겁니다. 하지만 그런 상태에서 Common CL에서 WebAPP CL에서만 로딩할 수 있는 어떤 클래스를 로딩할 필요가 있다면... WebAPP CL을 직접 참조하지 않고 바로 Thread Context ClassLoader를 사용해서 로딩하도록 코딩을 하면 로딩할 수 있게 되는거죠.

흠.. 하지만 이.. 미적지근한 느낌은 뭘까요.. 왠지 상위 CL에서 하위 CL의 클래스패스에 있는걸 로딩해버리면 왠지 꼬여버릴 것만 같은 이 기분... 아.. 불안해. 왜 그럴까나..
저작자 표시
신고
top


[구글 Visualization API] Fluent Interface 적용하기

모하니?/Coding : 2010.03.11 14:18


http://martinfowler.com/bliki/FluentInterface.html

이름은 좀 멋지지만 별거 아니라는...

        Statistics statistics = new Statistics(StatisticsChartType.MOTION, "스터디 참석자 통계 그래프");
        statistics.addColumn(StatisticsColumnType.STRING, "스터디 이름");
        statistics.addColumn(StatisticsColumnType.DATE, "모임 날짜");
        statistics.addColumn(StatisticsColumnType.NUMBER, "참석자 수");
        statistics.addColumn(StatisticsColumnType.NUMBER, "모임 횟수");
        statistics.addColumn(StatisticsColumnType.NUMBER, "참석율");
        statistics.addColumn(StatisticsColumnType.STRING, "스터디 상태");
        statistics.addColumn(StatisticsColumnType.STRING, "모임명 이름");

이 코드는 얼마전에 봄싹 사이트에 Statistics 메뉴를 추가하면서 코딩한 것인데 오늘 보다가 갑자기 Fluent Interface가 떠올랐습니다. 그래서 적용해봤지요.

        Statistics statistics = new Statistics(StatisticsChartType.MOTION, "스터디 참석자 통계 그래프")
            .withColumn(StatisticsColumnType.STRING, "스터디 이름")
            .withColumn(StatisticsColumnType.DATE, "모임 날짜")
            .withColumn(StatisticsColumnType.NUMBER, "참석자 수")
            .withColumn(StatisticsColumnType.NUMBER, "모임 횟수")
            .withColumn(StatisticsColumnType.NUMBER, "참석율")
            .withColumn(StatisticsColumnType.STRING, "스터디 상태")
            .withColumn(StatisticsColumnType.STRING, "모임명 이름");

조금 fluent 하게 보이나요??ㅋ 객체를 다 만든 다음에 추가하는게 아니라 객체 만들 때 메서드를 연달아 호출하는 모양이여서 메서드 이름을 add -> with로 변경했습니다. 메서드 리턴 타입도 당연히 바꿨지요.

장점은 저렇게 조금 API가 이쁘고 사용하기 편해진다는 것이고 단점은 디버깅할 때 조금 불편할 수 있다는데.. 머.. 글쎄요. 그다지...
저작자 표시
신고
top


[거꾸로 읽는 세계사] 드레퓌스 사건을 보고서...

모하니?/Reading : 2010.03.10 10:51


요즘 역사 공부를 하는 중인데 그 두번째 책으로 선택한게 '거꾸로 읽는 세계사'다. 오래전에 쓴 책이고 2009년에 개정했다지만 역시 약간의 운동권 냄새(?)가 나긴 난다. 하지만 뭐 어떠하리.. 내가 생각할 수 있게 해주는 책이면 그만이다.

구글링 해보면 대략 나오기 때문에 자세한 사건 소개는 안하고 시간만 요약해 봅니다.

1894년 12월: 드레퓌스 죄없이 유태인이기 때문에 누명을 쓰고 무기징역 선고 받음. 소위 '악마섬'이라 불리는 곳으로 끌려감.

1896년 3월: 피카르 중령이 진짜 반역자 에스테라지 발견. 상부에 보고하지만 묵살 당함.

1898년 1월 13일: 에밀 졸라가 '로로르'라는 신문에 대통령에게 보내는 '나는 고발한다.'라는 공개 편지 기고.

1898년 8월 30일: 앙리 중령 자살. 진실이 탄로날까 두려워서..

1899년 6월 3일: 재심이 열림. 하지만 7명의 재판관 중 2명의 재판관만 드레퓌스 편을 들어 줌. 결국 드레퓌스는 '정상을 참작하여' 10년 형 선고 받음. 에밀 졸라가 다시 펜을 든다.

1899년 9월 19일: 드레퓌스 특별 사면. 드레퓌스를 지지했던 시민들 실망. 왜냐면.. 사면이라는게 자신의 죄를 인정해야 받을 수 있는 것이었기 때문에.

1902년: 에밀 졸라 석탄난로 가스 때문에 자다가 질식사. 옛날에 연탄 가스 때문에 죽는 것과 비슷한 듯.. 하지만 죽음이 석연찮은건 사실.

1904년 3월: 드레퓌스 재심 청구

1906년 7월 12일: 드레퓌스 무죄 선고

내가 보기엔 드레퓌스 사건은 특별 사면을 받은 시점에서 끝났다. 드레퓌스가 '사면'을 받았다고 해서 실망할수도 있겠지만, 당사자가 아니고서는 그러면 안된다. 자신이 유배 당했는데도 끝까지 남편을 위해서 고생해준 아내를 두고 어떻게 그 고통을 어떻게 지속하겠는가. 차라리 억울하더라도 '사면'을 받고 세상으로 나와서 다시 자신의 죄를 벗기는데 노력했으니 아주 현명한 선택이었다고 생각한다.

여기에 견줄만큼은 아니지만 이와 비슷한 사건은 저 뒤로도 전 세계에서 많이 발생했을 것이다. 저 사건으로 인해 프랑스를 비롯한 유럽에서 '인권'을 중요시 하게 되었다는 단순한 결론으로 끝나는건 정말 아쉬운 일이다.

저 사건은 비양심적인 대규모 집단과 양심적인 소수와의 싸움이다. 그 힘들고 때로는 승리가 불가능해 보이는 싸움에서도 얌심이 승리하는 방법을 알려준 역사적인 의미를 지니고 있다. 사실 책에서는 민중의 역할을 중요하게 본다. 아마도 이는 저자가 민중의 단결을 원했기 때문일 수도 있고 어쩌면 일부는 사실일 수도 있다. 하지만 난 그렇게 생각하지 않는다. 민중은 그냥 자기 맘에 드는걸 따라갔을 뿐이지. 그게 옳고 그른지 생각하는 건 한참이 지나서야 가능한 일인 것 같다. (이점에 대해서는 아직도 생각을 정리하는 중이라 나중에 자세히 쓸 생각이다.) 따라서 나는 민중 보다는 '창조적 소수자' '양심적인 지성인'의 역할이 민중의 역할보다 크다고 생각한다.

그 양심의 편에 서있던 대표적인 인물이 에밀 졸라 였다. 이미 프랑스에서 성공한 작가로 굳이 저런 일에 뛰어들지 않아도 혼자 잘먹고 잘 살 수 있었을텐데 그 사람은 왜 무모할 정도로 저런 일에 뛰어든걸까.

에밀 졸라 같이 절대 다수를 상대로 자신의 신념을 굽히지 않았던 사람들은 역사적으로 많은 것 같다. 가톨릭 교회가 면죄부를 팔던 시절에 '루터'라는 사람도 그러했고, 사대주의에 쩔어있던 고려시대의 '묘청'도 그러했다. 언론과 맞짱뜬 '노무현' 전대통령은 어떠한가.

내가 이런 역사를 통해서 궁금한건 바로 저 사람들이다. 대체 무엇때문에.. 왜... 저 사람들은 피곤한 길을 선택한 것일까. 아니 피곤하다 못해 자신의 목숨이 달려있는 일에 뛰어든 것일까. 하다못해 어느정도 승부수라도 보이면 모르겠다. 그런데 전혀 그런 상황이 아니었단 말이다.

다시 본 사건으로 돌아가서 에밀 졸라가 '나는 고발한다'는 글을 썼을 당시 드레퓌스는 이미 정부, 군대, 반유태인 세력 등 대다수로부터 미움을 받고 낙인이 찍힌 상태였다. 군국주의자들의 위신을 떨어뜨릴 수 있기 때문에 이미 진범도 알고 있으면서도 건들지 않았다. 오히려 진범인 에스테라지는 영국으로 망명가서 '다 위에서 시킨일이다'라는 내용의 책까지 썼다. 어디 그뿐인가. 시민들도 동요하여 유태인 상점을 부수고 다니며 드레퓌스 재심 반대파를 결성하여 드레퓌스 지지 세력과 싸움을 하고 다녔다. 그런 시대에 과감하게 펜을 들어 대통령에게 글을 보내다니.. 이게 말이나 되는 소~리?

지금 대한민국으로 따지면 한국을 대표 할만한 소설가(이외수가 그정도 되려나? 흠; 누가 노벨 문학상이라도 받았어야지 원.. )가 이명박한테 제발 4대강 한다면서 땅좀 파헤치지 말라고. '너는 삽질한다.'라는 장문의 비판글을 써제낀 겪이나 다름없다.

대체 그런 용기와 심념은 어디서 나오는 것일까...
혹시 모르니.. 다음에는 에밀 졸라가 쓴 '진실'이라는 소설을 봐야겠다.

저작자 표시
신고
top


Class.forName()와 ClassLoader.loadClass() 차이점

Java : 2010.03.09 18:04


http://www.javaworld.com/javaworld/javaqa/2003-03/01-qa-0314-forname.html?page=1

일단 Class.forName() 메서드는 인자가 한 개 짜리인 것과 세 개 짜리인 것이 있습니다.

static Class<?>     forName(String className)
          Returns the Class object associated with the class or interface with the given string name.

static Class<?>     forName(String name, boolean initialize, ClassLoader loader)
          Returns the Class object associated with the class or interface with the given string name, using the given class loader.

1. 클래스를 로딩할 때 사용하는 클래스로더 차이

인자가 한개 짜리인 forName(String) 메서드는 클래스를 로딩할 때 사용하는 클래스로더가 저 코드를 실행하는 클래스로더가 됩니다. 하지만 ClassLoader.loadClass()를 사용하면 당연히 자기 자신을 사용해서 클래스 로딩을 실행하게 되죠. (그렇다고 해서 반드시 해당 클래스로더가 읽어온다는 보장은 없죠. 그 부모가 읽어올 수도 있고 클래스 패스에 없을 수도 있고 암튼 여기서 로딩한다는 건 로딩을 시도한다고 보시기 바랍니다.)

하지만 Class.forName(String, boolean, ClassLoader)를 사용하면 클래스 로더를 지정해 줄 수 있습니다.

2. 초기화

Class.forName(String) 메서드를 사용하면 곧바로 클래스의 static 초기화 블럭과 static 멤버 변수의 값을 초기화 합니다. 하지만 ClassLoader.loadClass()를 사용하면 해당 클래스를 처음으로 사용하기 전까지 초기화가 지연됩니다.

이것 역시 Class.forName(String, boolean, ClassLoader)의 두번째 인자값을 이용하여 조절할 수 있습니다.

- 클래스 초기화 에러

만약 Class.forName(String)을 사용해서 로딩할 때 static 영역에서 에러가 난다면 해당 클래스는 다시 로딩할 수가 없습니다. 특정 클래스로더가 일단 로딩한 클래스는 다시 로딩할 수가 없죠. 그래서 NoClassDefinitionFound 에러가 날 수도 있습니다.

이때는 해당 클래스로더 인스턴스를 버리고 새로 만들어야 하는데 그럴 때를 대비해 인자 세개짜리 forName을 쓰라는군요.

결국 forName()으로 클래스를 로딩할 떄는 별 개의 클래스로더를 쓰라는건데... 흠.. 그렇게 단순해 보이지가 않는데;; 머 클래스로더를 지정해 둔다고 해봤자. 보통 App CL로 읽어올테고 그럼 App CL 인스턴스를 버리라고?? 에이;; 그건 좀..

forName으로 읽어올 클래스를 클래스패스를 가지고 있으면서 parent로 위임하지도 않는 CL을 이용해서 forName으로 읽은 경우라면 뭐 괜찮을지도.. 어쨋거나 직접 통제가 가능한 클래스로더를 사용해야 겠군요.


저작자 표시
신고
top


[Google Visualization API] 캬.. 멋지구나~

모하니?/Coding : 2010.03.07 23:12


http://code.google.com/intl/ko-KR/apis/visualization/interactive_charts.html
http://code.google.com/intl/ko-KR/apis/visualization/documentation/index.html
http://code.google.com/intl/ko-KR/apis/visualization/documentation/using_overview.html
http://code.google.com/intl/ko-KR/apis/visualization/documentation/reference.html
http://code.google.com/apis/ajax/playground/?type=visualization#motion_chart


09-10 시즌 보딩을 저번 주를 끝으로 접었더니 주말에 갑자기 시간이 좀 생겼네요.
심심해서 낮에는 본가에 다녀오고 저녁에는 와이프가 공부하느라 흑흑.. 저는 코딩이나...

http://springsprout.org/statistics/index.do
저작자 표시
신고
top


톰캣 메일링 리스트 가입하기

모하니?/Coding : 2010.03.05 20:34


http://tomcat.apache.org/lists.html#tomcat-dev

1. 메일 보내기

위 페이지에서 원하는 메일링 리스트로 메일을 보낸다. 제목도 없고 본문도 없이 그냥 빈 메일..

2. 답장하기

그럼 조금 뒤에 메일이 한통오는데 잘 읽어보면 결국 확인할겸 해당 주소로 답장을 보내달라는 것이다.
그냥 답장 버튼을 눌러서 간단하게 적어서 전송하면 된다.

3. 확인하기

그럼 확인 메일이 또 한통 온다.

끝.
저작자 표시
신고
top


[스프링 3.0] @Async 테스트

Spring/3.0 : 2010.03.04 17:55


@Async 애노테이션을 사용한 메서드의 반환 타입은 둘 중 하나여야 합니다. void 거나.. java.util.concurrent.Future 타입이어야 한다네요. 뭐.. 그러니까 사실상 하나나 마찬가지이죠. 그렇다고 해서 다른 리턴타입으로 설정하면 비동기로 동작하지 않는 건 아닙니다. 다만;; 해당 메서드의 클라이언트 입장에서 보면 리턴값이 전부 null 이기 때문에 황당한 경우가 발생할테지만 말이죠.

스프링에서 Future 인터페이스의 구현체로 AsyncResult를 제공해줍니다. 이걸 이용해서 간단하게 Thread를 반환하는 비동기 메서드를 만들었습니다.

    @Async
    public Future<Thread> more() {
        return new AsyncResult<Thread>(Thread.currentThread());
    }

@Async는 @Transaction과 비슷하게 타입에 선언할 수도 있습니다. 그러면 해당 클래스의 모든 메서드가 비동기 메서드로 처리되겠죠.

<task:executor id="myExecutor" pool-size="5"/>

쓰레드 풀 갯수를 5개로 설정해 놓고 다음과 같이 테스트를 해봤습니다.

    @Test
    public void async() throws Exception {
        assertThat(beanService, is(notNullValue()));
        Set<Thread> threads = new HashSet<Thread>();

        for(int i = 0 ; i < 200 ; i++){
            collectThreadInfo(beanService.more(), threads);
        }
        assertThat(threads.size(), is(5));
        assertThat(threads.contains(beanService.more().get()), is(true));
    }

    private void collectThreadInfo(Future<Thread> future, Set<Thread> threads) throws Exception {
        threads.add(future.get());
    }

200번까지 안돌려도 상관없지만. 그냥.. 충분히 돌려서 쓰레드 풀에 있는 모든 쓰레드를 컬렉션에 모아둔 다음에 쓰레드 풀에서 만든 쓰레드 갯수를 확인하고 마지막으로 한 번 더 호출해서 반환 받은 Thread가 현재까지 사용한 쓰레드 중 하나인지 확인합니다.


저작자 표시
신고
top

TAG @Async, Spring

[MySQL Administrator] Row 갯수 제대로 못세나?

Good Tools : 2010.03.03 16:38


스키마 테이블 탭에서 보여주는 Rows 갯수는 믿지 못하겠습니다.


리프래시를 하면..


헐.. 갯수가 계속 바뀝니다.


이렇게 찍으면 정확한 갯수가 나오는데 말이죠. 왜이런담; 멀라 멀라. @_@
저작자 표시
신고
top


우편번호 검색&입력기 만들기

모하니?/Coding : 2010.03.03 16:30


1. 우편번호 DB 다운받기

http://www.zipfinder.co.kr/zipcode/index.html

우정국에서 제공하는 zipcode 데이터를 받습니다.
여러가지 형태가 있는데 저는 가장 컬럼 갯수가 적은 걸 받았습니다.

2. 도메인 만들기

@Entity
@DomainInfo("우편번호")
public class Zipcode {

    @Id
    @GeneratedValue(strategy= GenerationType.AUTO)
    int id;
    @Column(length=7)
    @DomainInfo("zipcode")
    String zipcode;
    @Column(length=4)
    String sido;
    @Column(length=15)
    String gugun;
    @Column(length=52)
    String dong;
    @Column(length=17)
    String bunji;

위에서 받은 필드에 맞게 정의해줍니다.
hbm2ddl을 auto 모드로 놓고 개발중이라 하이버네이트가 돌다가 테이블이 없어서 만들어 줍니다.
제품 단계에서 hbm2ddl을 auto로 설정해두는 것은 매우 위험하오니 꼭 개발중에만 사용하시길..(저는 배포 중에도 쓰지만 ㅋㅋㅋ)

3. zipcode 데이터 넣기

1번에서 받은 데이터를 엑셀로 변환하고 만개 단위로 나눠서 저장했습니다. DBUnit으로 넣었는데 5만개가 넘는걸 한 방에 넣으려고 했더니 POI 쪽에서 에러가 나더군요. 스크롤 기능이 있는지 찾아보려다 귀찮아서 그냥 만개 단위로 잘라서 넣었습니다.

        dbUnitSupport.insertXlsData("zipcode1.xls");
        dbUnitSupport.insertXlsData("zipcode2.xls");
        dbUnitSupport.insertXlsData("zipcode3.xls");
        dbUnitSupport.insertXlsData("zipcode4.xls");
        dbUnitSupport.insertXlsData("zipcode5.xls");

대충 요런식으로..

4. 사용자 추가 화면에 우편번호 자동완성 필드 추가

<k:fzipcode />

이 태그 파일을 만들고 저거 한줄을 사용자를 추가하는 jsp에 추가해줍니다. 저 태그 파일은 대충..

...
<c:set var="pathNS" value="${path != null ? path : 'zipcode'}" />
<c:set var="labelNS" value="${label != null ? label : '우편번호'}" />
<c:set var="popupUrlNS" value="${popupUrl != null ? popupUrl : '/system/zipcode/popupsearch.do'}" />

<o:fSearchPopup label="${labelNS}" url="/ajax/zipcode/dong.do" path="${pathNS}"
    model="zipcodes" size="20"  maxlength="20" />

<script type="text/javascript">
function formatItem(row, i, total){
    return row[1] + " " + row[2] + " " + row[3] + " " + row[4] + " " + row[5];
}
function formatResult(row){
    return row[1];
}
function result(event, row, formatted){
    if (row){
        $("#addr").attr("value", row[2] + " " + row[3] + " " + row[4]);
    }
}
$("#${pathNS}row img").click(function(e) {
    popup("<c:url value='${popupUrlNS}'/>", "itemPopup", 900, 900, "yes", "yes");
});
</script>
...

이런식으로 생겼습니다. 여기서 또 다른 태그 파일을 사용하고 있는데 그녀석이 예전에 만들어둔 Ajax 자동완성 태그 파일입니다.

5. Ajax 요청처리를 구현합니다.

    @RequestMapping("/ajax/zipcode/dong.do")
    public ModelAndView zipcodes(ModelMap map, AjaxParams ajaxParams) {
        map.addAttribute("zipcodes", zipcodeService.findZipcodesByDong(ajaxParams));
        return new ModelAndView("jsonView", map);
    }

Ajax 요청이 왔을 때 우편번호에 필요한 것들을 가져다 주도록 Service -> DAO로 전달 전달. DAO가 가져와서 Service -> Controller 전달 전달.. 뷰까지 ㄱㄱㄱ

6. 검색 버튼 구현

자동완성 오른쪽에는 항상 수동으로 검색할 수 있도록 검색 버튼이 달려있습니다. 팝업이 떠서 검색하고 팝업이 닫힐 때 사용자 추가 화면에 자동으로 값을 넣어 주도록 만드는게 관건.

버튼 눌렀을 때 요청할 URL은 4번 단계에서 태그 파일에 설정해 두었으니 이제 해당 요청을 처리할 컨트롤러와 뷰 만들고 서비스 구현하면 끝.

<o:searchpage title="우편번호 검색">
    <o:buttons>
        <o:searchbtn />
    </o:buttons>
    <o:searchform action="selectgrid.do">
        <o:searchrow>
            <o:text label="읍, 면, 동, 리, 건물명" id="dong" size="30" maxlength="30" />
        </o:searchrow>
    </o:searchform>
</o:searchpage>
<%@ include file="/include/searchfooter.jsp" %>

이게 검색화면 끝.

<o:gridpage>
    <o:zipcodeselectgrid list="${list}">
        <o:col header="id" path="id" hidden="true" type="integer" pattern="#########"/>
        <o:col header="우편번호" path="zipcode" width="80"/>
        <o:col header="시, 도" path="sido" width="50"/>
        <o:col header="구, 군" path="gugun" width="160" />
        <o:col header="동, 면, 건물" path="dong" width="250" />
        <o:col header="번지" path="bunji" width="180" />
    </o:zipcodeselectgrid>
</o:gridpage>

이게 그리드 화면 끝.

    @RequestMapping
    public void popupsearch() {
    }

    @RequestMapping
    public ModelAndView selectgrid(HttpServletRequest req, HttpServletResponse res,
            ModelMap model, ZipcodeParams params, OrderPage orderPage, String view) throws Exception {
        ModelAndView mav = super.grid(req, res, model, params, orderPage, view);
        mav.setViewName("system/zipcode/selectgird");
        return mav;
    }

이게 요청 처리 및 서비스 구현 끝.

7. 완성



팝업으로 검색해서 선택하면 우편번호와 주소에 팍팍 입력되고 팝업 닫히는 것도 되지만 스샷은 생략~
저작자 표시
신고
top


톰캣 6.0 클래스로더 구조

Java : 2010.03.02 18:10


http://tomcat.apache.org/tomcat-6.0-doc/class-loader-howto.html

BootStrap CL: JVM 코어, 확장 라이브러리용 클래스 로더

System CL: $CATALINA_HOME/bin/bootstrap.jar와 CATALINA_HOME/bin/tomcat-juli.jar를 가지고 시스템 클래스 로더를 생성함. 일반적인 애플리케이션의 시스템 클래스 로더와는 동작 방식이 다름. 일반적인 애들은 시스템 클래스로더에서 CLASSPATH에 있는 것들을 로딩하지만, 톰캣의 스크립트(($CATALINA_HOME/bin/catalina.sh 또는 %CATALINA_HOME%\bin\catalina.bat)가 이를 무시해버림.

Common CL: $CATALINA_HOME/lib에 위치한 톰캣 내부 클래스와 웹 애플리케이션이 공통으로 참조할 라이브러리를 로딩한다. 

웹 애플리케이션 CL:  각각의 웹 애플리케이션당 해당 애플리케이션의 /WEB-INF/classes와 /WEB-INF/lib 디렉토리에 있는 클래스와 리소스를 로딩한다. 
SRV.9.7.2 Web Application Classloader

The classloader that a container uses to load a servlet in a WAR must allow the
developer to load any resources contained in library JARs within the WAR
following normal J2SE semantics using getResource. It must not allow theWAR to
override J2SE or Java servlet API classes. It is further recommended that the loader
not allow servlets in theWAR access to the web container’s implementation classes.
It is recommended also that the application class loader be implemented so that classes and resources packaged within the WAR are loaded in preference to classes and resources residing in container-wide library JARs.
위와 같은 서블릿 2.3 스펙에 따라 웹 애플리케이션 CL은 다음의 순서로 클래스를 로딩한다.

1. Bootstrap classes of your JVM
2. System class loader classes (described above)
3. /WEB-INF/classes of your web application
4. /WEB-INF/lib/*.jar of your web application
5. $CATALINA_HOME/lib
6. $CATALINA_HOME/lib/*.jar

1번은 웹 애플리케이션 CL의 최상위 CL인 BootStrap CL에 의해 로딩될 것이고, 거기서 못찾는다면
2번 System CL에 의해서 로딩 된다. 거기서도 못찾는거라면 일단 자바의 코어 라이브러리나 확장 라이브러리도 아니며 톰캣의 내부 클래스(톰캣 서버를 실행하는 클래스와 톰캣의 Commons logging API와 java.util.logging LogManager를 찾는게 아닐것이다. 그런 경우.
3번과 4번에서 웹 애플리케이션 CL 자신의 클래스패스에 있는 라이브러리를 찾아보고.. 거기에 없는 경우
5, 6번 Commons CL의 클래스패스에서 찾아본다.

클래스 로더의 동작 방식이 웹 애플리케이션이 아닌 경우가 웹 애플리케이션인 경우가 다르며, 이것 마저도 웹 서버 마다 다를 수 있으니 자신이 사용하는 컨테이너의 클래스로더 동작 방식을 알고 있어야겠다. 

흠.. 스프링의 TC 서버는 어떨지 궁금하다. 그것도 톰캣을 확장한 거니까 이것과 비슷하지 않을까. DM 서버는 왠지 완전히 다를 것 같다.
저작자 표시
신고
top

Java : 2010.03.02 18:10 Trackback. : Comment.

null은 캐스팅이 되는건야 안 되는거야?

Java : 2010.03.02 13:38


    @Test
    public void nullCasting() throws Exception {
        Class nullClass = (Class)null;
        Class thisClass = this.getClass();
        assertThat(nullClass, is(nullValue()));
        assertThat(thisClass, is(instanceOf(Class.class)));
        assertThat(nullClass, is(not(instanceOf(Class.class))));
    }

캐스팅이 된것 같다. 하지만 안 됐다. 

저 상태에서 컴파일 에러가 날 것 같지만 컴파일 에러가 발생하지 않았다.

Whiteship whiteship = (Whiteship)thisClass;

하지만 이런 코드를 적어보면 컴파일 에러가 난다. 

그렇다면 null을 어느 타입으로든 캐스팅 하는건 문법적인 에러가 아닌가본데... 사실 그 안의 인스턴스를 사용하려들면 에러가 난다.

nullClass.getDeclaredMethods();

즉 이런 코드를 실행하면 NullPointerException이 발생하게 된다.

 따라서 null을 다른 타입으로 캐스팅을 하는 코드를 작성 하더라도 컴파일 에러가 나지는 않지만 그렇다고 해서 실제로 캐스팅이 된 것은 아니다.

대체 이런 짓을 왜 할까? 언제 null을 다른 타입으로 캐스팅 하는 코드를 써먹을 수 있을까?
퀴즈로 남겨줄까 한다. 후훗.

힌트이자 정답을 알려주자면...스프링의 ClassPathResource의 소스코드를 보면 나와있다.
저작자 표시
신고
top


봄싹은 마치 경차같은...

모하니?/Thinking : 2010.03.02 11:18




커뮤니티의 규모가 작다고해서 그 구성원들의 실력이나 커뮤니티의 내실이 허술한 건 아니죠. 

흠.. 그래도 이러한 사회적 착각이 팽배한 사회라면... 어차피 유지비도 그게 그거라던데 대형차로 갈아타는 것도 편할 것 같습니다.

걍 그렇다구요.ㅋㅋ 
저작자 표시
신고
top

TAG 봄싹






티스토리 툴바