달력

122024  이전 다음

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

'프로그래밍/It 용어'에 해당되는 글 27건

  1. 2016.09.28 웹기술동향 ( HTML5 )
  2. 2016.09.27 IT에서 IP란?? ( IP - 지적재산권 )
  3. 2016.09.27 기본용어
  4. 2016.09.26 자바 리플렉션 ( JAVA Reflection )
  5. 2016.09.26 스프링 프레임워크 ( Spring Framework )
프로그래밍/It 용어 2016. 9. 28. 10:33


'프로그래밍 > It 용어' 카테고리의 다른 글

IT에서 IP란?? ( IP - 지적재산권 )  (0) 2016.09.27
기본용어  (0) 2016.09.27
자바 리플렉션 ( JAVA Reflection )  (0) 2016.09.26
스프링 프레임워크 ( Spring Framework )  (0) 2016.09.26
퍼시스턴스 프레임워크  (0) 2016.09.26
Posted by 당구치는 개발자
|
프로그래밍/It 용어 2016. 9. 27. 17:58



출처 : http://webcache.googleusercontent.com/search?q=cache:FcTV9DEbsckJ:welcon.kocca.kr/common/cmm/fms/FileDown.do%3FatchFileId%3DFILE_800000000209246%26fileSn%3D1%26bbsId%3D+&cd=7&hl=ko&ct=clnk&gl=ph

'프로그래밍 > It 용어' 카테고리의 다른 글

웹기술동향 ( HTML5 )  (0) 2016.09.28
기본용어  (0) 2016.09.27
자바 리플렉션 ( JAVA Reflection )  (0) 2016.09.26
스프링 프레임워크 ( Spring Framework )  (0) 2016.09.26
퍼시스턴스 프레임워크  (0) 2016.09.26
Posted by 당구치는 개발자
|
프로그래밍/It 용어 2016. 9. 27. 16:21


Posted by 당구치는 개발자
|
프로그래밍/It 용어 2016. 9. 26. 11:44



Java 편집 ]

다음 코드는 java.lang.reflect 패키지를 사용한 Java 6 이후의 예이다.

/ / 리플렉션없이 
Foo foo =  New Foo ( ) ; 
foo. hello ( ) ;
/ / 리플렉션 
Class CL =  Class . forName ( "Foo" ) ; 
Method Method = cl. getMethod ( "hello" ) ; 
method. invoke ( cl. newInstance ( ) ) ;

어느 코드에서도 Foo 클래스 의 인스턴스 를 만들고 인스턴스의 hello () 메소드 를 부르고있다. 전자의 예는 클래스와 메소드 이름이 하드 코딩되어 있기 때문에 실행시 다른 클래스로 변경하는 것은 불가능하다. 리플렉션을 이용한 후자의 예는 그들을 실행할 때 쉽게 변경할 수있다. 그러나 반면에, 후자는 어지럽고 또한 컴파일 시 검사의 혜택도 얻을 수 없다. 즉, 만약 Foo 클래스가 존재하지 않았다고하면 전자의 코드는 컴파일시에 오류가되지만, 후자의 코드가 실행될 때까지 오류가 발생하지 않는다.

Perl 편집 ]

다음 코드는 같은 예를 Perl 로 쓴 것이다.

# 리플렉션없이 
Foo -> new -> hello ( ) ;
# 리플렉션 
my  $ class  =  "Foo" ; 
my  $ method  =  $ class -> can ( "hello" ) ; 
$ class -> new -> $ method ( ) ;

Objective-C 편집 ]

다음 코드는 같은 예를 Objective-C 로 쓴 것이다.

/ / 리플렉션없이 
[ [ [ Foo alloc ] init ] hello ] ;
/ / 리플렉션 
ID AClass = objc_getClass ( "Foo" ) ;
 SEL aSelector = NSSelectorFromString ( @ "hello" ) ;
objc_msgSend ( [ [ aClass alloc ] init ] , aSelector, nil ) ;

ActionScript 편집 ]

다음은 같은 예제를 ActionScript 로 쓴 것이다.

/ / 리플렉션없이 
var foo : Foo = New Foo ( ) ;
foo. hello ( ) ;
/ / 리플렉션 
var ClassReference : Class = flash. utils . getDefinitionByName ( "Foo" ) as Class ;
 var instance : Object = New ClassReference ( ) ;
instance. hello ( ) ;

JavaScript 편집 ]

다음은 같은 예제를 JavaScript 로 쓴 것이다.

/ / 리플렉션없이 
var foo =  New Foo ( ) ; 
foo. hello ( ) ;
/ / 리플렉션 
var foo =  this [ 'Foo' ] ; 
var methodName =  'hello' ; 
( New foo ( ) ) [ methodName ] ( ) ;

C # 편집 ]

다음 예제는 C # 의 예에서보다 진전 된 반성의 용법을 보여주고있다. 프로그램은 명령 줄 에서 어셈블리 이름을 입력으로 취한다. 어셈블리는 클래스 라이브러리 와 같은 것이다. 어셈블리가로드되면 어셈블리에 정의 된 방법을 찾기 위해 리플렉션을 사용한다. 발견 된 각 메소드에 대해 최근 변경이 있었는지 여부를 리플렉션을 사용하여 조사하고있다. 만약 변경이 있어야하며 인수를 취하지 않는 방법이면 메서드 이름과 반환 형식을 표시한다.

최근 변경되었는지 여부를 확인하기 위해 개발자는 사용자 정의 속성 을 사용할 필요가있다.

using  System ; 
using  System.Collections.Generic ; 
using  System.Text ; 
using  System.Reflection ; 
using  Recent ;
 
namespace Reflect
 { 
    class Program
     { 
        private Assembly A ; 
        Program ( String assemblyName ) 
        { 
            a = Assembly . Load ( New AssemblyName ( assemblyName ) ) ; 
            / / 지정된 어셈블리에 SupportsRecentlyModified 특성이 적용되어 있는지 확인. 
            Attribute c = Attribute . GetCustomAttribute ( a, typeof ( SupportsRecentlyModifiedAttribute ) ) ; 
            if  ( c ==  null ) 
            { 
                 / / "SupportsRecentlyModified"속성 가져 오지 못했습니다. 
                 / / 즉, 어셈블리는 "RecentlyModified"속성을 지원하지 않는다. 
                 throw  New Exception ( "어셈블리"  + assemblyName +  "필요한 속성을 지원하지 않습니다." ) ; 
            } 
            Console . WriteLine ( "읽기 완료 :"  + a . FullName ) ; 
        }
 
        public  void FindNewMethodsWithNoArgs ( ) 
        { 
            / / 어셈블리에 정의 된 형식을 모두 취득하는 
            Type [ ] t = a . GetTypes ( ) ; 
            foreach  ( Type type in T ) 
            { 
                / /이 형식이 클래스이어야 건너 뜀. 
                if  ( ! type . IsClass ) 
                    continue ; 
                Console . WriteLine ( "클래스 이름 :"  + type . FullName ) ; 
                MethodInfo [ ] methods = type . GetMethods ( ) ; 
                foreach  ( MethodInfo Method in Methods ) 
                { 
                    object [ ] ab = method . GetCustomAttributes ( typeof ( RecentlyModifiedAttribute ) , true ) ; 
                    / / 속성이 하나를 얻을 수없는 경우에,이 메소드는 이전하는 것이다. 
                    if  ( ab . Length  ! =  0 ) 
                    { 
                        / / 그렇지 않으면 하나의 속성 만 검색된다. 
                        / / 이는 "RecentlyModified"속성은 다른 속성과 함께 수 없기 때문이다.
 
                        Console . Write ( " \ t 업데이트 된 메소드 : "  + method . Name ) ; 
                        if  ( method . GetParameters ( ) . Length  >  0 ) 
                            break ;
 
                        / / 개발자가 지정한 코멘트를 얻기 위해, 
                        / / "RecentlyModifiedAttribute"속성의 인스턴스를 사용한다. 
                        Console . WriteLine ( " \ t "  +  ( ab [ 0 ]  as RecentlyModifiedAttribute ) . comment ) ; 
                        Console . WriteLine ( " \ t \ t 반환 값 : "  + method . ReturnType . Name ) ; 
                    } 
                } 
            } 
        }
 
        static  void Main ( string [ ] args ) 
        { 
            try 
            { 
                Program reflector =  New Program ( "UseAttributes" ) ; 
                reflector . FindNewMethodsWithNoArgs ( ) ; 
            } 
            catch  ( Exception e ) 
            { 
                Console . Error . WriteLine ( e . Message ) ; 
            } 
        } 
    } 
}

에서 사용하는 사용자 지정 특성의 구현을 다음.

using  System ; 
using  System.Collections.Generic ; 
using  System.Text ;
 
namespace Recent
 { 
    / /이 특성은 메서드에만 적용 할 수 없으며 다른 특성과 함께 할 수 없다. 
    [ AttributeUsage ( AttributeTargets . Method , AllowMultiple = false , Inherited = true ) ] 
    public  class RecentlyModifiedAttribute : Attribute
     { 
        / /이 특성은 메서드에 적용된다. 
        / / 인수없이 (RecentlyModified), 
        / / 또는 코멘트와 함께 (RecentlyModified (comment = "<someComment>")) 적용 할 수있다.
 
        private  String Comment =  "이 방법은 최근에 변경되었습니다." ;
 
        public RecentlyModifiedAttribute ( ) 
        { 
            / / 속성의 인스턴스를위한 빈 생성자. 
            / / 필수 인수는 없기 때문에 생성자는 하늘. 
        }
 
        / / 선택적 인수 "comment"의 정의. 속성이 사용되는 때 명명 된 인수로 지정된다. 
        public  String comment
         { 
            get 
            { 
                return Comment ; 
            } 
            set 
            { 
                Comment = comment ; 
            } 
        } 
    }
 
    [ AttributeUsage ( AttributeTargets . Assembly , AllowMultiple = false ) ] 
    public  class SupportsRecentlyModifiedAttribute : Attribute
     { 
        / /이 특성은 인수없이 어셈블리에 적용된다. 
        / / as in [SupportsRecentlyModified] 
        public SupportsRecentlyModifiedAttribute ( ) 
        { 
            / / 필수 인수는 없기 때문에 생성자는 하늘. 
        } 
    } 
}

또한 사용자 지정 특성을 사용하여 클래스 정의 예를 들면.

using  System ; 
using  System.Collections.Generic ; 
using  System.Text ; 
using  Recent ;
 
/ / 어셈블리가 "RecentlyModified"속성을 지원하는 것을 보여주기 위해 
/ / "SupportsRecentlyModified"속성을 적용한다. 
[ assembly : SupportsRecentlyModified ]
 
namespace Reflect
 { 
    class UseAttributes
     { 
        private  Object info ;
 
        public UseAttributes ( ) 
        { 
            info =  ( object )  "Hello World" ; 
        }
 
        public  void OldMethodWithNoArgs ( ) 
        { 
            Console . WriteLine ( "이것은 인수를 취하지 않는 오래된 방법이다." ) ; 
        }
 
        / / 메소드가 최근 변경되었다는 것을 나타내는 "RecentlyModified"속성을 적용한다. 
        [ RecentlyModified ] 
        public  void NewMethodWithNoArgs ( ) 
        { 
            Console . WriteLine ( "이것은 인수를 취하지 않는 새로운 방법이다." ) ; 
        }
 
        public  void OldMethodWithOneArg ( object something ) 
        { 
            info = something ; 
            Console . WriteLine ( "이것은 인수를 1 개 취하는 오래된 방법이다." ) ; 
        }
 
        [ RecentlyModified ] 
        public  void NewMethodWithOneArg ( object something ) 
        { 
            info = something ; 
            Console . WriteLine ( "이것은 인수를 1 개 취하는 새로운 방법이다." ) ; 
        }  
    } 
}


===================================================================================


컴퓨터 과학에서, 반영(Reflection)은 컴퓨터 프로그램에서 런타임 시점에 사용되는 자신의 구조와 행위를 관리(type introspection)하고 수정할 수 있는 프로세스를 의미한다. “type introspection”은 객체 지향 프로그램언어에서 런타임에 객체의 형(type)을 결정할 수 있는 능력을 의미한다.

많은 컴퓨터 아키텍처에서, 프로그램 명령은 데이터와 같이 메모리에 적재되므로, 명령과 데이터 사이의 구분은 단지 컴퓨터와 프로그램 언어에 의하여 어떻게 정보가 처리되느냐의 문제이다. 일반적으로, 명령은 실행되고, 데이터는 (명령의 자료로서) 처리된다. 그렇지만, 어떤 언어에서는, 프로그램은 명령 역시 데이터와 같은 방식으로 처리 할 수 있기 때문에, Reflective 수정이 가능하게 된다. 가장 일반적으로 반영은 스몰토크, 스크립트 언어와 같이 높은 수준의 가상 머신 프로그램 언어에서 주로 많이 사용되고, 자바C 언어와 같이 선언적이거나 정적 형식의 프로그램 언어에서는 드물게 사용된다.

[편집]이용

반영은 런타임에 프로그램의 수행을 수정하고, 관찰하기 위하여 사용할 수 있다. 반영 지향적인 프로그램 구성 요소는 내부 코드의 실행을 감시할 수 있고, 구성 요소 자신의 궁극적인 목표에 맞도록 내부를 수정 할 수 있다. 이는 전형적으로 런타임에 프로그램 코드를 동적으로 할당하여 이루어진다.

반영은 프로그램을 서로 다른 상황에서 동적으로 사용할 수 있게 한다. 예를 들어, 비슷한 동작을 수행하는 서로 다른 클래스 ‘X’와 클래스 ‘Y’를 교체하여 사용하는 응용 프로그램을 고려해보자. 반영 지향적이 아닌 프로그램 개발에서는, 응용 프로그램은 클래스 ‘X’와 클래스 ‘Y’의 함수를 호출하기 위하여, 함수(이름)을 직접 코드로 작성해야 한다. 반면, 반영 지향적인 프로그램 패러다임에서, 응용 프로그램은 함수(이름)을 직접 코드로 작성하지 않고, 클래스 ‘X’와 클래스 ‘Y’의 함수를 호출하기 위하여, 반영을 이용하여 설계되고 작성될 수 있다. 반영 지향적인 프로그램은 보다 보편적인(Generic) 코드의 수행의 기능을 사용하기 위하여, 거의 언제나 추가적인 정보, 프레임워크, 연관 사상, 객체 관계 등을 필요로 한다. 반영 지향적인 프로그램 기술을 확장하면, 하드 코딩을 방지할 수 있다.

[편집]기능

반영이 지원되는 언어는, 낮은 수준의 언어의 처리와는 다른, 런타임에 사용할 수 있는 매우 모호한 몇 가지 특성을 제공한다. 이런 특성들은 다음과 같다.

  • 런타임에 First-Class 객체로서 소스 코드의 생성자(코드 블록, 메소드, 프로토콜, ……)를 인식하고 수정할 수 있다.
  • 참조 및, 호출되는 클래스 또는 함수와 일치하는 문자열을 클래스 또는 함수의 기호 이름으로 변환할 수 있다.
  • 런타임에 소스 코드 구문으로 구성된 문자열을 평가할 수 있다.
  • 프로그래밍 구성에 새로운 의미 또는 의도를 부여하여, 프로그램 언어의 바이트 코드에 대한 새로운 번역기를 생성할 수 있다.

컴파일 언어들은 소스 코드에 대한 정보를 제공하기 위하여, 자신들의 런타임 시스템에 의존한다. 예를 들어, 컴파일 된 오브젝티브 C 실행 모듈은, 프로그램 컴파일 과정을 통하여 소스 코드와 메소드가 상응하는 테이블을 제공하여, 실행 모듈의 블록 안에 모든 메소드의 이름을 기록한다. 커먼 리스프(Common Lisp) 언어와 같이, 함수의 런타임 생성을 지원하는 컴파일 언어는 런타임 환경은 컴파일 또는 인터프리터를 포함해야 한다.

반영은 자동화된 소스 코드 변환 체계를 정의한 프로그램 변환 시스템을 사용하여, 반영 기법이 내장되지 않은 언어에서 구현될 수 있다.

[편집]예제

다음은 여러 언어에서 구현된 반영의 사용 예를 표시한다.

C#:

Int32 i = 1234;
Console.WriteLine(String.Format("{0}", i.ToString("F")));
 
Type t = typeof(Int32);
// Reflection 1
Object res1
    = t.InvokeMember("ToString", 
        System.Reflection.BindingFlags.InvokeMethod, 
        null, 
        i, 
        new Object[]{"C", CultureInfo.CreateSpecificCulture("ko-KR")});
Console.WriteLine(String.Format("{0}", res1));
 
// Reflection 2
System.Reflection.MethodInfo method 
    = t.GetMethod("ToString", new Type[] { typeof(String) });
Object res2 = method.Invoke(i, new Object[] {"X"});
Console.WriteLine(String.Format("{0}", res2));

Objective-C

NSString* str = [[NSString alloc] initWithString:@"1234"];
NSLog(@"%d", (int)[str length]);
 
// Reflection
Class class = NSClassFromString(@"NSString");
id idOfClass = [[class alloc] initWithString:@"Reflection"];
SEL selector = NSSelectorFromString(@"length");
NSLog(@"%d", (int)[idOfClass performSelector:selector]);
 
[str release];
[idOfClass release];

Python:

>>> class MyClass:
...     def Hello(self, sText):
...             return 'Hello ' + sText
...
>>> MyClass().Hello('Peter')
'Hello Peter'
>>> 
>>> getattr(globals()['MyClass'](), 'Hello')('Peter')
'Hello Peter'
>>>


===================================================================================


RTTI(Run-Time Type Information)을 이용하면 실행중에 타입 정보를 알아내고 사용할 수 있다. 이는 매우 까다롭고 복잡한 문제점들을 쉽고 빠르게 해결 할 수 있고, 굉장히 강력한 프로그램을 작성 할 수 있게 도와준다. 하지만 이는 객체 지향적인 프로그래밍 방법을 위배하고, 코드의 가독성을 떨어트린다는 의견도 있다. 그렇지만 이런 강력한 기능의 유혹을 벗어나긴 힘들다. 그리고 실제 여러 Framework에서 RTTI를 잘 활용 하고 있다. (특히나 Spring Framework)



자바에서는 모든 .class 파일 하나당 java.lang.Class 객체 하나씩 생성된다. Class는 모든 .class들의 정보를 가지고 있으며 .class파일에 같이 저장된다. 모든 .class들은 이 클래스를 최초로 사용하는 시점에서 동적으로 ClassLoader을 통해 JVM에 로드된다. 


최초로 사용하는 시점이라면 해당 .class에서 static을 최초로 사용할때를 말한다. (생성자도 static 메소드이다.  그렇기 때문에 new를 하게 되면 로드된다고 보면 된다. 이를 동적 로딩이라 한다.) 


이렇게 .class의 정보와 Class객체는 JVM에 Run Time Data Area의 Method Area에 저장된다.


그리고 이러한 정보들을 java.lang.reflect 에서 접근할 수 있게 도와 준다.  이 패키지에서는 Field, Method, Constructor같은 class들이 있고,  Constructor를 통해 새로운 객체를 생성하고, getter method와 setter method를 통해 Field의 값을 읽거나 수정할 수 있다. 또 invoke method를 통해 method를 호출한다. 그 외에도 parameter, return type, modifier 등 class의 관련된 모든 정보를 알아올 수 있고 조작할 수 있다. 심지어 private으로 Encapsulation된 immutable한 것들 까지도 setAccessible을 통해 접근, 조작 할 수 있다. 


출처 - http://blog.naver.com/PostView.nhn?blogId=cracker542&logNo=40160053694


===================================================================================


Java 란 언어는 참 보면 볼 수록 재미난 언어다. 기반 라이브러리가 참 충실하다보니 이것 저것 생각나는걸 해볼 수 있다는게 참 맘에 든다. 하기사... 다른 언어들도 그렇지만 암튼 그렇다 치자. 
  오늘은 얼마전에 개발을 시작한 모 프로그램에 들어가는 기능에 삘받아 간단한 리플렉션 예제를 실어 볼까한다. 리플렉션을 쓰다보면 참 노가다도 이런 상노가다가 없다. 물론 인터페이스를 사용할 수 없을 때 얘기다. 이게 뭔고 하니 아래의 코드로 비교를 해보자. 일단은 인터페이스를 쓰지않은 리플렉션 코드다.

우선 리플렉션을 통해 실행할 클래스를 소개한다.


public class HandlerA{
  private String msg;
  
  public void setMessage(String msg){
    this.msg = msg;
  }
  
  public void handle(){
    System.out.println(msg);
  }
}

아~ 주 심플하다. 메시지를 설정하고 표준출력으로 뱉는 단순한 기능을 하는 클래스다. 

그러면 저 녀석을 로드해서 실행하는 클래스를 보자.


import java.lang.reflect.Method;

public class DynaLoad{
  public static void main(String[] args) throws Exception{
    if(args.length < 1){
      System.out.println("DynaLoad classname");
    }else{
      loadAndExecute(args[0]);
    }
  }
  
  public static void loadAndExecute(String className) throws Exception{
    Class c = null;
    c = Class.forName(className);
    Object target = c.newInstance();
    Method m = c.getDeclaredMethod("setMessage", String.class);
    m.invoke(target, "Hello");
    m = c.getDeclaredMethod("handle");
    m.invoke(target);
  }
}



뭐시가 아주 길다. java.lang.reflect.Method 라는 놈도 임포트 되어 있다. 요녀석이 실행되는 과정은 다음과 같다. 

ex) java DynaLoad HandlerA 라고 실행을 하면
녀석이 인자를 하나 받아서 해당 인자를 클래스 명으로 간주하고 로드를 한다. 그리고 나서 해당 Class 를 통해 객체를 하나 생성한다. 근데 그 객체가 뭔 타입인지 미리 알 수가 없으니 Object 로 일단 놓고 아까 로드한 클래스를 통해 우리가 호출할 객체의 메서드를 얻는다. Object 에는 setMessage 는 물론이거니와 handle 이라는 메서드도 없으니 클래스를 통해 호출할때 쓸 메서드를 얻어야 한다. 우선 setMessage 를 호출해야 하니까. setMessage 라는 메서드명과 setMessage 의 인자의 타입을 통해 해당 메서드를 얻는다. 그리고 나서 얻어낸 메서드에 생성한 객체와 인자를 넣고 invoke 를 호출해 주면 해당 객체에대해 setMessage 를 호출한 것과 동일한 효과를 볼 수있다. 뭐 이런식으로 해서 handle 도 호출하면 Hello 라고 출력되는걸 볼 수 있다. 아~ 뭔가 길고, 지루하지 않은가 게다가 메서드를 호출할 때마다 이짓을 해야하니 정말 죽을 맛이다. 그러면 저기다가 인터페이스를 이용하면 어떨까? 

일단 우리가 로드할 클래스는 어떠한 인터페이스를 구현한 클래스라고 가정하자. 이래야 뭔가 문제가 쉬워진다. 그 인터페이스는 아래와 같다.


public interface HandlerInterface {
  public void handle();
  public void setMessage(String msg);
}

간단하다. 호출할 메서드를 다 넣어놨다. 그러면 이녀석을 구현한 클래스는 어떨까? 아래를 보자

public class HandlerB implements HandlerInterface{
  private String msg;
  public void setMessage(String msg){
    this.msg = msg;
  }
  public void handle(){
    System.out.println(msg);
  }
}

implements 만 붙었을 뿐 HandlerA 와 똑같다! 그러면 위에서 썻던 DynaLoad 클래스는 어떻게 바뀔까?

import java.lang.reflect.Method;
public class DynaLoad{
  public static void main(String[] args) throws Exception{
    if(args.length < 1){
      System.out.println("DynaLoad classname");
    }else{
      loadAndExecute(args[0]);
    }
  }
  
  public static void loadAndExecute(String className) throws Exception{
    Class c = null;
    c = Class.forName(className);
    HandlerInterface target = (HandlerInterface)c.newInstance();
    target.setMessage("Hello");
    target.handle();
  }
}



main 메서드는 그대로고 loadAndExecute 라는 메서드는 좀 짧아졌다. 지금은 로드해서 실행할 클래스가 간단해서 그렇지 메서드가 수십개씩 되는 클래스라고 생각해 보라. 코드가 얼마나 줄겠는가?!! 위의 코드는 딱히 설명할 것도 없다. 인터페이스를 이용함 으로써 로드된 클래스의 모든 기능을 맘편히 사용할 수 있다.


출처 - http://lemonfish.egloos.com/4087285


===================================================================================


(1) 리플렉션(Reflection)

 

자바에서 리플렉션은 유연성을 제공하기위해 필수적인 기법이다물론 리플렉션이 없더라도 훌륭한 코드를
작성할 수 있다하지만 리플렉션을 사용하면 좀더 유연한 프로그램을 작성할 수 있다자바에서 리플렉션을 
이해하기 위해서자바 클래스 파일은 바이트 코드로 컴파일 되며 실행시간에 이 바이트 코드가해석되어
 
실행된다는 것을 아는 것이 첫 출발점이 된다이 바이트 코드에는 클래스에 대한 모든 정보를포함하고 있다

클래스 파일이 있는 위치와 이 클래스 파일의 이름을 알수 있다면 언제든지 바이트 코드를 뒤져서 클래스에 대한
 
정보를 얻어낼 수 있다이제부터 리플렉션을 통해 어떻게,어떤 정보를 얻을 수 있는지 알아보자.




1. 
리플렉션(Reflection)

리플렉션은 구체적인 클래스 타입을알지 못해도, 컴파일된 바이트 코드를 통해 역으로 클래스에 정보를 알아내어
클래스를 사용할 수 있는 기법을 의미한다. 마치 거울에 비친 모습과 유사하여 리플렉션이란이름을 붙힌 것 같다

2. 리플렉션을사용하는 이유

리플렉션은 조합(Composition)과 함께 사용되어 다형성을 구현하는 강력한 도구이다. 조합을사용하여 교체할 수 있는 
위임 클래스를 리플렉션을 통해 동적/정적으로 생성하고 교체하는 방식으로 사용된다. 프레임워크에서 유연성이 있는 동작을

위해 자주 사용되는 방식이기도 하다.



3. 
리플렉션을 통해 얻을 수 있는 정보

리플렉션을 통해 얻을 수 있는 정보에대해서 알아보자.

 

ClassName

 

Class Modifiers (public, private, synchronized 등)

 

Package Info

 

Superclass

 

Implemented Interfaces

 

Constructors

 

Methods

 

Fields

 

Annotations

 



이외에도 더 많은 클래스 정보를 얻을 수 있다.java.lang.Class 클래스에 대한 JavaDoc 문서를 참고하자

3.1 Class Object

클래스 정보를 얻기 위해 가장 먼저해야할 일은 정보를 담고 있는 java.lang.Class 객체를 획득하는 것이다.
프리미티 타입과 배열 타입을 포함하여 자바의 모든 타입들은 연관된 Class 객체를 가지고있으며, 컴파일 타임에
 
클래스의 이름을 알수 있다면, 다음과 같이 Class 객체를얻을 수 있다.

 ClassmyObjectClass = MyObject.class

 

컴파일 타임에 이름을 알수 없다면, 런타임에 문자열로 된 이름으로부터 클래스 객체를 아래와 같이 얻을 수 있다.

String className =  ... // 클래스 풀네임

 

Class myObjectClass = Class.forName(className);

 

이때 문자열로 된 클래스 이름은 패키지경로까지 포함한 풀네임이여야 하며, 해당 패키지에 클래스가 존재하지 않으면
Class.forName 메소드는 ClassNotFoundException 예외를 던지게된다.



3.1 Class Name

Class객체로부터 2가지버전의 클래스 이름을 얻을 수 있다. getName() 메소드를 사용하면 패키지까지 포함한 풀네임을얻을 수
있고, getSimpleName() 을 사용하여 패키지가 포함되지 않은 클래스 이름을 얻을수 있다.

// 클래스 풀네임

 

Class aClass = ... // 이전에 얻은 클래스객체

 

String className = aClass.getName();

 

 

// 클래스 심플 네임

 

Class  aClass =... // 이전에 얻은 클래스 객체

 

String simpleClassName = aClass.getSimpleName();

 



3.2 Modifier

Class객체로부터 변경자에 접근할 수 있다. 클래스 변경자는 public, private, static 과 같은키워드를 의미한다.

클래스에 대한 플래그 비트가 설정된 int 값을 얻을 수 있으며 java.lang.reflect.Modifier 클래스에있는 메소드를 
통해 해당 플래그가 켜져있는지 확인할 수 있다.

// 변경자 얻기

 

Class  aClass =... // 이전에 얻은 클래스 객체

 

int modifiers = aClass.getModifiers();

 

 

// 변경자 플래그 확인 메소드들

 

Modifier.isAbstract(int modifiers)

 

Modifier.isFinal(int modifiers)

 

Modifier.isInterface(int modifiers)

 

Modifier.isNative(int modifiers)

 

Modifier.isPrivate(int modifiers)

 

Modifier.isProtected(int modifiers)

 

Modifier.isPublic(int modifiers)

 

Modifier.isStatic(int modifiers)

 

Modifier.isStrict(int modifiers)

 

Modifier.isSynchronized(int modifiers)

 

Modifier.isTransient(int modifiers)

 

Modifier.isVolatile(int modifiers)

 



3.3 Package Info
다음과 같이 Class 객체로부터 패키지에 대한 정보를얻는다.

// 패키지 정보 얻기

 

Class  aClass =... // 이전에 얻은 클래스 객체

 

Package package = aClass.getPackage();

 

 

Package객체로부터 패지지 이름과 같은 정보에 접근할 수 있다. 또한 패키지가 위치한 classpath에 있는jar 파일의 
Manifest 파일에서도 이 패키지에 대한 특정한 정보를 얻을 수 있다

(예를 들면 Manifest 파일에 지정된 패지키 버전 번호 같은...)



3.4 Superclass
아래와 같이 수퍼클래스의 class 객체를 얻을 수 있다.

// 수퍼 클래스의 class 객체 얻기

 

Class superclass = aClass.getSuperclass();

 



3.5 Implemented Interfaces
클래스 객체에 의해 구현된 인터페이스의 목록을 얻어보자.

// 구현한 인터페이스 목록 얻기

 

Class  aClass =... // 이전에 얻은 클래스 객체

 

Class[] interfaces = aClass.getInterfaces();

 

 

수퍼클래스가 구현한 인터페이스지만, 자식클래스가 특별히 해당 인터페이스를 구현한다고 명시하지 않으면
해당 인터페이스는 목록에 포함되지 않는 것에 주의하자. 구현하는 완전한 인터페이스의 목록을얻기 위해서는
 
자신의 수퍼클래스의 구현 인터페이스 목록을 재귀적으로 확인해야 한다.



3.6 Constructors
다음과 같이 클래스의 생성자 목록에 접근한다.

// 클래스 생성자 목록 얻기

 

Constructor[] constructors =aClass.getConstructors();

 




3.7 Methods 
다음과 같이 클래스의 메소드들에 접근한다.

// 메소드 목록 얻기

 

Method[] methods = aClass.getMethods();

 




3.8 Fields
다음과 같이 클래스의 멤버 변수들에 접근한다.

// 필드 목록 얻기

 

Field[] fields = aClass.getFields();

 


3.9 Annotations
다음과 같이 클래스의 어노테이션에 접근한다.

// 어노테이션 목록 얻기

 

Annotation[] annotations = aClass.getAnnotations();

 


※ 
리플렉션을 사용하여 Annotation을 처리하는 것은아래 포스트를 참고!!
Java-어노테이션(Annotation)
Java-어노테이션 사용하기


4. 
리플렉션 사용 예

게시물 정보를 갖는 Board 클래스에 대한 정보를 리플렉션을 사용하여 출력하는 예를 보자
Board 클래스에 대한 정의는 아래와 같다.

package com.tistory.hiddenviewer.reflection;

 

import java.awt.event.ActionEvent;

 

import java.awt.event.ActionListener;

 

import java.util.ArrayList;

 

public class Board implements ActionListener{

 

         publicfinal static String boardName = "MyBoard";

 

         publicArrayList boardList;

 

        

 

         publicint seq;

 

         protectedString title;

 

         privateString contents;

 

        

 

         publicBoard() {

 

                 this(10);

 

         }

 

        

 

         publicBoard(int count) {

 

                 this.boardList= new ArrayList(count);

 

         }

 

        

 

        

 

         publicint getSeq() {

 

                 returnseq;

 

         }

 

         publicvoid setSeq(int seq) {

 

                 this.seq= seq;

 

         }

 

         public StringgetTitle() {

 

                 returntitle;

 

         }

 

         publicvoid setTitle(String title) {

 

                 this.title= title;

 

         }

 

         publicString getContents() {

 

                 returncontents;

 

         }

 

         publicvoid setContents(String contents) {

 

                 this.contents= contents;

 

         }

 

         @Override

 

         publicvoid actionPerformed(ActionEvent e) {

 

                 //TODO Auto-generated method stub

 

                

 

         }

 

}

 



Board 
클래스에 대한 정보를 리플렉션을 사용하여 출력하였다.

package com.tistory.hiddenviewer.reflection.executor;

 

import java.io.BufferedReader;

 

import java.io.IOException;

 

import java.io.InputStreamReader;

 

import java.lang.annotation.Annotation;

 

import java.lang.reflect.Constructor;

 

import java.lang.reflect.Field;

 

import java.lang.reflect.Method;

 

import java.lang.reflect.Modifier;

 

public class BoardReflectionExecutor {

 

         publicstatic void main(String[] args) throws IOException, ClassNotFoundException {

 

                

 

                 BufferedReaderbr = new BufferedReader(new InputStreamReader(System.in));

 

                

 

                 System.out.print("생성할 클래스 이름을 입력하세요(패키지 포함): ");

 

                 StringclassName = br.readLine();

 

                

 

                 Classcls = Class.forName(className);

 

                

 

                 //클래스 이름얻기

 

                 StringclassFullName = cls.getName();

 

                 StringclassSimpleName = cls.getSimpleName();

 

                

 

                 System.out.println("classfull name: " + classFullName);

 

                 System.out.println("classsimple name: " + classSimpleName);

 

                

 

                 //변경자 얻기

 

                 intmodifiers = cls.getModifiers();

 

                

 

                 if(Modifier.isPublic(modifiers)) {

 

                          System.out.println("classis public class");

 

                 }

 

                 if(Modifier.isFinal(modifiers)) {

 

                          System.out.println("classis final class");

 

                 }

 

                 //패키지 얻기

 

                 Packagepkg = cls.getPackage();

 

                 System.out.println("packagename: " + pkg.getName());

 

                

 

                

 

                 //수퍼클래스 얻기

 

                 ClasssuperCls = cls.getSuperclass();

 

                 System.out.println("superclass name :" + superCls.getName());

 

                

 

                

 

                 //구현 인터페이스 목록 얻기

 

                 Class[]interfaces = cls.getInterfaces();

 

                 for(Class cs : interfaces) {

 

                          System.out.println("thisclass implements " + cs.getName() + " interface");

 

                 }

 

                

 

                 //생성자 목록 얻기

 

                 Constructor[]conturctors = cls.getConstructors();

 

                 for(Constructor constructor : conturctors) {

 

 

                          System.out.println("Constructor:" + constructor.getName());

 

                 }

 

                //메서드 목록 얻기

                 Method[]methods = cls.getMethods();

 

                 for(Method method : methods) {

                          System.out.println(method.getReturnType()+ " " + method.getName() + "(...)");

                 }

 

                 //프로퍼티 목록 얻기

 

                 Field[]fields = cls.getFields();

 

                 for(Field field : fields) {

 

                          System.out.println(field.getType()+ " " + field.getName());

 

                 }

 

                 //어노테이션 얻기

 

                 Annotation[]annotations = cls.getAnnotations();

 

                 for(Annotation annotation : annotations) {

 

                          System.out.println(annotation.toString());

 

                 }

 

         }

 

}

 



출력결과

 

///////////////////////////////////////////////////////////////////////////////////////////


(2) 리플렉션(Reflection) 사용하기

 

지난 포스팅에 이어 리플렉션을 사용하여객체를 생성하고, private 필드에 접근하는 방법 그리고 메서드를 호출하는 
방법에 대해 알아보자.



1. 리플렉션을 사용하여 객체생성

리플렉션을 사용하여 런타임에 클래스에생성자들을 검사하고, 생성자 객체를 통해 객체를 생성하는 과정을 알아보자. 
아래와 같이 3가지 단계를 거치게 되며 가장 먼저 클래스의 생성자 객체 java.lang.reflect.Consturctor 를 얻어야 한다.

 

1.1 Constructor 객체 획득하기
다음과 같이 Class 객체로부터 Constructor 클래스를 얻는다.

// 생성자 목록 얻기

 

Class aClass = ... // 이전에 얻은 클래스객체

 

Constructor[] constructors = aClass.getConstructors();

 



Constructor[] 
배열은 클래스에 선언된 모든public 생성자의 Constructor 인스턴스를 가집니다. 특정한 파라미터를 갖는 
특정한 생성자는 다음과 같이 얻을 수 있다.

// 특정 파라미터를 갖는 생성자 얻기

 

Class aClass = ... // 이전에 얻은 클래스객체

 

Constructor constructor =

 

aClass.getConstructor(new Class[]{String.class});

 



위 예는 하나의 String 타입 파라미터를 갖는 생성자를반환하는데, 일치하는 파라미터를 갖는 생성자가 없으면 
NoSuchMethodException 예외가 발생한다.

1.2 Constructor 파라미터 얻기 
다음과 같이 생성자에 포함된 파라미터 타입 목록을 얻을수 있다.

// 생성자의 파라미터 타입목록 얻기

 

Class aClass = ... // 이전에 얻은 클래스객체

 

Class[] parameterTypes =constructor.getParameterTypes();

 



1.3 Constructor 객체를 사용하여 객체 생성하기
다음과 같이 생성자 객체로부터 객체를 생성한다.

// 하나의 String 파라미터를 갖는 생성자를 얻는다.

 

Constructor constructor =MyObject.class.getConstructor(String.class);

 

// 생성

 

MyObject myObject = (MyObject)constructor.newInstance("constructor-arg1");

 


Constructor.newInstance() 
메서드는 선택적인 개수의 파라미터를취한다. 하지만 반드시 해당 생성자에 맞는 개수와 타입의

파라미터를 제공해야 한다는 것에 주의하자.





2. 리플렉션을 사용하여 Field 에 접근하기

리플렉션을 사용하여 클래스의 모든멤버 변수를 검사할 수 있으며, 런타임에 값을 얻어오거나 설정할 수 있다.

이때 하나의 프로퍼티 당 하나의 java.lang.reflect.Field 클래스 객체를 사용하게 된다.

 

2.1 Field 객체 얻기

다음과 같이 Field 객체를 얻는다.

// 클래스에 선언된 public 속성의 Field 객체얻기

 

Class aClass = ... // 이전에 얻은 클래스객체

 

Field[] fields = aClass.getFields();

 

 

Field[]배열은 클래스에 선언된 각 public field 당 하나의 Field 객체를 갖는다.(public field 만을 갖는다는 것에 주의)
접근하려는 필드의 이름을 안다면, 다음과 같이 접근할 수 있다.

// Field에 접근하기

 

Class  aClass =MyObject.class

 

Field field = aClass.getField("someField");

 

 

위 예제는 아래 MyObject 에 선언된 someField 에 대응하는 Field 인스턴스를 반환합니다.

public class MyObject{

 

public String someField = null;

 

}

 


getField() 
메서드의 파라미터에 해당하는 이름의 필드가 클래스에 존재하지않으면 NoSuchFieldException 예외가발생한다.

2.2 Field 이름 얻기

Field인스턴스를 획득하면, 다음과같이 Field.getName()을 사용하여 이름을 얻을 수 있다.

// Field 이름 얻기

 

Field field = ... // Field 객체를 얻는다.

 

String fieldName = field.getName();

 

 

2.3 Field 타입 얻기

Field.getType()메서드를 사용하여 필드의 타입을 얻을 수 있다.

// Field 타입 얻기

 

Field field = aClass.getField("someField");

 

Object fieldType = field.getType();

 

 

2.4 Field 값 조회하고 설정하기 
Field 
에 대한 참조를 얻게되면, Field.get(), Field.set() 메소드를 사용하여 값을 얻거나 설정할수 있다.

// Field 값 설정하고 조회하기

 

Class  aClass =MyObject.class

 

Field field = aClass.getField("someField");

 

MyObject objectInstance = new MyObject();

 

Object value = field.get(objectInstance);

 

field.set(objetInstance, value);

 


field.get(), set() 
메서드에는 해당 필드를 소유하는 객체가 인자로전달되야 하며 만일 static 필드라면 null을전달한다.

2.5 Private Field 에 접근하기

클래스의 Private 필드는 외부클래스에서 접근 할 수 없지만, 리플렉션을사용하면 접근이 가능하다. 캡슐화를 깨는 동작일 
수 있지만, 단위테스트와 하이버네이트와 같은 프레임워크에서 유용하게 사용되기도 한다. 
private 필드에 접근하기 위해서는 Class.getDeclaredField(Stringname)와 Class.getDeclaredFields() 메서드를 사용한다. (Class.getField(String name) 와Class.getFields() 메서드는 public 필드만을 반환한다.)


다음은 privatefield에 접근하는 예이다.

 

Board board = new Board();

 

board.setContents("test contents...");

 

                

 

Field field = cls.getDeclaredField("contents");

 

field.setAccessible(true);

 

String contents = (String) field.get(board);

 

                

 

System.out.println("Private Contents Field:" + contents);

 


Field 
객체의 setAccessible(true)를 호출하지 않고, private 필드값을 조회하려고 하면 IllegalAccessException 예외가 발생한다. 




3. 리플렉션을 사용하여 Method 호출하기

java.lang.reflect.Method클래스를 사용하여 메서드를 검사하고 호출 하는 방법을 알아보자.



3.1 Method 객체 얻기

Method클래스를 다음과 같이 획득한다.

// Method 객체목록 얻기

 

Class aClass = ... // 이전에 얻은 클래스객체

 

Method[] methods = aClass.getMethods();

 

 

역시 클래스에 선언된 public 메서드당 하나의 Method 인스턴스를 갖으며, 메서드의 구체적인 파라미터 타입들을 알고 있으면 
해당 메서드의 인스턴스를 얻을 수 있다. 다음은String 파라미터 하나를 갖는 doSomething 메소드의 Method 객체를 얻는다.

// 파라미터를 갖는 메소드의 Method 객체 얻기

 

Class aClass = ... // 이전에 얻은 클래스객체

 

Method method = aClass.getMethod("doSomething",new Class[]{String.class});

 

 

만일 일치하는 메서드가 없으면, NoSuchMethodException이 발생한다. 파라미터가없는 메서드를 얻기 위해서는 파라미터 
배열 대신 null을 전달한다.

// 파라미터가 없는 메서드의 Method 객체 얻기

 

Class aClass = ... // 이전에 얻은 클래스객체

 

Method method =aClass.getMethod("doSomething", null);

 



3.2 Method 파라미터와 반환값 얻기

다음과 같이 해당 메서드의 파라미터들을얻을 수 있다.

// 메서드의 파라미터 타입목록 얻기

 

Method method = ... //

 

Class[] parameterTypes = method.getParameterTypes();

 


메서드의 반환타입은 다음과 같이 접근한다.

// 메서드의 반환값 타입 얻기

 

Method method = ... //

 

Class returnType = method.getReturnType();

 



3.3 Method 객체를 사용하여 메서드 호출하기(Invoking)

다음과 같이 메서드를 호출할 수 있다.

// 메서드 호출

 

Method method =MyObject.class.getMethod("doSomething", String.class);

 

Object returnValue = method.invoke(null,"parameter-value1");

 

 

invoke()메소드에는 호출하기를 원하는 객체를 전달하며, static 메서드이면 null을 대신 전달한다. 
Method.invoke(Object target, Object...parameters) 메서드는 선택적인 개수의 파라미터를 취하지만, 메서드가 필요로 하는 
정확한 개수의 파라미터를 전달해야 한다. 

3.4 Private Method 에 접근하기 
Private Field 
에 접근하는 것과 유사하게 Class.getDeclaredMethod(String name)와 Class.getDeclaredMethods()메서드를 사용한다. 
(Class.getField(String name) 와 Class.getFields() 메서드는 public 필드만을 반환한다.)


/////////////////////////////////////////////////////////////////////////////////////////////////


리플렉션으로 Getter 와 Setter 검사하기

 

리플렉션을 사용하여 해당 클래스가 어떤 getter와 setter를 갖는지 검사하는 예를 알아보자.



클래스에 getter와setter 메서드만을 검사할 수는 없으며, 모든 메서드들을 스캔하여 getter인지 setter인지를 확인해야 한다.

getter와 setter 메서드가나타내는 특성은 다음과 같다.

 

Getter 는 “get” 이라는 이름으로 시작하며, 파라미터가 없고 하나의 값을 반환한다.

Setter 는 “set” 이라는 이름으로 시작하며, 하나의 파라미터를 취한다.


몇몇 setter는 값을 반환하지 않을 수도 있고 또 어떤 것은집합값을 반환하거나 어떤 것들은 메서드 chaining을 위해 
값을 반환할 수 있기 때문에 setter의 반환타입에 대한 가정을 해서는 안된다.
다음은 getter와 setter를 찾아 출력하는예이다.

package com.tistory.hiddenviewer.reflection.executor;

 

import java.lang.reflect.Method;

 

import com.tistory.hiddenviewer.reflection.Board;

 

public class GetterSetterPrinter {

 

        publicstatic void main(String[] args) {

 

               printGettersSetters(Board.class);

 

        }

 

        publicstatic void printGettersSetters(Class aClass){

 

               Method[]methods = aClass.getMethods();

 

               for(Methodmethod : methods){

 

                       if(isGetter(method)){

 

                               System.out.println("getter:" + method);

 

                       }

 

                       if(isSetter(method)){

 

                               System.out.println("setter:" + method);

 

                       }

 

               } //for

 

        }

 

       

 

        publicstatic boolean isGetter(Method method){

 

               //get 으로 시작하지 않으면 반환

 

               if(!method.getName().startsWith("get")){

 

                       returnfalse;

 

               }

 

               // 파라미터가 있으면 반환

 

               if(method.getParameterTypes().length!= 0) {

 

                       returnfalse;

 

               }

 

               // 반환값이 없으면 반환

 

               if(void.class.equals(method.getReturnType())){

 

                       returnfalse;

 

               }

 

               returntrue;

 

        }

 

        publicstatic boolean isSetter(Method method){

 

               //set 으로 시작하지 않으면 반환

 

               if(!method.getName().startsWith("set")){

 

                       returnfalse;

 

               }

 

               // 파라미터가 개수가 1이 아니면 반환

 

               if(method.getParameterTypes().length!= 1) {

 

                       returnfalse;

 

               }

 

               returntrue;

 

        }

 

}

 



출력결과



출처 - http://blog.naver.com/PostView.nhn?blogId=kimgas2000&logNo=90133009397







제공 : 한빛 네트워크
저자 : Russ Olsen
역자 : 백기선
원문 : Reflections on Java Reflection 

일상에서의 리플렉션(reflection)이란 거울 속에 비친 자신의 모습입니다. 프로그래밍 세상에서의 리플렉션은 프로그램이 자신의 모습을 보고 심지어 수정하기 까지 하는 것을 말합니다. Java reflection API는 바로 그런 기능을 언어의 기본 요소인 클래스, 필드, 메소드를 들여다 볼 수 있는 평범한 Java API를 통해 제공합니다. 리플렉션을 이해하는 것은 여러분이 자주 사용하는 툴을 이해하는데 도움이 됩니다. Eclipse가 어떻게 자동완성으로 메소드 이름을 만들어 줄까? Tomcat은 web.xml파일에 있는 클래스 이름을 가지고 웹의 요청을 처리할 서블릿을 실행하는 걸까? Spring은 어떻게 마술 같은 dependency injection을 하는 것일까? 여러분의 프로그램에서도 리플렉션을 사용하여 보다 동적이고 유연한 코드를 작성하실 수 있습니다. 리플렉션을 사용하면 이전에 본적 없는 클래스들을 매우 멋지게 처리할 수 있습니다. 

클래스 만들기 

이미 말했듯이 리플렉션의 기본 아이디어는 프로그램이 동작하는 내부에 집어 넣을 API를 제공하는 것입니다. Java에서 가장 기본이 되는 사상이 바로 클래스기 때문에(클래스 없이 자바 프로그램을 만들어 보세요) Class 클래스부터 살펴보는 것이 좋겠습니다. 이것의 객체는 Class 타입일 것입니다. 일단 Class객체를 가지게 되면 그것으로부터 클래스에 관련된 모든 정보를 뽑아낼 수 있습니다. 클래스의 이름, 그것이 public 인지 abstract 인지 final 인지 그리고 심지어 상위 클래스까지 말이죠. 

이 정도면 이론은 충분합니다. 자, 이제 리플렉션 현미경을 가지고 아래에 있는 매우 간단한 Employee 클래스를 살펴봅시다.

package com.russolsen.reflect;

public class Employee
{
   public String _firstName;
   public String _lastName;
   private int _salary;

      public Employee()
   {
      this( "John", "Smith", 50000);
   }
 
   public Employee(String fn, String ln, int salary)
   {
      _firstName = fn;
      _lastName = ln;
      _salary = salary;
   }
   
      public int getSalary()
   {
      return _salary;
   }
   
   public void setSalary(int salary)
   {
      _salary = salary;
   }
   
   public String toString() 
   {
      return "Employee: " + _firstName +  " "
             + _lastName + " " + _salary;
   }

}

Class 객체를 만드는 가장 쉬운 방법은 해당 클래스 객체의 getClass 메소드를 호출하는 것입니다. 아래에 있는 코드는 Employee 객체를 만들고 그것의 Class 객체를 만들어서 클래스에 대한 다양한 정보들을 출력합니다.

package com.russolsen.reflect;

import java.lang.reflect.Modifier;

public class GetClassExample
{
   public static void main(String[] args)
   {
 
      Employee employee = new Employee();
      
      Class klass = employee.getClass();
      
      System.out.println( "Class name: " + klass.getName());
      System.out.println( 
            "Class super class: " + klass.getSuperclass());
      
      int mods = klass.getModifiers();
      System.out.println( 
            "Class is public: " + Modifier.isPublic(mods));
      System.out.println( 
            "Class is final: " +  Modifier.isFinal(mods));
      System.out.println( 
            "Class is abstract: " + Modifier.isAbstract(mods)); 

   }

}

코드를 실행하면 다음과 같은 결과를 확인할 수 있습니다.

Class name: com.russolsen.reflect.Employee
Class super class: class java.lang.Object
Class is public: true
Class is final: false
Class is abstract: false

예제에서 보이듯이 클래스의 이름과 상위 클래스를 알아내는 것은 다른 접근 메소드들(Getters or Accessors)을 호출하는 것처럼 쉬운 일입니다. 만약 해당 클래스가 public 인지 abstract 인지 final 인지 알고 싶다면 약간 복잡해 집니다. 이 모든 정보가 getModifires에 의해 하나의 int 값으로 패키징되어 넘어오기 옵니다. 다행히 Java는 Modifier 클래스를 통해 getModifiers에서 넘어온 숫자를 가지고 여러 일을 할 수 있는 static 메소드들을 제공해 줍니다. 

객체에 getClass를 호출하는 것 만이 Class 객체를 얻을 수 있는 유일한 방법은 아닙니다. 클래스 이름을 사용하여 직접 얻을 수도 있습니다.

Class klass = Employee.class;

세 번째 방법은 좀 더 흥미로운 방법입니다. 문자열을 통해서 Class 객체를 생성할 수 있습니다. 물론 그 문자열이 클래스 이름을 포함하고 있을 때 말이죠. 다음과 같이 Class 클래스에 있는 forName 을 호출하여 얻을 수 있습니다.

      Class klass = Class.forName("com.russolsen.reflect.Employee");
      
      System.out.println( "Class name: " + klass.getName());
      System.out.println( 
            "Class super class: " + klass.getSuperclass());
      
      // Print out the rest...

forName을 사용할 때 한 가지 주의할 것은 클래스 이름 앞에 완전한 패키지 경로를 붙여줘야 한다는 것입니다. 평범하고 늙은 “Employee” 말고 “com.russolsen.reflect.Employee" 여야만 합니다. forName을 통해 리플렉션 API의 기본적인 강력함(그리고 멋진 것)을 살펴봤습니다. 클래스 이름을 포함한 문자열로 시작할 수도 있고 class로 끝낼 수도 있습니다. 

바로 인스턴스 만들기 

class 객체를 가져오고 그것에 관한 정보를 찾는 것은 그것 자체로도 흥미롭고 유용합니다. 하지만 진짜 재미있는 것은 리플렉션을 사용하여 무언가를 실제 해보는 것입니다. Class 객체를 가지고 할 수 있는 가장 눈에 띄는 작업은 그 클래스의 새로운 객체를 만드는 것입니다. newInstance 메소드를 사용하여 간단하게 만들 수 있습니다. 실제 사용하는 것을 보여 주기 위하여 아래에 있는 간단한 프로그램은 커맨드 라인 인자(command line argument)로 Class 객체를 만들고 그 클래스 타입의 객체를 만드는 프로그램을 보여줍니다.

package com.russolsen.reflect;

public class NewInstanceExample
{
   public static void main(String[] args)
      throws ClassNotFoundException,
      InstantiationException, IllegalAccessException
   {

      Class klass = Class.forName(args[0]);
      Object theNewObject = klass.newInstance();
      System.out.println("Just made: " + theNewObject);
   }
}

위 코드를 "com.russolsen.reflect.Employee" 인자와 함께 실행하면 새로운 Employee 객체를 만들게 됩니다.

Just made: Employee: John Smith 50000

Run it again, but this time feed it "java.util.Date" and you will get:

Just made: Tue Feb 27 20:25:20 EST 2007

간단한 코드 몇 줄로 얼마나 많은 유연성을 얻게 되었는지 생각해보세요. 위에 있는 프로그램은 실제 Employee 나 Date 클래스에 관해 아는 것이 하나도 없지만 각각의 새로운 객체들을 만들 수 있습니다. 이것이야 말로 Java 프로그래밍을 하는 또 다른 방법입니다. 

인자 없는 생성자 너머에 

Class.newInstance 메소드를 호출하는 것은 인자 없이 new를 사용하는 것과 동일합니다. 그러나 만약 인자가 없는 생성자 즉 default 생성자가 없는 클래스에 newInstance를 호출하면 어떻게 될까요? 좋을 일 없습니다. 별로 맘에 안 드는 InstantiationException을 받게 됩니다. 

좋은 소식이 있습니다. 생성자 인자를 필요로 하는 클래스의 객체를 동적으로 만들 수 있습니다. 하지만 약간 더 힘든 작업을 해야 합니다. 그건 바로 클래스에서 해당 생성자를 찾고 적당한 인자를 사용하여 그것을 호출하는 일입니다. 적당한 생성자를 찾는 일은 여러분이 찾고자 하는 생성자에 대한 정보를 가지고 getConstrucor 메소드를 사용하면 됩니다. 그럼 Constuctor 객체를 얻게 되고 그것을 사용하여 새로운 객체를 만들 수 있습니다.

Let's see how this all works in code:
      Class klass = Class.forName("com.russolsen.reflect.Employee");

      Class[] paramTypes = {
            String.class, 
            String.class, 
            Integer.TYPE };
      
      Constructor cons = klass.getConstructor(paramTypes);
      
      System.out.println( "Found the constructor: " + cons);

      
      Object[] args = { 
            "Fred", 
            "Fintstone", 
            new Integer(90000) };
      
      Object theObject = cons.newInstance(args);
      System.out.println( "New object: " + theObject);

생성자들 사이의 차이점은 오직 그것들이 가지고 있는 매개 변수들입니다. 따라서 getConstructor 메소드에 찾고자 하는 생성자에 들어갈 매개변수 각각의 Class들 객체의 배열을 넘겨줍니다. 위에 있는 예제에서는 두 개의 String 그리고 하나의 int를 받는 생성자를 찾게 됩니다. Constructor 객체를 얻은 뒤 새로운 객체를 생성하는 일은 간단합니다. 실제 인자로 들어갈 객체의 배열을 newInstance 메소드를 호출하면서 넘겨주면 됩니다. 

geConstructor에는 조그만 지뢰가 하나 있습니다. 파라미터의 타입으로 생성자를 식별하여 원하는 것을 찾을 때 primitive 인자와 그것의 wrapper 클래스를 잘 구별해야 합니다. 생성자가 인자로 primitive 타입인 int를 받는 것인가 아니면 그것의 삼촌 격인 java.lang.Integer 클래스의 객체를 받는 건가요? 만약 java.lang.integer 같은 wrapper 타입의 객체를 받는 생성자라면 Integer.class처럼 wrapper 클래스를 사용하면 됩니다. primitive 타입인 int를 사용하는 생성자라면 Integer.TYPE을 사용합니다. TYPE은 primitive을 위해 마련한 것입니다. 모든 wrapper 클래들은 static 타입인 TYPE 필드를 가지고 있고 이것을 사용하여 primitive 타입이라는 것을 알려줄 수 있습니다. 역자 백기선님은 AJN(http://agilejava.net)에서 자바 관련 스터디를 하고 있는 착하고 조용하며 점잖은 대학생입니다. 요즘은 특히 Spring과 Hibernate 같은 오픈소스 프레임워크를 공부하고 있습니다. 공부한 내용들은 블로그(http://whiteship.tistory.com)에 간단하게 정리하고 있으며 장래 희망은 행복한 개발자입니다.



출처 - http://www.hanb.co.kr/network/view.html?bi_id=1369

'프로그래밍 > It 용어' 카테고리의 다른 글

IT에서 IP란?? ( IP - 지적재산권 )  (0) 2016.09.27
기본용어  (0) 2016.09.27
스프링 프레임워크 ( Spring Framework )  (0) 2016.09.26
퍼시스턴스 프레임워크  (0) 2016.09.26
iBatis(아이바티스)란?  (0) 2016.09.26
Posted by 당구치는 개발자
|
프로그래밍/It 용어 2016. 9. 26. 11:35



---------------------------------------------------------------------------------------------------------------



[이게 필자가 알고 있는 컨테이너]

토비의 스프링은 다음과 같이 말한다. 스프링은 거대한 컨테이너임과 동시에 Ioc/DI를 기반으로 하고 있는 거룩한 존재이며 서비스 추상화를 통해 삼위일체로 분리되는 3단 변신로봇이라고 한다. 이럴수가! 뭔말하는지는 하나도 모르겠지만 일단 말만 들어도 엄청난데다 가격까지 공짜다. 게다가 이걸 쓰는 사람들마다 칭찬 또 칭찬 일색이니 궁금해서 참을 수가 없다.



근데 말이다…. 필자는 스프링의 지독한 뉴비이므로 여기서 뉴비답게 눈치없게 한번 굴어보려 한다. 일단 스프링이 대단하고 무지 엄청나다는 건 알겠는데…. 컨테이너는 뭐고 IoC/DI는 뭐란 말인가? 평생 살면서 컨테이너란 위의 사진같은 것 밖에 모르는데… 그리고 서비스 추상화? 난 아직 서비스가 뭔지도 모르겠다.


컨테이너란?

컨테이너란 당신이 작성한 코드의 처리과정을 위임받은 독립적인 존재라고 생각하면 된다. 컨테이너는 적절한 설정만 되있다면 누구의 도움없이도 프로그래머가 작성한 코드를 스스로 참조한 뒤 알아서 객체의 생성과 소멸을 컨트롤해준다. 거기다 컨테이너를 이용함으로써 얻게되는 이득은 어마어마 하며, 이런 강력한 기능을 무료로 이용할 수 있다는 점 또한 엄청나다.

허나 소수의 독립심 강하고 줏대있는 사람들은 내가 잘 알지도 못하는 프로그램을 무작정 써야 한다는 것에 반감을 살 수도 있다. 게다가 프로그래머가 다른 라이브러리에 의존하지 않고 직접 코드를 작성해나간다면 완성된 프로그램에 대한 이해도 또한 상상을 초월할 것이다. 혹은 인간이 그렇게 줏대없이 프로그램만 의존하다가 터미네이터나 매트릭스 같은 세상이 되버리면 어떡하냐고 필자에게 따질 수도 있는 것이다.

만약 이러저러한 이유로 컨테이너를 거부하겠다면 필자는 굳이 사용하지 않아도 상관은 없다고 말하겠다. 진심으로 실력만 된다면사용자가 직접 컨테이너를 만들어써도 상관은 없다.

그러나 직접 컨테이너를 구현해보고자 키보드에 손을 올렸다면 아마 배보다 배꼽이 더큰 컨테이너의 어마어마한 기능에 입이 쩍벌어질 것이다. 빈정상하라고 하는 말은 아니지만 사실 당신이 구현하고자 하는 코드와 컨테이너의 구현 코드는 비교도 안될 정도로 수준차가 어마어마하다. 그러므로 코드의 이해가 당장 힘들어지고 뭐가뭔지 모르겠더라도 일단 막강한 기능을 제공하는 컨테이너를 이용하기로 하자.

다시 말하지만 프로그래머가 작성한 코드는 컨테이너를 사용하게 됨으로서 프로그래머의 손을 떠나 컨테이너의 영역으로 떠나버리게 된다. (정확히 말하자면 컨테이너가 맘대로 객체를 생성하는 게 아니라 프로그램을 이용하는 이용자의 호출에 의해 컨테이너가 동작하게 되는 구조이다.) 여하튼 컨테이너를 이용한다면 우리의 골치아픈 문제는 다 컨테이너 알아서 해주므로 쓸데없는 버그나 난해한 과제들에 골치 아파질 이유가 없어진다. 당신은 컨테이너의 기능을 이용하고 사용 중 오류가 있으면 일단 무조건 개발사 탓을 하면 되므로 회사에서 짤릴 가능성까지 어느 정도 낮아진 셈이다!

내친 김에 당신이 필자와 같은 初뉴비라면 임기응변을 위해서라도 반드시 스프링의 개발자와 자바의 개발자 정도가 누군지는 알아두어야 한다. 이런걸 미리미리 알아둬야 나중에 직장 상사가 괜한 버그로 트집을 잡을 때 무조건 이 사람들 탓으로 돌릴 수 있게 된다.

자바의 아버지, 제임스 고슬링 - 선 마이크로시스템즈라는 회사와 함께 자바라는 언어를 탄생시키는데 혁혁한 공을 세운 인물이며 SUN이 오라클로 인수된 뒤 얼마 지나지 않아 회사를 퇴임하고 현재 구글에서 근무 중이다. 개인적으로 C와 UNIX를 만든 데니스 리치 다음으로 훌륭한 프로그래머라고 생각한다.
스프링 혁명가, 로드 존슨 - 프로그래밍 계의 락스타같은 존재라 할 수 있다. 그가 한 유명한 말로 "항상 프레임워크 기반으로 접근하라"라는 말이 있는데 이 말은 곧 당신이 한 클래스에서 DB에 넣고 빼는 등 온갖 짓거리로 코드를 짜고 있다면 당신이 어디가서 프로그래밍의 프자도 꺼내서는 안된다는 말이다.

당신은 이 서블릿 클래스가 몇시몇분몇초에 호출될지 정확히 알고 있는가?

다시 본론으로 돌아와 스프링과 더불어 대표적인 컨테이너를 예로 들자면 톰캣과 같은 WAS(Web Application Service)를 들 수 있다. 우리가 열심히 작성한 서블릿을 써줄지 말지는 톰캣이 결정하므로 당연히 컨테이너 아닌가. 혹여나 그래도 이해가 가지 않는다면… 그냥 당신이 쓰고 있는 코드가 도통 언제 호출될지도 감이 안오는 막막한 상태라면 올바르게 컨테이너를 이용하고 있다고 하자. 그런 의미에서 컨테이너는 당신의 사수나 수석 프로그래머라고 할 수 있다. 내가 쓴 코드를 그 사람들이 써줄지 말지는 모르는 일이니 말이다.


IoC/DI란?

IoC/DI란 Inversion of ControlDependency Injection의 줄임말이다. 한글로 번역하면 제어의 역전, 의존적 주입이라고 하는데 목에 힘을 좀 빼고 솔직히 말하자면 살다살다 한글로 번역까지 했는데 이해 못할 말이 튀어나올 줄은 몰랐다. 그래서 나름 좀… 쪽팔려도 뉴비의 표현으로 바꾸자면 "대신 해줌(IoC)"과 "미리 찜해 놓음(DI)"라고 잠시 명칭을 정정하도록 하겠다.

일단 IoC란 무엇인가? 당신이 위의 컨테이너의 설명글을 꼼꼼히 읽어왔다면 IoC는 지금 당신이 생각하고 있는게 맞다. 바로 컨테이너다. 컨테이너는 당신이 작성한 코드 컨트롤(객체의 생성과 소멸)을 대신 해주는데 IoC덕분에 우리는 미친 사람마냥 언제 호출될지도 모르는 코드를 마음껏! 칠 수 있게 되었다.

사실 IoC는 스프링만의 코딩방식은 아니고 다른 프레임워크에서도 사용되는 방식인데 굳이 스프링만 우리는 IoC 방식이라고 목이 힘주어 강조하는 이유는 스프링은 정말 철저하게 IoC 방식을 써먹었다는 것이다. 그러므로 우리가 여기서 얻을 수 있는 결론은 어디가서 스프링 좀 안다고 IoC가 스프링만의 고유한 방식이라고 떠벌리면 안된다는 것일테다.

그렇다면 DI란 또 무엇인가? 이것은 마치 당신이 받아먹기도 전에 미리 받아먹겠노라고 선언하는…, 주는 놈은 생각도 않았는데 먼저 말부터 꺼내놓는 파렴치한 코딩방식이라고 할 수 있다. 예를 들어 당신이 조촐한 회식으로 사장님과 중국집을 갔는데 당당히 "저는 짜장면 안먹고 양장피 먹겠습니다."라고 선언한다면 당신은 DI개념이 아주 확실한 프로그래머인 셈이다.

게다가 사실 DI 마저도 스프링만의 고유한 방식은 아니라는 거다. DI란 전설과도 같은 프로그래밍 예언서 GoF 중에서 가장 유명한 전략패턴을 일종의 프레임워크 처럼 편리하게 사용할 수 있도록 만든 것이라 할 수 있는데 스프링은 이런 전설로만 알아오던 전략패턴을 뉴비마저도 산소 호흡하듯 능수능란하게 사용하게끔 편리화하였다.

좀 더 구체적으로 DI에 대해 말하자면, DI방식으로 코드를 작성한다면 현재 사용하고 있는 객체의 메서드가 어떻게 작성되었는지 알 필요가 없으며 솔직히 그 클래스가 기능을 잘 구현했는지 조차도 알 필요가 없다. 그저 당신이 확인할 사항은 클래스의 기능을 추상적으로 묶어둔 인터페이스만 갖다 쓰면 그만이다. 나머지는 스프링에서 찜한 걸 연결시켜줄테니 말이다.

아래 그림은 위의 글로만 써놓은 전략패턴과 DI에 대해 정말 간단히 예제로 만든 코드이다.



먼저 말했듯 DI는 스프링만 가지고 있는 고유의 기능이 아니므로 자바의 오브젝트 단위에서도 DI의 구현이 가능하다. 먼저 StrategyPatternService 클래스를 보면 sp라는 객체는 인터페이스 객체인 StrategyPattern로 선언되었으며 StrategyPattern의 메서드인 dependency_injection()를 호출하고 있긴 하다. 그러나 객체 생성 과정에서 객체 sp에 StrategyPatternImpl 클래스를 주입시켜버림으로써 결과적으로 객체 sp는 StrategyPatternImpl의 dependency_injection()이란 메서드를 호출해버리고 말았다는 것이다.

그러므로 위에서 말했듯이 당신은 인터페이스의 메서드만 이용하더라도 위와같이 구현부는 나중에 주입을 통해 해결하므로 획기적인 분업과 동시에 구현 클래스를 쉽게 교체할 수 있다는 장점을 얻게 된다. 물론 스프링은 위의 이미지와 같이 단순한 방식으로 주입하진 않는다. 주입 방식에만 클래스 형태와 XML형태 2가지가 있으며 그 세부적으로도 어마어마하게 많은 방식으로 DI를 할 수 있다.

가끔 스프링을 쓰다보면 이런 다양한 방식이 진짜 편리와 우수한 기능을 위해서인지… 아니면 일부러 사용자를 헷갈리게 하려는 건지 분간이 안갈 때가 종종 있다. (개인적으로 로드 존슨이 프로그래머들을 편집증으로 몰아가기 위해 꾸민 음모가 아닌가 싶다…)

여하튼 IoC/DI의 개념을 요약하자면 정신나간듯 언제 사용될 지도 모르는 코드를 쳐대면서(IoC) 동시에 사용하고 있는 코드가 뭔지도 모르면서 일단 갖다 쓰는(DI) 획기적이고 진보적인 프로그래밍 작성방식이라 이해하면 되겠다.


서비스 추상화란?

솔직히 지금 DI를 설명하면서 서비스 추상화에 대한 부분까지 함께 설명해버렸다. 간략하게 개념에 대해서만 설명하자면 토비의 스프링의 일부분을 발췌하는 것이 좋을 듯 싶다.

추상화란 하위 시스템의 공통점을 뽑아내서 분리시키는 것을 말한다. 그렇게 하면 하위 시스템이 어떤 것인지 알지 못해도, 또는 하위 시스템이 바뀌더라도 일관된 방법으로 접근할 수가 있다.

그렇다. 우리는 위의 간단한 DI예제에서 StrategyPattern이란 인터페이스로 기능을 분리시킴으로서 하위시스템, 즉 클래스가 그 무엇으로 교체되더라도 해당 메서드는 반드시 존재할 것이란 보장을 받은 셈이다. 당신이 위의 모든 글을 읽고서도 아직 잘 이해가 가지 않는다면 지금 당장은 이러한 것을 과연 어떻게 구현해낼까에 대해 고민하지 않아도 된다. 사실 그것을 알아나가는 과정이 바로 스프링을 공부하는 과정이기 때문이다.

다만… DI예제의 과정, 즉 인터페이스를 구현하고 주입하는 과정이 이해가 가지 않는다면 당신은 자바에 대한 기본기가 매우 부족한 것이니 자바의 정석을 펼치고 객체지향개념부터 다시 필독하고 읽어보길 바란다.


출처 : http://egloos.zum.com/springmvc/v/487497


'프로그래밍 > It 용어' 카테고리의 다른 글

기본용어  (0) 2016.09.27
자바 리플렉션 ( JAVA Reflection )  (0) 2016.09.26
퍼시스턴스 프레임워크  (0) 2016.09.26
iBatis(아이바티스)란?  (0) 2016.09.26
WAS와 웹서버의 차이  (0) 2016.09.23
Posted by 당구치는 개발자
|