Spring Bean 초기 화 및 소각 다양한 실현 방식
머리말
일상적인 개발 과정 은 응용 프로그램 이 시 작 된 후에 일부 자원 을 불 러 오 거나 응용 프로그램 이 닫 히 기 전에 자원 을 방출 해 야 할 때 가 있다.Spring 프레임 워 크 는 관련 기능 을 제공 합 니 다.Spring Bean 의 생명 주 기 를 중심 으로 Bean 생 성 과정 에서 자원 을 초기 화하 고 Bean 과정 에서 자원 을 방출 할 수 있 습 니 다.Spring 은 다양한 방식 으로 Bean 을 초기 화/소각 합 니 다.이 몇 가지 방식 을 동시에 사용 하면 Spring 은 이 몇 가지 순 서 를 어떻게 처리 합 니까?
2.자세 분석
우선 Spring 초기 화/Bean 소각 방식 을 살 펴 보 자.
2.1、init-method/destroy-method
이 방식 은 설정 파일 에서 초기 화/소각 방법 을 지정 합 니 다.XML 설정 은 다음 과 같 습 니 다.
<bean id="demoService" class="com.dubbo.example.provider.DemoServiceImpl" destroy-method="close" init-method="initMethod"/>
또는 주석 으로 설정 할 수도 있 습 니 다:
@Configurable
public class AppConfig {
@Bean(initMethod = "init", destroyMethod = "destroy")
public HelloService hello() {
return new HelloService();
}
}
스프링 프레임 워 크 를 처음 접 했 을 때 사용 하 는 것 이 이런 방식 이 었 던 것 으로 기억 된다.2.2、InitializingBean/DisposableBean
이 방식 은 Spring 인터페이스 InitializingBean/DisposableBean 을 계승 해 야 합 니 다.그 중에서 InitializingBean 은 동작 을 초기 화 하 는 데 사용 되 고 DisposableBean 은 제거 하기 전에 동작 을 정리 하 는 데 사 용 됩 니 다.사용 방식 은 다음 과 같 습 니 다.
@Service
public class HelloService implements InitializingBean, DisposableBean {
@Override
public void destroy() throws Exception {
System.out.println("hello destroy...");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("hello init....");
}
}
2.3、@PostConstruct/@PreDestroy이런 방식 은 위의 두 가지 방식 에 비해 사용 방식 이 가장 간단 하고 해당 하 는 방법 에 주 해 를 사용 하면 된다.사용 방식 은 다음 과 같 습 니 다.
@Service
public class HelloService {
@PostConstruct
public void init() {
System.out.println("hello @PostConstruct");
}
@PreDestroy
public void PreDestroy() {
System.out.println("hello @PreDestroy");
}
}
여기에 구 덩이 를 밟 았 습 니 다.JDK 9 이후 버 전 을 사용 하면@PostConstruct/@PreDestroy 는 Maven 을 사용 하여 javax.annotation-api 를 단독으로 도입 해 야 합 니 다.여부 자 설명 은 유효 하지 않 습 니 다.2.4、ContextStartedEvent/ContextClosedEvent
이런 방식 은 Spring 이벤트 메커니즘 을 사용 하 는데 일상적인 업무 개발 이 비교적 드 물고 프레임 워 크 와 통합 되 는 데 자주 사용 된다.Spring 이 시작 되면 ContextStarted Event 이 벤트 를 보 내 고 닫 기 전에 ContextClosed Event 이 벤트 를 보 냅 니 다.우 리 는 Spring Application Listener 를 계승 해 야 상기 두 가지 사건 을 감청 할 수 있다.
@Service
public class HelloListener implements ApplicationListener {
@Override
public void onApplicationEvent(ApplicationEvent event) {
if(event instanceof ContextClosedEvent){
System.out.println("hello ContextClosedEvent");
}else if(event instanceof ContextStartedEvent){
System.out.println("hello ContextStartedEvent");
}
}
}
@EventListener 주 해 를 사용 할 수도 있 습 니 다.사용 방식 은 다음 과 같 습 니 다.
public class HelloListenerV2 {
@EventListener(value = {ContextClosedEvent.class, ContextStartedEvent.class})
public void receiveEvents(ApplicationEvent event) {
if (event instanceof ContextClosedEvent) {
System.out.println("hello ContextClosedEvent");
} else if (event instanceof ContextStartedEvent) {
System.out.println("hello ContextStartedEvent");
}
}
}
PS:Application Context\#start 를 호출 해야만 ContextStarted Event 를 보 낼 수 있 습 니 다.이렇게 귀 찮 게 하고 싶 지 않 으 면 ContextRefreshedEvent 사건 을 감청 해서 대체 할 수 있다.스프링 용기 초기 화가 완료 되면 ContextRefreshedEvent 를 발송 합 니 다.3.종합 적 으로 사용
위의 몇 가지 방식 을 돌 이 켜 보면 위의 네 가지 방식 을 종합 적 으로 사용 하여 Spring 내부 의 처리 순 서 를 살 펴 보 자.결 과 를 보기 전에 독자 여러분 은 이 몇 가지 방식 의 집행 순 서 를 추측 할 수 있 습 니 다.
public class HelloService implements InitializingBean, DisposableBean {
@PostConstruct
public void init() {
System.out.println("hello @PostConstruct");
}
@PreDestroy
public void PreDestroy() {
System.out.println("hello @PreDestroy");
}
@Override
public void destroy() throws Exception {
System.out.println("bye DisposableBean...");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("hello InitializingBean....");
}
public void xmlinit(){
System.out.println("hello xml-init...");
}
public void xmlDestory(){
System.out.println("bye xmlDestory...");
}
@EventListener(value = {ContextClosedEvent.class, ContextStartedEvent.class})
public void receiveEvents(ApplicationEvent event) {
if (event instanceof ContextClosedEvent) {
System.out.println("bye ContextClosedEvent");
} else if (event instanceof ContextStartedEvent) {
System.out.println("hello ContextStartedEvent");
}
}
}
xml 설정 방식 은 다음 과 같 습 니 다.
<context:annotation-config />
<context:component-scan base-package="com.dubbo.example.demo"/>
<bean class="com.dubbo.example.demo.HelloService" init-method="xmlinit" destroy-method="xmlDestory"/>
시작 방법 은 다음 과 같 습 니 다.
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring/dubbo-provider.xml");
context.start();
context.close();
프로그램 출력 결 과 는 다음 과 같 습 니 다.마지막 으로 그림 설명 으로 상기 결 과 를 요약 한다.
소스 코드 분석
독자 여러분 이 이 몇 가지 방식 의 집행 순 서 를 맞 혔 는 지 모 르 겠 지만,다음은 스프링 내부 처리 순 서 를 소스 코드 각도 에서 분석 하 겠 습 니 다.
4.1 초기 화 과정
ClassPathXmlApplication Context 를 사용 하여 Spring 용 기 를 시작 하면 refresh 방법 으로 용 기 를 초기 화 합 니 다.초기 화 과정 에서 Bean 이 생 성 됩 니 다.마지막 으로 모든 준비 가 완료 되면 ContextRefreshedEvent 를 보 냅 니 다.용기 초기 화가 완료 되면 context.start()를 호출 하면 ContextStarted Event 이 벤트 를 보 냅 니 다.
refresh 방법 원본 코드 는 다음 과 같 습 니 다.
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//...
// Bean
finishBeanFactoryInitialization(beanFactory);
// ContextRefreshedEvent
finishRefresh();
//...
}
}
AbstractAutowireCapableBeanFactory\#initializeBean 까지 finishBeanFactory 초기 화 원본 코드 를 추적 합 니 다.원본 코드 는 다음 과 같 습 니 다.
protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
// BeanPostProcessor#postProcessBeforeInitialization
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
// Bean
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}
}
BeanPostProcessor 는 차단기 역할 을 하 며,Bean 이 조건 에 맞 으 면 일부 처 리 를 수행 합 니 다.여기에@PostConstruct 주석 이 있 는 Bean 은 CommonAnnotationBeanPostProcessor 류 에 의 해 차단 되 고 내부 에@PostConstruct 레이 블 방법 이 실 행 됩 니 다.이어서 invokeInitMethods 를 실행 합 니 다.방법 은 다음 과 같 습 니 다.
protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd)
throws Throwable {
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
//
// Bean InitializingBean, afterPropertiesSet
((InitializingBean) bean).afterPropertiesSet();
}
if (mbd != null) {
String initMethodName = mbd.getInitMethodName();
if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.isExternallyManagedInitMethod(initMethodName)) {
// XML init-method
invokeCustomInitMethod(beanName, bean, mbd);
}
}
}
빈 이 InitialingBean 인 터 페 이 스 를 계승 하면 after PropertiesSet 방법 을 실행 합 니 다.또한 XML 에 init-method 를 지정 하면 트리거 됩 니 다.위의 소스 코드 는 빈 을 중심 으로 만 드 는 과정 입 니 다.모든 빈 이 만 든 후에 context\#start 를 호출 하면 ContextStarted Event 를 보 냅 니 다.여기 소스 코드 는 비교적 간단 합 니 다.다음 과 같 습 니 다.
public void start() {
getLifecycleProcessor().start();
publishEvent(new ContextStartedEvent(this));
}
4.2 소각 과정ClassPathXmlApplication Context\#close 방법 을 호출 하면 용 기 를 닫 고 구체 적 인 논 리 는 doClose 방법 에서 실 행 됩 니 다.
doClose 이 방법 은 먼저 ContextClosed Event 를 보 낸 다음 에 Bean 을 소각 하기 시작 합 니 다.
영혼 고문:우리 가 위의 두 순 서 를 뒤 바 꾸 면 결 과 는 같 을 까?
doClose 소스 코드 는 다음 과 같 습 니 다.
protected void doClose() {
if (this.active.get() && this.closed.compareAndSet(false, true)) {
//
try {
// Publish shutdown event.
publishEvent(new ContextClosedEvent(this));
}
catch (Throwable ex) {
logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", ex);
}
// Bean
destroyBeans();
//
}
}
destroyBeans 는 최종 적 으로 DisposableBeanAdapter\#destroy,@PreDestroy,DisposableBean,destroy-method 세 가지 정의 방법 을 내부 에서 실행 합 니 다.우선 Destruction AwareBeanPostProcessor\#postprocessBeforeDestruction 을 실행 합 니 다.이 방법 은 위의 BeanPostProcessor 와 유사 합 니 다.
@PreDestroy 주 해 는 CommonAnnotationBeanPostProcessor 에 의 해 차단 되 며,이 클래스 는 Destruction Aware BeanPostProcessor 를 계승 합 니 다.
마지막 으로 빈 이 Disposable Bean 의 하위 클래스 라면 destroy 방법 을 실행 하고 xml 에서 destroy-method 방법 을 정의 하면 이 방법 도 실 행 됩 니 다.
public void destroy() {
if (!CollectionUtils.isEmpty(this.beanPostProcessors)) {
for (DestructionAwareBeanPostProcessor processor : this.beanPostProcessors) {
processor.postProcessBeforeDestruction(this.bean, this.beanName);
}
}
if (this.invokeDisposableBean) {
//
// Bean DisposableBean, destroy
((DisposableBean) bean).destroy();
}
if (this.destroyMethod != null) {
// xml destroy-method
invokeCustomDestroyMethod(this.destroyMethod);
}
else if (this.destroyMethodName != null) {
Method methodToCall = determineDestroyMethod();
if (methodToCall != null) {
invokeCustomDestroyMethod(methodToCall);
}
}
}
총화init-method/destroy-method 는 XML 설정 파일 이나 단독 주석 설정 클래스 를 사용 해 야 하기 때문에 상대 적 으로 번거롭다.InitialingBean/DisposableBean 은 Spring 의 인 터 페 이 스 를 단독으로 계승 하여 관련 방법 을 실현 해 야 합 니 다.@PostConstruct/@PreDestroy 라 는 주해 방식 은 사용 방식 이 간단 하고 코드 가 뚜렷 하 므 로 이런 방식 을 사용 하 는 것 을 추천 합 니 다.
또한 ContextStarted Event/ContextClosed Event 라 는 방식 은 일부 통합 프레임 워 크 에서 사용 하기에 비교적 적합 하 다.예 를 들 어 Dubbo 2.6.X 우아 한 정지 기 는 바로 메커니즘 을 바 꾸 는 것 이다.
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
thymeleaf로 HTML 페이지를 동적으로 만듭니다 (spring + gradle)지난번에는 에서 화면에 HTML을 표시했습니다. 이번에는 화면을 동적으로 움직여보고 싶기 때문에 입력한 문자를 화면에 표시시키고 싶습니다. 초보자의 비망록이므로 이상한 점 등 있으면 지적 받을 수 있으면 기쁩니다! ...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.