Spring | Reflection์ ์ฌ์ฉํ์ฌ Annotation ๊ฐ์ ธ์ค๊ธฐ
ํด๋น ๋ด์ฉ์ ๊ณต๋ถ์ ๋ชฉ์ ์ผ๋ก ๊ธฐ๋ก๋์์ผ๋ฉฐ,
์๋ ๋ด์ฉ์ ๋ชจ๋ ๊ตฌ๊ธ ๊ฒ์์ผ๋ก ๊ณต๋ถ ๋ธ๋ก๊ทธ๋ฅผ ์ฐธ๊ณ ํ์ฌ ์์ฑํ์ต๋๋ค.
์ด์ฌํ ๋ฐฐ์ฐ๋ ์ค์ ๋๋ค. ๊ฐ์ฌํฉ๋๋ค.
๐ ๋ฆฌํ๋ ์ (Reflection)
๋ฆฌํ๋ ์ ์ ์์ฑ์, ๋ฉ์๋, ํ๋ ๋ฑ ํด๋์ค์ ๋ํ ์ ๋ณด๋ฅผ ์์ฃผ ์์ธํ ์๋ ค์ฃผ๋ ์๋ฐ API ์ด๋ค.
๋ํ์ ์ธ ์์๋ก ์ฌ๋ฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ, ํ๋ ์์ํฌ์์ ์ฌ์ฉ๋๋ ์ด๋ ธํ ์ด์ ์ด ๋ฆฌํ๋ ์ ์ ํตํด ํ์ธํ๋ ๋ฐฉ๋ฒ ๋ฑ์ด ์๋ค.
๋ฆฌํ๋ ์ ์ ์ฌ์ฉํ๋ฉด ์ ๊ทผ ์ ์ด์์ ๋ฌด๊ดํ๊ฒ ํด๋์ค์ ํ๋๋ ๋ฉ์๋๋ ๊ฐ์ ธ์์ ํธ์ถํ ์ ์๋ค.
๐ ์ด๋ ธํ ์ด์ (Annotation) ๊ฐ์ ธ์ค๊ธฐ
๋ํ์ ์ธ ์์ ์ค ํ๋๋ฅผ ์ค์ตํด๋ดค๋ค.
๐ ์์ ์ด๋ ธํ ์ด์ ์์ฑ
์ด๋ ธํ ์ด์ ์ ํด๋ ๊ตฌ์ฑ
์์ ์ด๋ ธํ ์ด์ ์ ์ฝ๋
package com.example.mvcpractice.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {
}
๐ ์ด๋ ธํ ์ด์ ์ ์ธ์ฝ๋
์ด๋ ธํ ์ด์ ์ ์ ์ธํ๊ธฐ ์ํ ์ฝ๋๋ก @Target ์ด๋ผ๋ ๊ธฐ๋ฅ์ด ์๋ค. ํด๋น ์ฌ์ฉ์๊ฐ ๋ง๋ ์ด๋ ธํ ์ด์ ์ด ๋ถ์ฐฉ๋ ์ ์๋ ํ์ ์ ์ง์ ํ๋ค. (ํ์ : ํด๋์ค, ์์ฑ์, ๋ฉ์๋ ๋ฑ๋ฑ)
์ด๋ ธํ ์ด์ ์ ์ธ์ฝ๋ ๊ตฌ์ฑ
์ฌ์ฉ์ ์์ฑ: @Controller
import java.lang.annotation.ElementType;
@Target({ElementType.TYPE})
// TYPE ์ด์ธ์๋ CONSTRUCTOR, METHOD, FIELD ๊ฐ ์์ผ๋ฉฐ, ์ํ๋ ํ์
์ ์์ฑํ๋ค.
public @interface Controller {
}
@Retention์ ํด๋น ์ด๋ ธํ ์ด์ ์ด ์ธ์ ๊น์ง ์ ์งํ ์ง ์๋ ค์ฃผ๋ ์ด๋ ธํ ์ด์ ์ด๋ค.
์ด๋ ธํ ์ด์ ์ ์ ์ธํ๊ธฐ ์ํด์ ์ฌ์ฉ์์ ์์ฑ ์ด๋ ธํ ์ด์ ๋ํ ์ด๋ ์์ ๊น์ง ์ ํจํ์ง๋ฅผ ๋ช ์ํด์ค์ผ ํ๋ค.
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {
}
@Retention์ ์ฌ์ฉํ๋ RetentionPolicy๋ SOURCE, CLASS, RUNTIME ์ด๋ ๊ฒ 3๊ฐ์ง ์์ฑ์ ์ฌ์ฉํ ์ ์๋ค.
* SOURCE
- ์ปค์คํ ํ ์ด๋ ธํ ์ด์ ์ ์ฃผ์์ฒ๋ผ ์ฌ์ฉํ๊ณ ์ถ์ ๋ ์ฌ์ฉํ๋ค.
- ์ฌ์ฉํ ๊ฒฝ์ฐ ์ปดํ์ผ ํ ์์ฑ๋ .class ์ ํด๋น ์ด๋ ธํ ์ด์ ์ ๋ณด๊ฐ ์ฌ๋ผ์ง๊ณ ์์ค์ฝ๋๊น์ง๋ง ์ ์งํ๋ค.
- ์ปดํ์ผ ํ ํด๋น ์ด๋ ธํ ์ด์ ์ ์ฌ๋ผ์ง๋ค.
* CLASS
- @Retention์ ๊ธฐ๋ณธ๊ฐ ํด๋์ค ํ์ ์ด๋ค.
- ๋ฐํ์ ์ ํด๋์ค๋ฅผ ๋ฉ๋ชจ๋ฆฌ๋ก ์ฝ์ด์ค๋ฉด ํด๋น ์ ๋ณด๋ ์ฌ๋ผ์ง๋ค.
* RUNTIME
- ํด๋์ค๋ฅผ ๋ฉ๋ชจ๋ฆฌ์ ์ฝ์ด์์ ๋๊น์ง ์ ์งํ๋ค.
- ์ฝ๋์์ ์ด ์ ๋ณด๋ฅผ ๋ฐํ์ผ๋ก ํน์ ๋ก์ง์ ์คํํ ์ ์๋ค.
๐ ์ด๋ ธํ ์ด์ ๋ถ์ฐฉ ํด๋์ค ์์ฑ
์์ฑํ ์ด๋ ธํ ์ด์ ์ ๋ถ์ฐฉํ ํด๋์ค๋ฅผ ์์ฑํ๋ค.
package com.example.mvcpractice.controller;
import com.example.mvcpractice.annotation.Controller;
// ํด๋น ํจํค์ง๋ฅผ ํ์ธํ๋ฉด ์ฌ์ฉ์ ์์ฑ ์ด๋
ธํ
์ด์
์์ ์ ์ ์๋ค.
import com.example.mvcpractice.annotation.RequestMapping;
import com.example.mvcpractice.annotation.RequestMethod;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Controller // ์ฌ์ฉ์ ์์ฑ ์ด๋
ธํ
์ด์
public class HomeController {
@RequestMapping(value = "/", method = RequestMethod.GET)
public String home(HttpServletRequest request, HttpServletResponse response) {
return "home";
}
}
๐ ํ ์คํธ ์ฝ๋
ํ ์คํธ ํด๋์ค ์์ฑ
ํ ์คํธ ์ฝ๋
package com.example.mvcpractice;
import com.example.mvcpractice.annotation.Controller;
import com.example.mvcpractice.annotation.Service;
import org.junit.jupiter.api.Test;
import org.reflections.Reflections;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.annotation.Annotation;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class ReflectionTest {
private static final Logger logger = LoggerFactory.getLogger(ReflectionTest.class);
// ์ฌ์ฉ์๊ฐ ์์ฑํ ์ด๋
ธํ
์ด์
์ ๋ก๊ทธ๋ก ํ์ธํ๋ค.
@Test
void controllerScan() {
Set<Class<?>> beans = getTypesAnnotatedWith(List.of(Controller.class, Service.class));
logger.info("beans: [{}]", beans);
// beans ์ ๋ด๊ธด ๋ด์ฉ์ ๋ก๊ทธ๋ฅผ ํตํด ํ์ธํ ์ ์๋ค.
}
// getTypesAnnotatedWith() ๋ฉ์๋๋ฅผ ํตํด Controller๊ณผ Service ์ด๋
ธํ
์ด์
์ด ๋ถ์ฐฉ๋ ํด๋์ค๋ฅผ ์ฐพ๋๋ค.
private static Set<Class<?>> getTypesAnnotatedWith(List<Class<? extends Annotation>> annotations) {
Reflections reflections = new Reflections("com.example.mvcpractice");
// reflection ์ ํด๋น ํจํค์ง ๋ฐ์ ์๋ ํด๋์ค์ ๋ํ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์จ๋ค.
Set<Class<?>> beans = new HashSet<>();
annotations.forEach(annotation -> beans.addAll(reflections.getTypesAnnotatedWith(annotation)));
// reflections.getTypesAnnotatedWith() ์ ํจ์๋ฅผ ์ฌ์ฉํด ํด๋์ค๊ฐ ์์นํ ํจํค์ง ์ ๋ณด๋ฅผ ๋ด๋๋ค.
return beans;
}
}
์คํ ๊ฒฐ๊ณผ
ํ ์คํธ ์ฝ๋2
@Test
void showClass() {
Class<User> clazz = User.class;
logger.info(clazz.getName());
logger.info("User all declarde fields [{}]", Arrays.stream(clazz.getDeclaredFields()).collect(Collectors.toList()));
// getDeclaredFields(): User ํด๋์ค์ ์ ์ธ๋ ๋ชจ๋ ํ๋(String userId, String name)๋ฅผ ๊ฐ์ ธ์จ๋ค.
logger.info("User all declarde constructors [{}]", Arrays.stream(clazz.getDeclaredConstructors()).collect(Collectors.toList()));
// getDeclaredConstructors(): User ํด๋์ค์ ์ ์ธ ๋์ด์๋ ๋ชจ๋ ์์ฑ์(User(...))๋ฅผ ๊ฐ์ ธ์จ๋ค.
logger.info("User all declarde Methods [{}]", Arrays.stream(clazz.getDeclaredMethods()).collect(Collectors.toList()));
// getDeclaredMethods(): User ํด๋์ค์ ์ ์ธ๋ ๋ฉ์๋(equalsUser(...))๋ฅผ ๊ฐ์ ธ์จ๋ค.
}
์คํ ๊ฒฐ๊ณผ
๐ ๋์ผ๋ก
Reflection์ ๊ณต๋ถํ๋ฉด์ ์์ฃผ ๋ฑ์ฅํ๋ ๋ง์ด ์๋๋ฐ '๊ตณ์ด ์์์ผํ ๊น?', 'ํ์ฉํ ์ผ์ด ๊ฑฐ์ ์๋ค' ๋ฑ์ด๋ค.
์๊ฐ์ฒ๋ผ ์ ๋ณด๋ฅผ ํ์ธํ๋ ๊ธฐ๋ฅ์ด์ง๋ง, ํํ ์ฌ์ฉํ๊ฒ ๋ ๊ตฌ์ฒด์ ์ธ ํด๋์ค๋ฅผ ๋ชจ๋ฅผ๋ฆฌ ์์ด ์ฌ์ฉํ์ง ์๋ ๋ฏ ํ๋ค.
๊ทธ๋ฆฌ๊ณ ๋จ์ ์ผ๋ก ์๋ ค์ง ๋ด์ฉ๋ค ์ค, ์ฑ๋ฅ ์ค๋ฒํค๋๊ฐ ์๋ค.
์ปดํ์ผ ํ์์ด ์๋ ๋ฐํ์์ ๋์ ์ผ๋ก ํ์ ์ ๋ถ์ํ๊ณ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ค๋ฏ๋ก JVM์ ์ต์ ํ ํ ์ ์๋ค๊ณ ํ๋ค.
(์์ง ๋ฌด์จ ์๋ฆฌ์ธ์ง ์ ๋ชจ๋ฅด๊ฒ ์ง๋ง, ์์ข์ ๊ฒ ๊ฐ๋ค.)
๋จ์ ์ด๋ผ ๋งํ๊ณ ์์๋ชปํ ๋ถ์์ฉ์ ๊ตณ์ด ๊ฐ์ํ ํ์๋ ์๊ธฐ ๋๋ฌธ์ ์ฌ์ฉํ์ง ๋ง์๋ ๊ฒฐ๋ก ์ ๋๋ฌํ๋ค.
์ฐพ์ผ๋ฉด์ ๋ฐฐ์ด ์ด๋ ธํ ์ด์ ์ ์ ์ธ์ฝ๋๋ ์๊ฒ๋์๋ค.์์ง๊น์ง ์ง์ ์ ์ผ๋ก ์ด๋ ธํ ์ด์ ์ ์์ฑํ๊ณ ์ฌ์ฉํ๊ธฐ๊น์ง ์ค๋ ฅ์ด ๋ถ์กฑํ์ง๋ง, ์ข์ ๊ณต๋ถ๊ฐ ๋์๋ค.
์ถ์ฒ | hudi
์๋ฐ ๋ฆฌํ๋ ์ (Reflection) ๊ธฐ์ด
๋ฆฌํ๋ ์ (Reflection) JVM์ ํด๋์ค ์ ๋ณด๋ฅผ ํด๋์ค ๋ก๋๋ฅผ ํตํด ์ฝ์ด์์ ํด๋น ์ ๋ณด๋ฅผ JVM ๋ฉ๋ชจ๋ฆฌ์ ์ ์ฅํ๋ค. ๊ทธ๋ ๊ฒ ์ ์ฅ๋ ํด๋์ค์ ๋ํ ์ ๋ณด๊ฐ ๋ง์น ๊ฑฐ์ธ์ ํฌ์๋ ๋ชจ์ต๊ณผ ๋ฎ์์์ด, ๋ฆฌํ๋ ์
hudi.blog
์ถ์ฒ | goodGid
Spring MVC - @Retention ์ ๋ ธํ ์ด์
Index
goodgid.github.io
์ถ์ฒ | Tecoble
Reflection API ๊ฐ๋จํ ์์๋ณด์.
Spring Framework๋ฅผ ํ์ตํ๋ค ๋ณด๋ฉด Java Reflection API๋ฅผ ์์ฃผ ์ ํ๊ฒ ๋๋ค. ํ์ง๋ง Reflection API…
tecoble.techcourse.co.kr