일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 백준#BOJ#2615#오목
- 백준#BOJ#12865#평범한배낭
- 백준#BOJ#14501#퇴사#브루트포스
- 백준#boj#12755
- 백준#BOJ#8012#한동이는영업사원
- 백준#boj#16932#모양만들기
- 백준#BOJ#1939#중량제한
- Today
- Total
순간을 성실히, 화려함보단 꾸준함을
[리뷰]12주차 : 애노테이션 본문
애노테이션은 단순히 마크만 해놓은 것
애노테이션을 자세히 공부를 하지 않으면 무슨 기능이 있는 것처럼 생각할 수 있지만 그냥 주석과 다름이 없다.
이 말이 의미하는 바는 런타임 중에 알아내야 할 값은 들어가지 못한다
예시를 하나 들어보자
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
private static final String hello = "hello";
@GetMapping(hello)
public String hello(){
return "hello";
}
}
위의 코드는 hello
라는 변수를 static final
을 이용하여 정적인 변수로 만들었다. 당연히 @GetMapping()
안에 hello
라는 변수를 넣어도 아무 이상이 발생하지 않는다.
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
private static String hello = "hello";
@GetMapping(hello) //Compile Error
public String hello(){
return "hello";
}
}
그러나 final
키워드를 빼버리면 이렇게 컴파일 에러가 발생하는걸 확인할 수 있다.
@Retention 애노테이션의 SOURCE,CLASS,RUNTIME 의 의미
포스팅에 언급은 했지만 자세한 내용은 공부하지 않아 리뷰에 다시 공부하여 업로드를 한다.(굉장히 중요한 내용)
SOURCE
- @RetentionPolicy
가 SOURCE
라는 뜻은 컴파일 이후에는 이 애노테이션을 더 이상 사용하지 않겠다 라는 의미이다. 바이트코드에 남아있지를 않는다. 단순히 소스코드에서만 눈으로 보는 용도로 쓰이는 것이다.
(예를 들어 @Override
는 단순히 오버라이드가 되어 있는지 안되어 있는지를 확인 하는 용도)
CLASS
- 선언한 애노테이션의 정보를 바이트코드에 남겨 놓을거라는 뜻이다. 우리가 소스를 실행할때 클래스에 관한 정보를 클래스로더가 읽어들어 메모리에 적재를 한다. 이때 CLASS
라고 선언되어 있으면 애노테이션의 정보를 메모리에 적재시킬 때 누락 시켜버린다. 따라서 이는 리플렉션이 불가하게 만들어 버린다.
리플렉션이란???
구체적인 타입을 알지는 못해도,그 클래스의 메소드,타입,변수들을 접근가능하게 해주는 API를 뜻한다.
RUNTIME
- 리플렉션이 가능해 진다.(https://brunch.co.kr/@kd4/8)
getFields() 와 getDeclaredFields()
@RestController
public class HelloController {
private String privatename;
public String publicname;
private static final String hello = "hello";
@GetMapping(hello)
public String hello(){
return "hello";
}
}
HelloController
라는 클래스가 이런 형태로 선언되어 있다.
import java.lang.reflect.Field;
public class HelloMain {
public static void main(String[] args) {
Field[] fields = HelloController.class.getFields();
for(Field field : fields)
System.out.println(field);
}
}
//출력
public java.lang.String com.example.demo.HelloController.publicname
getFields()
는 외부에서 접근할 수 있는 필드값만을 가져와서 보여준다.
public class HelloMain {
public static void main(String[] args) {
Field[] fields = HelloController.class.getDeclaredFields();
for(Field field : fields)
System.out.println(field);
}
}
//출력
private java.lang.String com.example.demo.HelloController.privatename
public java.lang.String com.example.demo.HelloController.publicname
private static final java.lang.String com.example.demo.HelloController.hello
getDeclaredFields()
는 선언되어 있는 모든 필드값을 가져와 보여준다.
때문에 백기선님 말씀에 의하면 Getter
Setter
에 대해 너무 집착할 필요가 없다고 말씀하셨다.
이런 방법으로도 충분히 private
한 필드 값들을 가져올 수 있기 때문이다.
여기서 한단계 더 들어가 보자
public class MyHelloController extends HelloController{
private String myName;
}
MyHelloController
라는 클래스는 HelloController
클래스를 상속받고 있다.
이때,
import java.lang.reflect.Field;
public class HelloMain {
public static void main(String[] args) {
Field[] fields = MyHelloController.class.getDeclaredFields();
for(Field field : fields)
System.out.println(field);
}
}
는 어떤 값을 보여줄까?
정답은 private java.lang.String com.example.demo.MyHelloController.myName
이다. 그 이유는 getDeclaredFields()
는 선언되어 있는 필드값만을 보여주는데 현재 MyHelloController
에는 어떠한 애노테이션도 선언되어 있지 않기 때문이다.
그럼 getFields()
는 어떨까?public java.lang.String com.example.demo.HelloController.publicname
이렇게 출력이 된다.
왜 이럴까? getFields()
는 외부에서 접근을 할 수 있는 필드값만을 보여줄 수 있다고 하였다. 그럼 당연히 private
한 필드값들은 보여주지를 않는 것이다.
Service Loader
- Service Loader란?
주로 어플리케이션 내부에서 플러그인을 제공할 때 쓰이는 개념이다.
전체적인 개념은 특정 기능을 제공하는 인터페이스가 존재하고, 다양한 회사들이 이 인터페이스를 자신들의 서비스에 맞게 구현한다고 해보자.
그러면 사용자 입장에서 이 공통된 인터페이스만 가지고 있다면, 각 회사들이 제공하는 서비스들을 비교해가며 원하는 구현체만 골라서 쓸 수 있는 것이다. 이러한 인터페이스를 SPI(Service Provider Interface) 라고 한다.
길고 읽기 귀찮은 여정이 되겠지만 한번 Service Loader를 구현해 보자.
먼저 필요한 프로젝트는 총 3개이다.
필자는 demo
,demo1
,demo2
이렇게 3가지의 패키지를 사용을 했다.
demo
에는 HelloService
라는 인터페이스를 구현해주고, 패키징을 해보자(오른쪽 maven
버튼 클릭 후 clean
->install
을 누르자). 이때 주의해야 할 점은 pom.xml
파일에서 dependency
와 build
들을 삭제해준 후 패키징을 실행해야 한다.
이후에 demo
에 존재하는 groupId
,artifactId
,version
들을 복사하여 demo1
의 pom.xml
에 넣어주자.
그리고 MyHello
라는 클래스를 선언한 후 HelloService
의 구현체를 만들어주 주자.
구현체를 만들고 난 후에 resources
밑에 META-INF
와 dummy
파일을 만들어주자. 그리고 META-INF
안에 services
를 만들어 준다음에 HelloService
의 풀경로를 파일형식으로 생성해 주자.(빨간색으로 표시한걸 유심히 봐라)
그 파일 안에는 MyHello
의 풀경로를 적어주자
그 다음에 위에서 했던 거와 똑같이 패키징을 수행해주자 (clean
-> install
)
그리고 또 다시 groupId
,artifactId
,version
를 이번엔 demo2
에 pom.xml
에 넣어준 후main
함수를 구현해 주자.
우린 현재 무엇을 한건지 다시 상기시켜보면, 현재 우린 HelloService가 어떻게 구현되어 있는지 구현체를 전혀 알지 못하는 상태이고 단순히 인터페이스만 알고 있는 상태이다. 그러나 실행시켜보면 홧팅!! 이라고 잘 출력되는 것을 확인할 수 있다.
출처 : https://docs.oracle.com/javase/9/docs/api/java/util/ServiceLoader.html
Refernce
https://velog.io/@adduci/Java-%EC%84%9C%EB%B9%84%EC%8A%A4-%EB%A1%9C%EB%8D%94ServiceLoader
'백기선님과 함께 하는 자바 스터디' 카테고리의 다른 글
[리뷰] 13주차 : I/O (0) | 2021.07.25 |
---|---|
13주차 과제: I/O (0) | 2021.07.21 |
12주차 과제: 애노테이션 (0) | 2021.07.12 |
[리뷰] 11주차 : Enum (0) | 2021.07.10 |
11주차 과제: Enum (0) | 2021.07.10 |