Spring/Spring

[Spring][Core] 3-5. Container: Additional Capabilities of the ApplicationContext

noahkim_ 2025. 4. 6. 21:12

1. MessageSource

  • 국제화를 지원하는 인터페이스
  • 다양한 언어로 메시지를 관리하고 사용자에게 언어별로 적절한 메시지를 제공할 수 있게 함
public interface MessageSource {
    @Nullable
    String getMessage(String code, @Nullable Object[] args, @Nullable String defaultMessage, Locale locale);
    String getMessage(String code, @Nullable Object[] args, Locale locale) throws NoSuchMessageException;
    String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException;
}
메소드 설명
getMessage
(String code, Object[] args, String defaultMessage, Locale locale)
지정된 코드에 해당하는 메시지를 찾음
없으면 기본 메시지를 반환합니다.
getMessage
(String code, Object[] args, Locale locale)
지정된 코드에 해당하는 메시지를 찾음
없으면 NoSuchMessageException을 던집니다.
getMessage
(MessageSourceResolvable resolvable, Locale locale)
MessageSourceResolvable 객체를 사용하여 메시지 검색

 

구현체

클래스 설명
ResourceBundleMessageSource
(기본 메시지 소스)
ResourceBundle을 사용하여 메시지 로드
주로 프로퍼티 파일에서 메시지를 로드함
ReloadableResourceBundleMessageSource ResourceBundleMessageSource의 확장형
메시지 파일을 변경 시, 동적으로 다시 로드할 수 있음 (파일 감지)
StaticMessageSource 메시지가 미리 정적으로 설정된 경우 사용되는 메시지 소스입니다.

 

2. Events

ApplicationEvent 클래스

기본 제공 이벤트
이벤트 설명
ContextRefreshedEvent
ApplicationContext가 초기화되거나 갱신될 때 게시됩니다. (예: refresh() 메서드 호출 시)
ContextStartedEvent
ApplicationContext가 시작될 때 게시됩니다. (예: start() 메서드 호출 시)
ContextStoppedEvent
ApplicationContext가 중지될 때 게시됩니다. (예: stop() 메서드 호출 시)
ContextClosedEvent
ApplicationContext가 닫힐 때 게시됩니다. (예: close() 메서드 호출 또는 JVM 종료 시)
RequestHandledEvent
HTTP 요청이 처리되었을 때 게시됩니다. (Spring의 DispatcherServlet을 사용하는 웹 애플리케이션에서만 적용)
ServletRequestHandledEvent
RequestHandledEvent의 서블릿 관련 확장 이벤트입니다.

 

ApplicationListener 클래스

커스텀 이벤트
기능 방법 설명
이벤트 정의 ApplicationEvent를 상속한 클래스 작성
커스텀 이벤트 객체 정의
이벤트 게시 ApplicationEventPublisherAware 구현
ApplicationEventPublisher.publishEvent() 호출
스프링 컨텍스트에서 이벤트를 발행
이벤트 수신 ApplicationListener<?> 구현
특정 이벤트 타입을 수신하여 처리

 

 

예제

더보기
public class BlockedListEvent extends ApplicationEvent {
    private final String address;
    private final String content;

    public BlockedListEvent(Object source, String address, String content) {
        super(source);
        this.address = address;
        this.content = content;
    }

    // 접근자 및 기타 메서드
}
public class EmailService implements ApplicationEventPublisherAware {
    private List<String> blockedList;
    private ApplicationEventPublisher publisher;

    public void setBlockedList(List<String> blockedList) {
        this.blockedList = blockedList;
    }

    public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
        this.publisher = publisher;
    }

    public void sendEmail(String address, String content) {
        if (blockedList.contains(address)) {
            publisher.publishEvent(new BlockedListEvent(this, address, content));
            return;
        }
        // 이메일 전송...
    }
}
public class BlockedListNotifier implements ApplicationListener<BlockedListEvent> {
    private String notificationAddress;

    public void setNotificationAddress(String notificationAddress) {
        this.notificationAddress = notificationAddress;
    }

    public void onApplicationEvent(BlockedListEvent event) {
        // notificationAddress로 적절한 알림 전송
    }
}

 

애너테이션 기반
사용 애너테이션 기능 설명
@EventListener 이벤트 수신
해당 메서드가 이벤트를 수신하도록 지정.
condition 속성으로 조건 지정 가능
@Async, @EventListener 비동기 이벤트 수신
이벤트를 비동기 방식으로 처리 (별도 스레드에서 실행)
@Order 이벤트 리스너 우선순위 설정
이벤트 리스너 메서드의 실행 순서 지정 (숫자가 작을수록 우선순위 높음)

 

 

예제

더보기
public class BlockedListNotifier {
    private String notificationAddress;

    public void setNotificationAddress(String notificationAddress) {
        this.notificationAddress = notificationAddress;
    }

    @EventListener(condition = "#blEvent.content == 'my-event'")
    public void processBlockedListEvent(BlockedListEvent event) {
        // notificationAddress로 알림 전송
    }
}
@EventListener
@Async
public void processBlockedListEvent(BlockedListEvent event) {
    // BlockedListEvent는 별도의 스레드에서 처리됨
}
@EventListener
@Order(42)
public void processBlockedListEvent(BlockedListEvent event) {
    // 알림 전송
}

 

3. Low-level Resources

ResourceLoader

  • 다양한 위치(classpath, 파일 시스템, URL 등)에서 리소스를 투명하게 로드할 수 있음
  • JDK의 java.net.URL보다 더 많은 기능을 제공
항목 설명
ApplicationContext는 ResourceLoader
ApplicationContext 자체가 ResourceLoader이므로 Resource를 로드할 수 있음
ResourceLoaderAware
빈이 ResourceLoaderAware를 구현하면 초기화 시점에 ResourceLoader가 주입됨
Resource 타입의 속성 주입
Resource 타입의 속성을 String 경로로 설정하면 자동으로 Resource 객체로 변환됨

 

 

예제

더보기
@Component
public class ResourceLoaderAwareExample implements ResourceLoaderAware {

    private ResourceLoader resourceLoader;

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    public void readFile() throws Exception {
        Resource resource = resourceLoader.getResource("classpath:data.txt");
        String content = new String(resource.getInputStream().readAllBytes(), StandardCharsets.UTF_8);
        System.out.println("1. ResourceLoaderAware 내용: " + content);
    }
}

4. Startup Tracking

ApplicationStartup

  • 애플리케이션 시작 시 단계별 메트릭을 추적할 수 있음
항목 설명
기본 구현체
기본값은 noop이므로 별도 설정 없이는 추적이 비활성화됨
추적 활성화 방법
ApplicationContext 생성 시 FlightRecorderApplicationStartup 같은 구현체를 설정해야 추적 가능
사용 목적
애플리케이션 시작 시간 분석 및 컨텍스트 라이프사이클 파악 (Micrometer나 프로파일러 대체 아님)
사용 방법
ApplicationStartupAware 구현
DI로 주입받아 커스텀 단계 추적 가능
주의사항
spring.* 네임스페이스는 내부 용도이므로 커스텀 추적 시 사용 금지

 

 

예제

더보기
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(MyApplication.class);
        app.setApplicationStartup(new FlightRecorderApplicationStartup());
        app.run(args);
    }
}
@Component
public class StartupExample implements ApplicationRunner {

    @Autowired
    private ApplicationContext context;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        ApplicationStartup startup = context.getApplicationStartup();
        StartupStep step = startup.start("custom.startup.step");

        // 작업 수행
        System.out.println("✨ Startup 작업 중...");

        step.tag("작업", "예제");
        step.end();
    }
}
  • 결과는 Java FlightRecorder 에서 수집할 수 있음 (로그, 커스텀 리스너도 가능)

5. ApplicationContext Installation

ContextLoaderListener

  • 선언적으로 ApplicationContext 등록 가능
항목 설명
contextConfigLocation
여러 파일 지정 가능 (공백, 콤마, 세미콜론으로 구분)
Ant 패턴 지원
예: /WEB-INF/*Context.xml, /WEB-INF/**/*Context.xml

 

 

예제

더보기
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

 

applicationContext.xml

<beans xmlns="http://www.springframework.org/schema/beans"
       ...>

    <bean id="helloBean" class="com.example.HelloBean" />
</beans>

 

사용

public class MyServlet extends HttpServlet {
    @Override
    public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException {
        ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
        HelloBean helloBean = context.getBean(HelloBean.class);
        res.getWriter().write("Hello from: " + helloBean.sayHello());
    }
}

 

6. Deploy a ApplicationContext as a Jakarta EE RAR file

항목 설명
RAR 배포란?
Spring ApplicationContext를 RAR 파일로 만들어 Jakarta EE 환경에 배포하는 방법
사용 시점
HTTP 엔트리 포인트가 필요 없는 메시지 처리, 스케줄링 등의 목적일 때 적합
장점
JTA, JNDI, JMS 등 Jakarta EE 리소스와 통합 가능.
Spring의 트랜잭션, JNDI, JMX 지원 사용 가능
RAR 구성
1. 모든 클래스와 라이브러리를 포함한 .rar 생성
2. META-INF/ra.xml 및 applicationContext.xml 포함
3. 서버 배포 디렉터리에 RAR 파일 배치
관련 클래스
SpringContextResourceAdapter를 참고 (구체 설정은 Javadoc 참고)

 


출처