[봄 주제] Spring의 Bean 수명 주기 소스 코드 분석 - 4단계(Bean 파괴)(확장, 이해)

머리말

여기서 말하는 것은 Bean의 소멸 과정이다. 아마도 많은 친구들이 Beans의 파괴에 대해 이야기할 때 가비지 컬렉션(garbage collection)을 떠올릴 수도 있을 것입니다. 둘 다 수명 주기의 마지막 부분을 수행하지만 실제로는 동일한 작업이 아닙니다.가비지 수집은 JVM 수준의 작업이고 여기서 언급된 Bean 파괴는 Spring의 작업입니다., 당연히 같은 것은 아닙니다.

독서 조언

이 강의의 내용은 다음 코드를 시작점으로 사용하여 설명됩니다.

		// 注册Bean的销毁接口
		try {
    
    
			registerDisposableBeanIfNecessary(beanName, bean, mbd);
		}
		catch (BeanDefinitionValidationException ex) {
    
    
			throw new BeanCreationException(
					mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
		}

		return exposedObject;

이 코드는 실제로 Spring 인스턴스화 방법에 있습니다 AbstractAutowireCapableBeanFactory#doCreateBean(). 게다가 이 메소드 이름은 다들 아시겠지만, 이 단계는 실제 소멸이 아닌 소멸 로직을 등록하는 단계일 뿐입니다. 특정 조건을 만족해야만 파괴됩니다.
registerDisposableBeanIfNecessary구체적인 코드는 다음과 같습니다.

  /**
     * 将给定bean添加到此工厂中的一次性bean列表中,注册其DisposableBean接口和/或给定的destroy方法,以便在工厂关闭时调用(如果适用)。只适用于单例。 
     * 参数: 
     * beanName—bean的名称—bean实例mbd—bean的bean定义 
     * 参见: 
     * RootBeanDefinition。isSingleton RootBeanDefinition。getDependsOn, registerDisposableBean, registerDependentBean
     */
    protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) {
    
    
        AccessControlContext acc = (System.getSecurityManager() != null ? getAccessControlContext() : null);
        if (!mbd.isPrototype() && requiresDestruction(bean, mbd)) {
    
    
            if (mbd.isSingleton()) {
    
    
                // Register a DisposableBean implementation that performs all destruction
                // work for the given bean: DestructionAwareBeanPostProcessors,
                // DisposableBean interface, custom destroy method.
                registerDisposableBean(beanName, new DisposableBeanAdapter(
                        bean, beanName, mbd, getBeanPostProcessorCache().destructionAware, acc));
            } else {
    
    
                // A bean with a custom scope...
                Scope scope = this.scopes.get(mbd.getScope());
                if (scope == null) {
    
    
                    throw new IllegalStateException("No Scope registered for scope name '" + mbd.getScope() + "'");
                }
                scope.registerDestructionCallback(beanName, new DisposableBeanAdapter(
                        bean, beanName, mbd, getBeanPostProcessorCache().destructionAware, acc));
            }
        }
    }

코스 내용

1. 빈은 언제 폐기되나요?

Spring 컨테이너가 종료되는 동안 Bean 파괴가 발생합니다. 이때 Spring의 모든 싱글톤 Bean은 소멸되며, 사용자 정의 소멸 로직을 구현한 Bean의 소멸 메소드가 실행됩니다.. 이번 글에서 소개할 내용은 커스텀 Bean 소멸 로직을 구현하는 방법 이다 .
Spring 컨테이너가 닫히면 다음과 같이 닫힌 상태로 표시될 수 있습니다.

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = (UserService) context.getBean("userService");
userService.test();

// 容器关闭
context.close();

또는 종료 후크를 등록합니다.

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

// 注册关闭钩子
context.registerShutdownHook();

Object newUser = context.getBean("user");
System.out.println(newUser);

[참고: 프로세스를 강제로 종료(pid 종료)하면 사용자 정의 Bean 삭제 로직이 호출되지 않습니다.]

Spring이 컨테이너를 닫는 프로세스:

  1. 먼저 ContextClosedEvent 이벤트를 게시하세요.
  2. lifecycleProcessor의 onClose() 메서드를 호출합니다.
  3. 싱글톤 빈을 파괴하라
    • 일회용 빈에 대한 반복
      • 싱글톤 풀에서 각 temporaryBean을 제거합니다.
      • 일회용Bean의 destroy()를 호출하십시오.
      • 이 일회용 빈이 다른 빈에도 종속되어 있는 경우 다른 빈도 파기해야 합니다.
      • 이 일회용 빈에도 내부 빈이 포함되어 있으면 싱글톤 풀에서 해당 빈을 제거하세요.
    • 사용자가 수동으로 등록한 싱글톤 Bean의 beanName을 저장하는 Set인 manualSingletonNames를 지웁니다.
    • allBeanNamesByType을 지웁니다. 이는 Map이고 키는 Bean 유형이며 값은 이 유형의 모든 beanNames 배열입니다.
    • 싱글톤 Bean만 저장된다는 점을 제외하면 allBeanNamesByType과 유사한 빈 싱글톤BeanNamesByType입니다.

2. 맞춤형 Bean 소멸 로직 구현

구현 방법은 다음과 같습니다.

2.1 DisposableBean 또는 AutoCloseable 인터페이스 구현

사용자 정의 소멸이 필요한 Bean 코드 예: (구현: DisposableBean)

@Component
public class TestDestroyBean implements DisposableBean {
    
    
    public void test() {
    
    
        System.out.println("测试一下销毁方法");
    }

    @Override
    public void destroy() throws Exception {
    
    
        System.out.println("TestDestroyBean------自定义的Bean销毁方法");
    }
}

또는: (구현: AutoCloseable)

@Component
public class TestDestroyBean implements AutoCloseable {
    
    
    public void test() {
    
    
        System.out.println("测试一下销毁方法");
    }
    
    @Override
    public void close() throws Exception {
    
    
        System.out.println("TestDestroyBean------自定义的Bean销毁方法");
    }
}

2.2 @PreDestroy 주석 사용하기

세 가지 구현 방법이 있습니다.

@Component
public class TestDestroyBean {
    
    
    public void test() {
    
    
        System.out.println("测试一下销毁方法");
    }

    @PreDestroy
    public void close() throws Exception {
    
    
        System.out.println("TestDestroyBean------自定义的Bean销毁方法");
    }
}

2.3 기타 방법(파기 방법 이름을 수동으로 지정)

물론 다음과 같은 다른 방법도 있습니다.

<bean destroy-method='xxx'>

또는:

@Bean(destroyMethod = "xxx")

또는 beanDefinition파괴 방법을 직접 지정하십시오.

@Component
public class MyBeanPostProcessor implements MergedBeanDefinitionPostProcessor {
    
    
    @Override
    public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
    
    
        if (beanName.equals("user")) {
    
    
            beanDefinition.setInitMethodName("myInit");
            beanDefinition.setDestroyMethodName("xxxx");
        }
    }
}

위에 언급된 세 가지 방법은 수동으로 지정되므로 특별한 위치를 가지며 특별한 값을 설정할 수 있습니다 (inferred).
파기 메소드 이름을 이렇게 설정하고 해당 빈이 구현되지 않은 경우 파기 과정에서 해당 빈 아래에 메소드가 있는지 DisposableBean검사하게 된다 . 만약 그렇다면, 【사용자 정의 파기 방법】에 자동으로 바인딩됩니다.closeshutdown

3. Bean의 등록 및 파기절차 및 방법에 대한 자세한 설명

이 파괴 프로세스에는 【3개의 핵심 클래스, 6개의 핵심 메서드】가 포함됩니다.

3.1 AbstractBeanFactory#requiresDestruction: 이를 파기해야 합니까?

메소드 호출 체인: 항목에서:
전체 경로의 RegisterDisposableBeanIfNecessary() 호출: org.springframework.beans.factory.support.AbstractBeanFactory#requiresDestruction
메소드 주석: 지정된 Bean을 이 팩토리의 일회용 Bean 목록에 추가하고 DisposableBean을 등록합니다. 공장이 닫힐 때 호출할 인터페이스 및/또는 주어진 파괴 메소드(해당되는 경우). 싱글톤에만 적용됩니다.

소스 코드는 다음과 같습니다.

protected boolean requiresDestruction(Object bean, RootBeanDefinition mbd) {
    
    
    return (
            bean.getClass() != NullBean.class && (
                    DisposableBeanAdapter.hasDestroyMethod(bean, mbd) 
                    || (hasDestructionAwareBeanPostProcessors() 
                            && DisposableBeanAdapter.hasApplicableProcessors(bean, getBeanPostProcessorCache().destructionAware))
            )
    );
}

방법 해석: 내부의 핵심 소스 코드는 실제로 두 단계로 나뉩니다. 다음과 같이:

  1. 지정된 파괴 방법이 있습니까?DisposableBeanAdapter.hasDestroyMethod(bean, mbd)
  2. DestructionAwareBeanPostProcessor파괴를 감지할 수 있는 Bean 후처리자가 있는지 여부 ( hasDestructionAwareBeanPostProcessors). 있다면 트래버스DisposableBeanAdapter.hasApplicableProcessors(bean, getBeanPostProcessorCache().destructionAware)

3.2 DisposableBeanAdapter.hasDestroyMethod : 파기방법 유무

메소드 호출 체인: 3.1의 requireDestruction()에 의해 호출됨
전체 경로: org.springframework.beans.factory.support.DisposableBeanAdapter#hasDestroyMethod
메소드 주석: 지정된 Bean에 호출할 소멸 메소드가 있는지 확인하십시오.

소스 코드는 다음과 같습니다.

public static boolean hasDestroyMethod(Object bean, RootBeanDefinition beanDefinition) {
    
    
	return (bean instanceof DisposableBean || inferDestroyMethodIfNecessary(bean, beanDefinition) != null);
}

내부 내용은 실제로 매우 간단하며, 단계는 다음과 같습니다.

  1. 현재 Bean이 DisposableBean 인터페이스를 구현하는지 여부
  2. 그렇지 않은 경우 inferDestroyMethodIfNecessary추론된 소멸 메서드를 호출합니다(나중에 설명).

3.3 DisposableBeanAdapter#inferDestroyMethodIfNecessary: ​​​​파괴 방법 유추

메소드 호출 체인: 3.2에서 hasDestroyMethod()에 의해 호출됨
전체 경로: org.springframework.beans.factory.support.DisposableBeanAdapter#inferDestroyMethodIfNecessary
메소드 설명:
주어진 beanDefinition의 "destroyMethodName" 속성의 현재 값이 AbstractBeanDefinition인 경우. 그런 다음 파괴 방법을 추론해 보십시오. 후보 메서드는 현재 'close' 또는 'shutdown'이라는 매개 변수 없는 공용 메서드(로컬 선언 또는 상속 여부)로 제한됩니다. 그러한 메소드가 발견되지 않으면 주어진 BeanDefinition의 "destroyMethodName"을 null로 업데이트하고, 그렇지 않으면 추론된 메소드의 이름으로 설정됩니다. 이 상수는 @Bean#destroyMethod 속성의 기본값으로 사용되며, 이 상수의 값은 XML이나 속성에서도 사용할 수 있다. 또한 java.io.Closeable 및 AutoCloseable 인터페이스를 처리하고 Bean이 구현될 때 반사적으로 "close" 메소드를 호출합니다.

소스 코드는 다음과 같습니다.

	@Nullable
	private static String inferDestroyMethodIfNecessary(Object bean, RootBeanDefinition beanDefinition) {
    
    
		String destroyMethodName = beanDefinition.resolvedDestroyMethodName;
		if (destroyMethodName == null) {
    
    
			destroyMethodName = beanDefinition.getDestroyMethodName();
			boolean autoCloseable = (bean instanceof AutoCloseable);
			if (AbstractBeanDefinition.INFER_METHOD.equals(destroyMethodName) ||
					(destroyMethodName == null && autoCloseable)) {
    
    

				// 当销毁方法名字等于"(inferred)",且bean不是DisposableBean实现类
				destroyMethodName = null;
				if (!(bean instanceof DisposableBean)) {
    
    
					if (autoCloseable) {
    
    
						destroyMethodName = CLOSE_METHOD_NAME;
					}
					else {
    
    
						try {
    
    
							destroyMethodName = bean.getClass().getMethod(CLOSE_METHOD_NAME).getName();
						}
						catch (NoSuchMethodException ex) {
    
    
							try {
    
    
								destroyMethodName = bean.getClass().getMethod(SHUTDOWN_METHOD_NAME).getName();
							}
							catch (NoSuchMethodException ex2) {
    
    
								// no candidate destroy method found
							}
						}
					}
				}
			}
			beanDefinition.resolvedDestroyMethodName = (destroyMethodName != null ? destroyMethodName : "");
		}
		return (StringUtils.hasLength(destroyMethodName) ? destroyMethodName : null);
	}

방법 해석: 해석할 것이 없고, 게시글을 반복하고 댓글을 달면 됩니다. 파괴 방법을 추론해 보세요. 후보 메서드는 현재 'close' 또는 'shutdown'이라는 매개 변수 없는 공용 메서드(로컬 선언 또는 상속 여부)로 제한됩니다. 그러한 메소드가 발견되지 않으면 주어진 BeanDefinition의 "destroyMethodName"을 null로 업데이트하고, 그렇지 않으면 추론된 메소드의 이름으로 설정됩니다. 이 상수는 @Bean#destroyMethod 속성의 기본값으로 사용되며, 이 상수의 값은 <bean destroy-method="">나 XML의 속성에서도 사용할 수 있다. 또한 java.io.Closeable 및 AutoCloseable 인터페이스를 처리하고 Bean이 구현될 때 반사적으로 "close" 메소드를 호출합니다.

3.4 AbstractBeanFactory#hasDestructionAwareBeanPostProcessors: 지각적 파괴가 있는지 여부 Bean 포스트 프로세서

메소드 호출 체인: 3.1에서 requireDestruction()에 의해 호출됨
전체 경로: org.springframework.beans.factory.support.AbstractBeanFactory#hasDestructionAwareBeanPostProcessors
메소드 설명: 팩토리가 닫힐 때 싱글에 적용될 DestructionAwareBeanPostProcessor를 보유하는지 여부를 반환합니다. 콩.

소스 코드는 다음과 같습니다.

	protected boolean hasDestructionAwareBeanPostProcessors() {
    
    
		return !getBeanPostProcessorCache().destructionAware.isEmpty();
	}

3.5 DisposableBeanAdapter.hasApplicableProcessors: 현재 Bean에 적용된 파괴 인식 Bean 후처리기가 있는지 여부

메소드 호출 체인: 3.1의 requireDestruction()에 의해 호출됨
전체 경로: org.springframework.beans.factory.support.DisposableBeanAdapter#hasApplicableProcessors
메소드 주석: 주어진 Bean에 파괴 인식 후처리 프로세서가 적용되어 있는지 확인하십시오.

소스 코드는 다음과 같습니다.

	public static boolean hasApplicableProcessors(Object bean, List<DestructionAwareBeanPostProcessor> postProcessors) {
    
    
		if (!CollectionUtils.isEmpty(postProcessors)) {
    
    
			for (DestructionAwareBeanPostProcessor processor : postProcessors) {
    
    
				if (processor.requiresDestruction(bean)) {
    
    
					return true;
				}
			}
		}
		return false;
	}

클래식 BeanPostProcessor가 이를 처리했으므로 이에 대해 이야기하지 않겠습니다.

3.6 DefaultSingletonBeanRegistry#registerDisposableBean: 파기되어야 하는 Bean을 등록합니다.

메소드 호출 체인: 항목에서 호출됨:registerDisposableBeanIfNecessary()
전체 경로: org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#registerDisposableBean
메소드 주석: 이 레지스트리의 파괴된 Bean 목록에 지정된 Bean을 추가합니다.

소스 코드는 다음과 같습니다.

private final Map<String, Object> disposableBeans = new LinkedHashMap<>();


public void registerDisposableBean(String beanName, DisposableBean bean) {
    
    
	synchronized (this.disposableBeans) {
    
    
		this.disposableBeans.put(beanName, bean);
	}
}

메소드 해석: 소위 등록은 실제로 현재 Bean과 일부 정보를 캐시 맵에 추가하는 것입니다. 필요할 땐 지도를 직접 탐색하면 됩니다

3.7 Bean 등록 및 파기 과정 요약

전반적으로 2단계가 있습니다.

  1. 싱글톤 빈이며, 파기해야 하는지 판단됩니다. 판단 단계는 다음과 같습니다. (다양한 Spring 버전의 세부 사항은 다르지만 전체는 일관됩니다.)
    • 현재 Bean이 해당 DisposableBean인터페이스를 구현하는지 여부, 그렇다면 true를 직접 반환하고, 그렇지 않으면 [파괴 방법 유추] 과정을 진행한다.
    • 추론된 파괴 방법
      • Definition에 destroyMethod가 지정되어 있는지 여부 및 destroyMethod==(inferred). close그렇다면 현재 Bean 아래에 메소드나 메소드가 있는지 확인하고 shutdown, 그렇다면 소멸 메소드의 이름을 직접 리턴하십시오.
      • 또는 현재 Bean이 AutoCloseable 인터페이스를 구현하는지 여부, 그렇다면 소멸 메서드의 이름을 직접 반환합니다.
    • [Inferred Destruction Method]에 결과가 없으면 [Aware Destruction Bean Post Processor] DestructionAwareBeanPostProcessor.requiresDestruction(bean)을 호출하여 판단한다.
      • ApplicationListenerDetector에서 직접 현재 Bean이 ApplicationListener 하위 클래스인 경우 이를 삭제해야 합니다.
      • InitDestroyAnnotationBeanPostProcessor는 @PreDestroy 주석이 있는 메서드를 삭제해야 하도록 만듭니다.
  2. 파기해야 하는 경우 이를 DisposableBeanAdapter 객체에 적용하고 DisposableBeans(LinkedHashMap)에 저장합니다.

4. Bean 로직 흐름도 등록 및 삭제

여기에 이미지 설명을 삽입하세요

5. 컨셉 검토

여기서 사용되는 가장 중요한 후처리기 중 하나는 InitDestroyAnnotationBeanPostProcessor다음과 같이 정의된다는 것입니다.

    /**
     *
     通用场景
     术语库
     beanpostprocessor实现,调用带注释的init和destroy方法。允许一个注释替代Spring的org.springframework.beans.factory.InitializingBean和org.springframework.beans.factory.DisposableBean回调接口。
     这个后处理器检查的实际注释类型可以通过“initAnnotationType”和“destroyAnnotationType”属性来配置。可以使用任何自定义注释,因为没有必需的注释属性。
     Init和destroy注释可以应用于任何可见性的方法:public、package-protected、protected或private。可以注释多个这样的方法,但建议分别只注释一个init方法和destroy方法。
     Spring的
     org.springframework.context.annotation.CommonAnnotationBeanPostProcessor
     支持JSR-250开箱即用的javax.annotation.PostConstruct和javax.annotation.PreDestroy注释,
     就像init annotation和destroy annotation一样。此外,它还支持javax.annotation.Resource注释,
     用于注释驱动的命名bean注入。
     自:
     2.5
     参见:
     setInitAnnotationType, setDestroyAnnotationType
     作者:
     Juergen hoel
     以上翻译结果来自有道神经网络翻译(YNMT)· 通用场景
     逐句对照
     */
     public class InitDestroyAnnotationBeanPostProcessor
		implements DestructionAwareBeanPostProcessor, MergedBeanDefinitionPostProcessor, PriorityOrdered, Serializable {
    
    
}

요약하다

  1. Bean 파괴 과정을 배웠습니다.
  2. Bean을 등록하고 파기하는 논리를 배웠습니다.
  3. Bean 파괴 로직을 사용자 정의하는 방법을 배웠습니다.

Supongo que te gusta

Origin blog.csdn.net/qq_32681589/article/details/132316780
Recomendado
Clasificación