본문 바로가기
language/java

Reflection과 Spring DI

by 죄니안죄니 2026. 6. 17.
반응형

여기부터는 단순 Java가 아니라 Spring을 이해하는 챕터다.

 

Reflection은 이미 Java 내부 동작 챕터에서 다뤘다. 이번 글은 Reflection 자체를 설명하는 글이 아니라, Reflection이 Spring DI에서 실제로 어떻게 사용되는지를 이해하는 것이 목표이다.

 


Reflection과 Spring DI

Reflection은 이미 배웠는데, Spring에서는 왜 다시 등장할까?

이전 글에서는 Reflection을

실행 중(Runtime)에 클래스, 메서드, 필드 정보를 조회하고 조작하는 Java 기능

이라고 배웠다.

예를 들어

 
Class<?> clazz = UserService.class;

Method[] methods = clazz.getDeclaredMethods();

Field[] fields = clazz.getDeclaredFields();
 

처럼 클래스의 내부 구조를 읽거나

 
clazz.getDeclaredConstructor().newInstance();
 

처럼 객체를 생성할 수도 있었다.


여기서 많은 사람들이 이런 생각을 한다.

"Reflection 문법은 알겠는데 실무에서는 어디에 쓰는 거지?"

사실 Java 개발자가 Reflection을 직접 사용할 일은 많지 않다.

하지만 Spring은 거의 애플리케이션 전체를 Reflection 위에서 동작시킨다.

이번 글에서는

Reflection이라는 Java 기능이 Spring DI에서 어떻게 사용되는지

를 이해하는 것이 목표이다.


Spring DI를 모른다고 가정해보자.

다음 클래스를 보자.

 
@Service
public class UserService {

}
 

그리고

 
@RestController
public class UserController {

    @Autowired
    private UserService userService;

}
 

개발자는

 
new UserService();
 

를 작성한 적이 없다.

그런데도

 
userService.save();
 

가 정상적으로 동작한다.


그렇다면 질문이 생긴다.

Spring은 언제 UserService 객체를 만들었을까?

그리고

어떻게 UserController 안에 넣었을까?


우리가 직접 만든다면

Spring이 없다면

직접 객체를 생성해야 한다.

 
UserService userService =
        new UserService();

UserController controller =
        new UserController(userService);
 

즉,

개발자

↓

new

↓

객체 생성

↓

객체 전달
 

이다.


하지만 Spring은

개발자

↓

클래스만 작성

↓

Spring이 알아서 객체 생성

↓

Spring이 알아서 주입
 

한다.


Spring은 먼저 클래스를 찾는다.

애플리케이션이 시작되면

Spring은

com.example
 

패키지를 스캔한다.


예를 들어

UserController.class

UserService.class

OrderService.class

ProductRepository.class
 

를 발견한다.


그런데

Spring은

이것이 Service인지 Controller인지 어떻게 알까?


Reflection으로 Annotation을 읽는다.

Spring은 Reflection을 이용해서

 
Class<?> clazz = UserService.class;
 

를 얻는다.

그리고

 
clazz.isAnnotationPresent(Service.class);
 

를 실행한다.


결과

true
 

Spring은

@Service 발견

↓

Bean으로 등록
 

하기로 결정한다.


즉,

Reflection은

Spring이 클래스를 이해하는 눈(Eye)

역할을 한다.


Reflection으로 객체 생성

Spring은

 
new UserService();
 

를 코드에 작성하지 않는다.

Reflection을 이용한다.

 
Constructor<?> constructor =
        clazz.getDeclaredConstructor();

Object bean =
        constructor.newInstance();
 

즉,

Reflection

↓

Constructor 조회

↓

newInstance()

↓

객체 생성
 

이다.


생성한 객체는 어디에 저장될까?

Spring은 생성한 객체를

Bean Container에 저장한다.

BeanContainer

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

UserService

OrderService

UserController

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

이제 필요할 때마다

여기서 꺼내서 사용한다.


@Autowired도 Reflection이다.

다음 코드를 보자.

 
@RestController
public class UserController {

    @Autowired
    private UserService userService;

}
 

Spring은 Reflection으로

필드를 찾는다.

 
Field field =
        UserController.class
                .getDeclaredField("userService");
 

그리고

private라도 접근 가능하게 만든다.

 
field.setAccessible(true);
 

그리고 Bean Container에서

UserService를 찾아 넣는다.

 
field.set(controller, userService);
 

즉,

Reflection

↓

Field 조회

↓

private 접근 허용

↓

객체 주입
 

이다.


생성자 주입도 Reflection이다.

 
@RequiredArgsConstructor
@Service
public class OrderService {

    private final UserService userService;

}
 

Spring은

생성자를 Reflection으로 찾는다.

 
Constructor<?> constructor =
        clazz.getDeclaredConstructor(
                UserService.class
        );
 

그리고

Bean Container에서

UserService를 꺼내

 
constructor.newInstance(userService);
 

를 수행한다.


Reflection이 없다면?

Spring은

어떤 생성자가 있는지

↓

어떤 필드가 있는지

↓

어떤 Annotation이 붙어있는지

↓

객체를 어떻게 생성할지
 

전혀 알 수 없다.


즉,

Reflection이 없다면

@Component

@Service

@Repository

@Controller

@Autowired
 

모두 구현할 수 없다.


Reflection의 단점

Reflection은 매우 강력하지만

일반 메서드 호출보다 느리다.


왜냐하면

 
userService.save();
 

컴파일 시점에 이미 호출 대상이 결정된다.


Reflection은

 
method.invoke(object);
 

처럼

실행 중에

메서드 검색

↓

접근 권한 확인

↓

호출
 

을 수행한다.


그런데 Spring은 왜 성능 문제가 없을까?

많은 사람들이

Reflection은 느리니까 Spring도 느린 거 아닌가?

라고 생각한다.


실제로는 그렇지 않다.

Reflection은

애플리케이션 시작

↓

Bean 생성

↓

DI 수행
 

에서 주로 사용된다.


그리고 실제 비즈니스 로직은

 
userService.save();
 

처럼 일반 메서드 호출로 수행된다.


즉,

Reflection은

초기화 단계에서 사용

↓

실행 중에는 거의 사용하지 않음
 

이라는 것이 핵심이다.


그림으로 전체 흐름

Application Start

        │

        ▼

Reflection으로 Class 조회

        │

        ▼

@Service 확인

        │

        ▼

Reflection으로 객체 생성

        │

        ▼

Bean Container 저장

        │

        ▼

@Controller 생성

        │

        ▼

Reflection으로 @Autowired 필드 조회

        │

        ▼

UserService 주입

        │

        ▼

Application Ready
 

지금까지의 흐름에서 보면

지금 배우는 Reflection은

예전 글에서 공부했던

Java 기능 자체

가 아니다.

지금은

Spring이 DI를 구현하기 위해 Reflection을 어떻게 사용하는지

를 배우는 것이다.

그래서 다음 글인 Bean 생성 과정으로 넘어가면

Reflection
        ↓
@ComponentScan
        ↓
BeanDefinition 생성
        ↓
Bean 생성
        ↓
DI
        ↓
Bean Post Processor
        ↓
Proxy 생성
 

이라는 Spring의 전체 생명주기가 하나의 흐름으로 자연스럽게 이어진다.

반응형

'language > java' 카테고리의 다른 글

객체 생명주기와 Spring Bean  (0) 2026.06.17
Bean 생성 과정  (0) 2026.06.17
Virtual Thread(Java 21)  (0) 2026.06.17
Stream 개선사항(Java 9~21)  (0) 2026.06.17
Optional 심화  (0) 2026.06.17

댓글