시소당
/////////////////////////////////////////////////////////////////////
// 메타 데이타(MetaData ->Annotation
// 기능: 자바의 필드, 클래스, 메소드에 주석과 같이 별도의 표기법으로 표기
// 하여 개발도구. 배포도구, 런타임 라이버러리 와 같은 특수한 방법으로ㅓ
// 처리할 수 있도록 하는 장치 ==> XML에 배포 디스크립트 대체
// XDoclet ; JavaDoc에 EJB와 관련된 다양한 정보를 넣어 인터페이스
// 코드를 자동으로 생성
/////////////////////////////////////////////////////////////////////
// Annotation의 표현 방법
// (1) Mark Annotation
// Serializable 인터페이스가 어떤 메소드도 정의되어 있지 않지만
// 이 인터페이스는 객체직렬화가 가능하다는 의미를 마킹하는 용도로
// 사용되는 것과 유사하게 사용된다. 표시하는 방법은 @TODO 식이다.
// (2) Single Value Annotation
// 값을 하나 가지고 있는 annotation 이다. 예를 들어 @TODO("XXX")
// (3) Full Annotation
// 두개 이상의 멤버를 가지고 있는 annotation 이다.@TODO("XXX","YYY")
//////////////////////////////////////////////////////////////////////
Annotations이란 무엇인가 ?
- JCP의 5.0(JDK1.5)스펙의 일부인 JSR-175은 자바의
"metadata facility(메타데이터 기능)"을 제안한 것이다. 아마 더 적절한 표현은
metacoding(메타코딩)일 것이다. Annotation은 자바의 클래스와 메소드,필드(변수)들을
보다 상세하게 기술해주고 설명해주는 코드의 일부분인다. 기존의 주석문과 다른 점은
Annotation은 특별한 인터페이스로 구현되어지며 패키지화되고 컴파일 되며 다른 클래스들처럼
import될 수 있다.
===================
Marker Annotation
===================
우리가 소스를 읽거나 작성할 때 필요한 모든 정보를 제공해주는
Annotation이 어디에 있는지 알려주는 간단한 form이다.
- 예를 든다면, OODBMS가 코드를 마킹하기 위해 Annotation을 어떻게 사용할지를 생각해보자.
디자이너가 자신들의 persistence 메커니즘을 자바의 직렬화 기능에서 의존적이지 않게 만들기를
원한다고 하자. Serializable 인터페이스와 transient 키워드의 사용을 피하고 싶다.
마커들의 두 예제는 그 클래스가 persistent 하다는 것을 가르키며서 다른 하나는 어떤 필드가
transient하다는 것을 마킹할 것이다.
import oodbms.annotation.Persistable;
import oodbms.annotation.Transient;
@Persistable public class Foo {
private String field;
@Transient private String tmpField;
}
- 이렇게 하면 OODBMS는 트랜잭션의 맥락에서 사용되는 클래스를 persistent 한 것으로
인식할 수 있으며 적절하게 이 클래스를 저장하고 업데이트 할 수 있을 것이다.
또한 @Transient라고 마킹된 임시 필드 값을 무시하므로 클래스의 원래 용도를 충족시켜
줄 수 있을 것이다.
=============
Single value
=============
- 대부분의 annotation은 파라미터들을 필요로 한다. 그러나 하나만을 필요로 하는
특별한 경우 annotation 스펙은 빠른 방법(shorthand)을 제공한다. 우리가 persistent
클래스의 버젼 번호들을 선언하고 싶은 경우를 생각해보자. 여기 한가지 방법이 있다.
Import oodbms.annotation.Persistable;
Import oodbms.annotation.Version;
@Persistable
@Version ("1.0.0")
public class Foo { }
- 일반적으로 단일 파라미터 annotation은 다음과 같은 형식을 취한다.
@Annotation(param-value). 이는 @Annotation(value=param-value) 에 대해 보다
더 컴팩트한 표현이다.
===================
Normal annotations
===================
- 대개의 경우 1개 이상의 보다 더 구조화된 정보를 제공하고 싶을 것이다. 이런 경우
annotations은 하나 혹은 그 이상의 이름-값 쌍을 정의하게 해준다. 값들은 자바의
기본 자료형들 (int값들, String값들, double값들,boolean 값들, 등등의) 혹은
다른 annotation들이 될것이다.
- 이는 타입 안정성을 유지하면서 매우 복잡한 구조를 생성하게 할것이다.
이는 전통적인 Doclet 태그들에 대해 새로운 annotation이 갖는 혜택중 하나이다.
버젼 번호를 부여하는 예제를 다시 살펴보자 우리는 그 정보들을 보다 더 구조화 하고
싶을 것이다.
@Persistable
@Version(major=1, minor=0, micro=0)
public class Foo { }
- 컴파일러와 Eclipse 3.1 (in beta)같은 JDK 1.5를 지원하는 syntax-aware IDE는
이 새로운 Version annotation의 적절한 사용을 즉시 인식할것이다.예를 들어 이중 한
필드에 대한 오타를 인식한다는 것이다.
=============
Arrays
=============
- annotation은 배열구문({}를 이용하여 배열을 표현)을 이용하여 다중 값을 취하는 필드
들을 가질 수 있다. 예를 들어 우리가 Javadoc 의 작성자 필드들을 교체하고자 한다면
다음과 같이 다중 값을 허용할 수 있다.
@Authors({
@Author("Jane Doe")
@Author("John Doe")
})
- 배열 안에 배열을 집어넣을 수도 있고 배열안의 복합된 annotation을 집어 넣을 수도 있다.
만약 메타 코드들을 annotation으로 표현할 수 없는 경우 아마 이것으로 많은 일을 하려고
할 것이다. 이 구문은 매우 강력하다.
==============================
사용자정의 Annotations을 선언하기
==============================
- annotation은 이름을 가지고 타입이 지정되어 혹은 내장된 열거형으로 또는 필요하다면
인터페이스처럼 코드 없이 struct-like 폼안에 선언되어진다. annotation은 궁극적으로는
메소드, 필드, 혹은 그들이 annotate할 타입들을 위한 여분의 JVM 바이트코드 attribute로
변환될 순수한 데이터 구조이다.
public @interface HelloAnno {
enum Scope { LOCAL, WORLD }
String name();
Scope scope();
}
위의 선언에서 annotation은 다른 annotation에의 의해서 참조 되어질 수도 있다.
import oodbms.annotation.Version;
public @interface HelloAnno {
enum Scope { LOCAL, WORLD }
String name();
Scope scope();
Version version();
}
그리고 기본값도 가질 수 있다.
import oodbms.annotation.Version;
public @interface HelloAnno {
enum Scope { LOCAL, WORLD }
String name() default "Foo";
Scope scope() default Scope.LOCAL;
Version version();
}
위의 정의는 다음과 같은 두가지 방법으로 정의 될수 있다
@HelloAnno(version=@Version(major=1,minor=0,micro=0))
기본값을 가지는 장점으로 다음과 같이 명시적인 값을 세팅할수 있다
@HelloAnno(name="Bar", scope=Scope.WORLD,
version=@Version(major=1,minor=0,micro=0))
선언 기법을 하나만 더 보자. 단일 값을 가지는 annotation을 위해 컴파일러는
단일 필드는 value라는 이름을 가질 것이라고 가정할 것이다. 다음처럼
public @interface HelloAnno {
String value() default "world";
}
// usage: value는 "world"란 값을 할당 받을 것이다
@HelloAnno("world");
// 이렇게 하는 것도 가능하다.
@HelloAnno(value="world")
// 이것도 같게 동작한다.기본값을 취할것이므로
@HelloAnno
- Annotation 선언들은 그들만의 Annotation을 가질수 있다. 이것을 메타 코딩의
메타라고 보면 될것이다. 스펙에서는 이것들을 "Meta Attributes"라고 부른다.
예를 들어 @Target annotation 은 자바의 어떤 요소가 작성하는 애플리케이션에서
수정될수 있는지를 정의하고 있다. @Target(ElementType.TYPE)은 자바의 열거형(
enum),클래스, 인터페이스만 수정할 수 있도록 제한하고 있다.
@Target(ElementType.CONSTRUCTOR)은 생성자만 수정할 수 있다.
=============================
@RetentionPolicy annotation
=============================
@RetentionPolicy annotation 는 컴파일러에게 annotation으로 무엇을 할 수 있는지를
알려준다: SOURCE는 이것을 제거하며 클래스 파일안에 내장된 CLASS와 RUNTIME은 리플렉션을
통해 이를 가능하게 해준다.
- 위의 예를 든 OODBMS는 JVM에 올라가면 바이트코드를 볼수 있는 매우 영리한 클래스 로더를
가지고 있어서 우리는 리플렉션을 쓸 필요성을 못느낀다. 이 Version annotation은 다음과
같다.
package oodbms.annotation;
import java.lang.annotation.*;
@RetentionPolicy(RetentionPolicy.CLASS)
@Target(ElementType.TYPE)
public @interface Version {
int major();
int minor() default 0;
int micro() default 0;
}
///////////////////////////////////////////////////////
JDK 5.0없이 Annotation 하기
///////////////////////////////////////////////////////
- 만약 여러분이 JDK5.0을 설치하지 않았다면 어떻게 이 기능들을 여러분의 프로그램에
사용할 수 있을 것인가.
XDoclet
- XDoclet과 XDoclet의 AOP(attribute-oriented programming) 구현은 다른 책들에서
많이 다루어진 주제이기 때문에 여기서 비교를 목적 이상의 개요설명은 하지 않을 것이다.
- XDoclet은 코드들을 annotating하기 윈한 Javadoc 태그 기반의 접근이다.
Xdoclet 툴을 위한 플러그인들은 새로운 기능들을 제공한다. 메타데이터 구조는 JDK1.5보다
더 단조롭고하고 덜 객체지향적이다. 만약 당신의 IDE가 Javadoc 태그들은 인식하고 모르는
것들에 대해서 반전해서 표시한다면(IntelliJ처럼) 당신의 IDE에게 새로운 태그들에 대해서
알려줘야 한다. 또한 만약 문자열 대신에 integer값을 넘겨줘야 한다면 XDoclet을 도입하기
전까지는 고생좀 할 것이다. 게다가 이런 문제는 발생하기 전까지는 예측하기도 어렵다는 것이다.
- 반면에 XDoclet은 그것을 어떻게 하면 잘 사용하는가에 대한 지식이 있는 누군가가 있는
프로젝트에 적용되어 왔다. XDoclet은 자바 언어의 일부가 되기 보다는 오히려
직접 교류한다는(대등한 위치에 있다는) 장점을 가지고 있다: 자바언어와 XDoclet은 서로 독립적
이며 서로 다른 속도로 발전하고 있다.
- 만약 Sun이 XDoclet을 적용하다면 자바코드를 다루는 기존의 라이브러리와 UI 툴은 여전히
그대로 남아있을 수 있다.
===================
Commons attributes
===================
- 아파치의 자카르타 프로젝트는 Commons Attributes를 제공한다.(Commons 에는 로깅,
데이터 베이스 풀링, 콜렉션 같은 범용 기능을 제공하는 많은 API가 있다.)
이 패키지는 JDK5.0과 매우 유사한 기능들을 이전 버젼의 JDK를 쓰는 사용자들을 위해 제공
하고 있다.
- 오로지 JDK5.0가 @를 쓰는 대신 @@를 쓴다는 구문의 차이와 언어자체에 포함된게 아니라
Javadoc에 이식되어 있다는 점 밖에는 존재하지 않는다. Attributes 는 클래스로
구현되어 있으므로 post-processing 타임에 높은 수준의 타입 안정성을 보장 받을 수 있다.
위의 예제에서 Version 은 다음과 같이 바꿀 수 있다.
public class Version {
private int major;
private int minor;
private int micro;
public Version() { }
public int getMajor() {
return major;
}
public void setMajor(int major) {
this.major = major;
}
public int getMinor() {
return minor;
}
public void setMinor(int minor) {
this.minor = minor;
}
public int getMicro() {
return micro;
}
public void setMicro(int micro) {
this.micro = micro;
}
}
그리고 다음과 같이 사용될 수 있다.
/**
* @Persistable
* @@Version(major=1,minor=0,micro=0)
*/
public class Foo { }
- Commons Attributes 는 attribute ANT나 MAVEN에 통합되어 있는 컴파일러를
제공하므로 annotation을 포함한 최종 자바 클래스 화일들을 생성할 수 있다. 또한
클래스로부터 annotation을 읽어낼 수 있는 실시간 인터페이스도 제공한다.
- Commons Attributes가 가장 잘 만들어진 대안이며 JDK5.0으로 이동이 수월하다는
것도 큰 장점이다.
//////////////////////////////////////////////////////////////////////
// 실습
// 준비할 사항
/////////////////////////////////////////////////////////////////////////////////////////////////
//---------(1)
package com.sjw.ejb3.jdk5;
public @interface InProgress {
}
// Annotation은 실제 내부적으로는 Class로 처리한다.
// 이 @InProgress annotation은 "현재처리중" 이라는 문맥적인 의미를 소스코드 내에서 표현하고 싶을 경우 사용하고자
// 구현한 것이다.
// 내부에 아무런 변수도 가지고 있지 않기 때문에 이 annotation은 MakerAnnotation에 해당된다.
////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
//---------(2)
package com.sjw.ejb3.jdk5;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface TODO {
/**
* value는 Annotation의 name이고 String은 value의 자료형을 의미한다.
* 따라서 속성은 <code>@TODO(value="")</code>으로 표현할 수 있다.
*/
String value();
}
// 만약에 값을 가지는 annotation을 만들고 싶다면 위와 같이 String value() 라고
// 표현 해주면 된다.
// 이것이 의미하는 것은 @TODO annotation에 지정 가능한 값의 변수명이 value이고
// 자료형은 문자열이라는 의미이다.
// 위 아노테이션의 사용은 @TODO(value = "해결할 문제가 몇가지 있음 주의바람!") 과 같이 사용할 수 있다.
// 이것은 Single-Value annotation 이다.
//-------(3)
package com.sjw.ejb3.jdk5;
/*
// 이 주석 블록은 내장 Annotation 실습시에는 해제 한다.
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(
{
ElementType.TYPE, // 클래스, 인터페이스, enum
ElementType.METHOD, // 생성자를 제외한 모든 메소드
ElementType.CONSTRUCTOR, // 생성자
ElementType.ANNOTATION_TYPE // 메타 annotation
}
)
@Retention(RetentionPolicy.SOURCE) // 이 annotation은 소스코드에서만 사용됨
*/
public @interface Message {
public enum Priority {HIGH, NORMAL, LOW}
// 이 속성은 <code>@Message(priority=Message.Priority.HIGH)</code> 으로 표현할 수 있다.
Priority priority() default Priority.NORMAL;
// 이 속성은 <code>@Message(description="")</code> 으로 표현할 수 있다.
String description();
}
//-------------(4)
////////////////////////////////////
// Annotation 의 활용예제(Custom annotation 구현하기)
////////////////////////////////////
package com.sjw.ejb3.jdk5;
public class Example7 {
@InProgress
@TODO("아직 메소드를 구현하지 않았습니다.")
public int add(int a, int b) {
return 0;
}
@TODO(value = "0으로 나눌 수 없으므로 예외 처리를 해야 합니다.")
public int div(int a, int b) {
return a / b;
}
public int sub(int a, int b) {
return a - b;
}
}
///////////////////////////////////////////////////////
package com.sjw.ejb3.jdk5;
import java.lang.reflect.Method;
public class Example8 {
public static void main(String[] args) {
Object obj = null;
try {
// annotation을 검사할 클래스의 인스턴스를 생성함
obj = Class.forName("com.sjw.ejb3.jdk5.Example7").newInstance();
} catch (Exception e) {
e.printStackTrace();
}
// 해당 클래스에 작성한 메소드를 가져온다.
Method[] methods = obj.getClass().getDeclaredMethods();
for (Method m : methods) { // JDK 5.0의 향상된 루프를 이용
TODO todo = m.getAnnotation(TODO.class); // 현재 메소드에서 TODO annotation이
//있는지 확인한다.
if (todo == null) {
System.out.println("@TODO가 구현되지 않은 메소드 : " + m.getName());
} else {
System.out.println("@TODO가 구현된 메소드 : " + m.getName());
System.out.println(" 내용 --> " + todo.value());
}
}
}
}
////////////////// 내장 아노테이션 /////////////////////////
//(1) @Override
////////////////////////////////////////////////////////
package com.sjw.ejb3.jdk5;
public class Example9 {
public Example9() {
}
@Override
public String toString() {
return super.toString() + " [메소드 오버라이드 테스트]";
}
@Override // -> 컴파일 오류. 왜냐하면 부모에게 getName()이 정의되어 있지 않다.
public String getName() {
return Example9.class.getName();
}
}
// @Override 는 슈퍼클래스의 메소드를 오버라이드 한다는 의미를 가지고 있다.
// 그래서 위 소스는 컴파일시에 에러가 발생하게 된다. 즉 getName()이라는 메소드가
// 슈퍼 클래스(Object)에 없기 때문이다.
// 위의 소스에서는 toString() 과 getName()메소드는 반드시 오버라이드 해야 한다는 의미.
////////////////// 내장 아노테이션 /////////////////////////
//(2) @Deprecated
////////////////////////////////////////////////////////
package com.sjw.ejb3.jdk5;
public class Example10 {
@Deprecated
public void doSomething() {
System.out.println("Example10.doSomething()");
}
public static void main(String[] args) {
Example10 client = new Example10();
client.doSomething(); // deprecated 메소드 호출
}
}
// 이 아노테이션은 더이상 사용하지 말아야 하는 메서드를 지정할 때 붙이는 아노테이션이다.
// 이 아노테이션에 해당되는 메소드를 사용하였을 때 컴파일러는 경고 메시지를 보낸다.
////////////////// 내장 아노테이션 /////////////////////////
//(3) @Target
// @Retention
////////////////////////////////////////////////////////
// 주의 - 이 소스는 앞에 나온 Message 인터페이스와 같으나 주석부분을 제거 하였다.
/////////////////////////////////////////////////////////
package com.sjw.ejb3.jdk5;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
// @Target annotation 은 메타 annotation 이라고도 함.
// 이것은 내가 작성한 아노테이션이 어디에 사용될지를 결정하는 아노테이션이다.
// 이 예는 내가 작성하는 Message 아노테이션을 사용할 수 있는 곳을 아래 주석 처리한
// 곳으로 지정하는 것이다.
@Target(
{
ElementType.TYPE, // 클래스, 인터페이스, enum
ElementType.METHOD, // 생성자를 제외한 모든 메소드
ElementType.CONSTRUCTOR, // 생성자
ElementType.ANNOTATION_TYPE // 메타 annotation
}
)
// 자바 컴파일러가 이 아노테이션을 어떻게 다룰 것인지를 결정하는 것으로
// 이를 지정하지 않으면 런타임시 또는 수행시 해당 아노테이션을 인식할 수 없게 된다.
// 3가지 속성
// RetentionPolicy.SOURCE ~ 컴파일러가 컴파일시 삭제하여 클래스 파일에 저장되지 않는다.
// RetentionPolicy.CLASS ~ 클래스 파일에 저장되지만 자바 가상 머신은 무시한다.
// RetentionPolicy.RUNTIME ~ 클래스 파일에 저장하고 자바 가상 머신을 읽는다.
// 아래와 같은 표현: 메타아노테이션 표시법
@Retention(RetentionPolicy.SOURCE) // 이 annotation은 소스코드에서만 사용됨
public @interface Message {
public enum Priority {HIGH, NORMAL, LOW}
// 이 속성은 <code>@Message(priority=Message.Priority.HIGH)</code> 으로 표현할 수 있다.
Priority priority() default Priority.NORMAL;
// 이 속성은 <code>@Message(description="")</code> 으로 표현할 수 있다.
String description();
}
/////////////////////////////////////////////
// 앞에서 사용한 아노테이션 인터페이스 TODO 이다.
/////////////////////////////////////////////
package com.sjw.ejb3.jdk5;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface TODO {
/**
* value는 Annotation의 name이고 String은 value의 자료형을 의미한다.
* 따라서 속성은 <code>@TODO(value="")</code>으로 표현할 수 있다.
*/
String value();
}
// 위 소스에서 @Retention(RetentionPolicy.RUNTIME) 부분이 지정되어 있지 않다면,
// Example8을 실행할 경우 @TODO 아노테이션을 붙인 어떠한 메소드도 인식할 수 없다.
// 즉, 아노테이션 하지 않은 것과 같다.
/////////////////////////////////////////////////////////
// EJB3.0 과 Annotation
/////////////////////////////////////////////////////////
// EJB3.0에서는 EJB의 속성을 아노테이션을 활용하여 표시하기 때문에
// 과거의 xml 디스크립터를 대체 한다.
// 그래서 EJB3.0 공부는 아노테이션 공부이다.
//////////////////////////////////////////////////////////
////////////////////
// printf() 와 Varargs
////////////////////
public class SE50_ETC_Test {
public static void main(String[] args){
SE50_ETC_Test k = new SE50_ETC_Test();
k.aMethod("갑진이","정숙이","정희","명복이");
System.out.printf("%d %s %n",365,"일은 일년이다.");
k.aMethod("영제","희제","태제","백황이","남식이");
}
public void aMethod(String ... names){
for(String n: names){
System.out.println("안녕: " + n);
}
}
}
/////////////////////////////수고 많았습니다.////////////////