티스토리 뷰
1. Reflection 이란?
- Java에서 지원하는 기능으로, 실행중인 Java Application에서 동적으로 Class의 Metadata를 읽어와 특정 정보를 획득하거나, Instance를 다룰 수 있다. 대표적으로 Spring에서는 @Component가 붙은 Class의 Instance를 생성하거나, 제 3의 Instance를 DI해줄 때 사용된다. Spring에서 이런 작업들이 수행될 때 접근 제한자가 private이어도, 해당 메소드를 실행할 수 있는 이유가 바로 Reflection을 사용하기 때문이다.
2. 자주 사용될 수 있는 Class Metadata
//Class
String simpleName = clazz.getSimpleName();
Class<?> superclass = clazz.getSuperclass();
Class<?>[] interfaces = clazz.getInterfaces();
//Annotation
for (Annotation declaredAnnotation : clazz.getDeclaredAnnotations()) {
Class<? extends Annotation> annotationType = declaredAnnotation.annotationType();
}
//Constructor
for (Constructor<?> declaredConstructor : clazz.getDeclaredConstructors()) {
Class<?>[] parameterTypes = declaredConstructor.getParameterTypes();
Object newInstance = declaredConstructor.newInstance();
Annotation[] declaredAnnotations = declaredConstructor.getDeclaredAnnotations();
Class<?>[] exceptionTypes = declaredConstructor.getExceptionTypes();
Type[] genericParameterTypes = declaredConstructor.getGenericParameterTypes();
String name = declaredConstructor.getName();
Annotation[][] parameterAnnotations = declaredConstructor.getParameterAnnotations();
int parameterCount = declaredConstructor.getParameterCount();
}
//Field
for (Field declaredField : clazz.getDeclaredFields()) {
Class<?> type = declaredField.getType();
Annotation[] declaredAnnotations = declaredField.getDeclaredAnnotations();
String name = declaredField.getName();
}
//Method
for (Method declaredMethod : clazz.getDeclaredMethods()) {
Annotation[] declaredAnnotations = declaredMethod.getDeclaredAnnotations();
Class<?> declaringClass = declaredMethod.getDeclaringClass();
Class<?>[] exceptionTypes = declaredMethod.getExceptionTypes();
Type[] genericParameterTypes = declaredMethod.getGenericParameterTypes();
String name = declaredMethod.getName();
Annotation[][] parameterAnnotations = declaredMethod.getParameterAnnotations();
int parameterCount = declaredMethod.getParameterCount();
Class<?> returnType = declaredMethod.getReturnType();
}
3. Reflection을 활용한 Autowired DI Sample Code
@Getter
@ToString
@AllArgsConstructor
public class Board {
private String name;
public void modify(Board newBoard) {
this.name = newBoard.getName();
}
}
@Slf4j
@Service
public class BoardFinder {
public Board find() {
Board board = new Board("basicBoard");
log.info("#### BoardFinder.find - output: {}", board);
return board;
}
}
@Slf4j
@Service
public class BoardModifier {
@Autowired
private BoardFinder boardFinder;
public void modify(Board newBoard) {
log.info("#### BoardModifier.modify - input: {}", newBoard);
Board board = boardFinder.find();
board.modify(newBoard);
log.info("#### BoardModifier.modify - output: {}", board);
}
@PostConstruct
public void init() {
log.info("#### BoardModifier @PostConstruct");
}
}
import com.google.common.collect.Maps;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.reflect.*;
import java.util.Map;
import java.util.Optional;
public class BoardReflection {
private Map<String, Object> singletonBeanContext = Maps.newHashMap();
public static void main(String[] args) throws Exception {
BoardReflection boardReflection = new BoardReflection();
String classFullPath = "com.project.kanban.application.board.BoardModifier";
boardReflection.registerSingletonBean(classFullPath);
BoardModifier boardModifier = (BoardModifier) boardReflection.getBean("BoardModifier");
boardModifier.modify(new Board("newBoard"));
}
public Object getBean(String beanName) throws ClassNotFoundException {
return Optional.ofNullable(this.singletonBeanContext.get(beanName)).orElseThrow(() -> new ClassNotFoundException(beanName + " is not found"));
}
private void registerSingletonBean(String classFullPath) throws Exception {
Class<?> clazz = Class.forName(classFullPath);
registerSingletonBean(clazz);
}
private void registerSingletonBean(Class<?> clazz) throws Exception {
if (isComponent(clazz)) {
Object singletonBean = createSingletonBean(clazz);
singletonBeanContext.put(singletonBean.getClass().getSimpleName(), singletonBean);
}
}
//Spring에서는 Project를 Scan하여 BeanDefinition을 생성하고, 마지막 초기화 과정에서 객체 생성 및 DI를 진행한다.
private Object createSingletonBean(Class<?> clazz) throws Exception {
//Get Class Metadata Information
Field[] declaredFields = clazz.getDeclaredFields();
Method[] declaredMethods = clazz.getDeclaredMethods();
//Create Class Instance
Object clazzInstance = clazz.getDeclaredConstructor().newInstance();
//Extract and Create Autowired Target
for (Field declaredField : declaredFields) {
declaredField.setAccessible(true);
for (Annotation fieldDeclaredAnnotation : declaredField.getDeclaredAnnotations()) {
if (fieldDeclaredAnnotation.annotationType() == Autowired.class) {
Class fieldType = declaredField.getType();
Object autowiredInstance = createSingletonBean(fieldType);
declaredField.set(clazzInstance, autowiredInstance);
}
}
declaredField.setAccessible(false);
}
for (Method declaredMethod : declaredMethods) {
for (Annotation methodDeclaredAnnotation : declaredMethod.getDeclaredAnnotations()) {
if (methodDeclaredAnnotation.annotationType() == PostConstruct.class) {
declaredMethod.invoke(clazzInstance);
}
}
}
return clazzInstance;
}
private boolean isComponent(Class<?> clazz) {
for (Annotation clazzDeclaredAnnotation : clazz.getDeclaredAnnotations()) {
if (clazzDeclaredAnnotation.annotationType() == Documented.class) {
continue;
}
if (clazzDeclaredAnnotation.annotationType() == Retention.class) {
continue;
}
if (clazzDeclaredAnnotation.annotationType() == Target.class) {
continue;
}
if (clazzDeclaredAnnotation.annotationType() == Component.class) {
return true;
}
if (clazzDeclaredAnnotation.annotationType().getDeclaredAnnotations().length - 3 > 0) {
return isComponent(clazzDeclaredAnnotation.annotationType());
}
}
return false;
}
}
Reflection은 접근 제한자를 무시, Metadata에 정보를 획득, Field값 추가, Instance 생성 등 매우 강력하면서도 위험한 기능이다.
하지만 매우 느리다. Runtime에서 동적으로 Reflection을 사용할 수 있지만, 많은양의 작업에는 부적합 하다. 이러한 특징으로 인해 Application이 시작될 때 진행하는 초기화 작업에서 사용하는것이 좋다.
'Programming > Java' 카테고리의 다른 글
ClassPath (0) | 2020.05.02 |
---|---|
상속과 this (0) | 2020.01.12 |
LocalDateTime (0) | 2019.03.11 |
JMX - mBean (2) | 2018.11.15 |
StringBuffer, StringBuilder (0) | 2016.08.23 |
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
링크
TAG
- JPA Criteria
- Embedded Mapping
- scikit-learn
- Mapping
- Sprint RetryTemplate
- SmartLifecycle
- JPA
- Registrar
- Discriminate Mapping
- Property
- Akka
- Join Table
- java Equals
- spring spel
- Charles proxy
- Spring
- Embeddable Mapping
- 복합키 Mapping
- Query DSL
- guava
- java generic
- Spring Registrar
- docker
- Typesafe Config
- java EqualsAndHashCode
- RetryTemplate
- @Primary
- Spring JDBC Template
- Criteria
- DI
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
글 보관함