Spring 에서 비동기 주해@Async 의 사용,원리 및 사용 시 발생 할 수 있 는 문제 및 해결 방법

머리말
사실 최근 에 업무 와 관련 된 내용 을 연구 하고 있 습 니 다.이런 글 을 쓴 이 유 는 앞에서 순환 의존 에 관 한 글 을 썼 기 때 문 입 니 다.
Spring 순환 의존 해결 방법,정말 알 겠 어?
그 다음 에 많은 학생 들 이 다음 과 같은 문 제 를 만 났 습 니 다.Spring 이 제공 하 는 비동기 주 해 를 추가 하여 @Async 순환 의존 이 해결 되 지 않 았 습 니 다.다음은 일부 독자 들 의 댓 글 과 단체 친구 들 이 만 나 는 문제 입 니 다.


지식 을 말 하려 면 명확 하고 투철 한 원칙 을 말 해 야 한다.나 는 이런 글 을 단독으로 써 서 @Async 이라는 주 해 를 상세 하 게 소개 하기 로 결정 했다.이 주해 가 가 져 온 문 제 는 순환 의존 이 이렇게 간단 한 것 이 아니 라 익숙 하지 않 으 면 신중하게 사용 하기 로 했다.
문장의 요점

@Async 의 기본 사용
이 주해 의 역할 은 표 시 된 방법 을 비동기 적 으로 집행 할 수 있 지만 두 가지 전제조건 이 있다.
설정 클래스 에 @EnableAsync 주 해 를 추가 하여 비동기 실행 이 필요 한 방법 이 있 는 종 류 는 Spring 관리 에 비동기 실행 이 필요 한 방법 에 @Async 주 해 를 추가 하 였 습 니 다.
저희 가 데모 하나 로 이 주해 의 역할 을 알 아 보도 록 하 겠 습 니 다.
첫 번 째,설정 클래스 에서 비동기 시작:

@EnableAsync
@Configuration
@ComponentScan("com.dmz.spring.async")
public class Config {

}
두 번 째 단계,
[code] @Component // Spring public class DmzAsyncService { @Async //

@Component //        Spring  
public class DmzAsyncService {
 
	@Async //                
	public void testAsync(){
		try {
			TimeUnit.SECONDS.sleep(1);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("testAsync invoked");
	}
}
세 번 째 단계,비동기 실행 테스트

public class Main {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);
		DmzAsyncService bean = ac.getBean(DmzAsyncService.class);
		bean.testAsync();
		System.out.println("main      ");
	}
}
//         :
// main      
// testAsync invoked
위의 예 를 통 해 우 리 는 DmzAsyncService 중의 testAsync 방법 이 비동기 적 으로 집행 되 었 다 는 것 을 알 수 있다.그러면 이 뒤의 원 리 는 무엇 일 까?이어서 분석 하 겠 습 니 다.
원리 분석
우리 가 어떤 기술 을 분석 할 때 가장 중요 한 것 은 반드시 코드 의 입 구 를 찾 아야 한 다 는 것 이다.Spring 과 같은 것 은 모두 뚜렷 하 다.입 구 는 반드시 @EnableAsync 이라는 주해 위 에 있 을 것 이다.우 리 는 이 주해 가 무슨 일 을 했 는 지 살 펴 보 자(본 고 는 5.2.x 판 을 바탕 으로)

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
//      ,     ImportSelector
@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync {
 
 //                     ,          @Async  
	Class<? extends Annotation> annotation() default Annotation.class;
	
 //     jdk  
	boolean proxyTargetClass() default false;
	
 //     Spring AOP
	AdviceMode mode() default AdviceMode.PROXY;
	
 //           ,               
 // AsyncAnnotationBeanPostProcessor,          Ordered  
 //          AsyncAnnotationBeanPostProcessor     
	int order() default Ordered.LOWEST_PRECEDENCE;
}
위의 이 주해 에서 가장 중요 한 일 은 바로 AsyncConfigurationSelector 을 가 져 온 것 이다.이런 종류의 소스 코드 는 다음 과 같다.

public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {

	private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME =
			"org.springframework.scheduling.aspectj.AspectJAsyncConfiguration";

	@Override
	@Nullable
	public String[] selectImports(AdviceMode adviceMode) {
		switch (adviceMode) {
 //      SpringAOP    
			case PROXY:
				return new String[] {ProxyAsyncConfiguration.class.getName()};
			case ASPECTJ:
				return new String[] {ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME};
			default:
				return null;
		}
	}

}
이러한 역할 은 용기 에 ProxyAsyncConfiguration 을 등록 한 것 과 같은 것 으로 이런 계승 관 계 는 다음 과 같다.

우 리 는 먼저 그것 의 아버지 류 AbstractAsyncConfiguration 을 보 았 는데 그 소스 코드 는 다음 과 같다.

@Configuration
public abstract class AbstractAsyncConfiguration implements ImportAware {
	
	@Nullable
	protected AnnotationAttributes enableAsync;

	@Nullable
	protected Supplier<Executor> executor;

	@Nullable
	protected Supplier<AsyncUncaughtExceptionHandler> exceptionHandler;
	
 //                   EnableAsync  
 //          
	@Override
	public void setImportMetadata(AnnotationMetadata importMetadata) {
		this.enableAsync = AnnotationAttributes.fromMap(
				importMetadata.getAnnotationAttributes(EnableAsync.class.getName(), false));
		if (this.enableAsync == null) {
			throw new IllegalArgumentException(
					"@EnableAsync is not present on importing class " + importMetadata.getClassName());
		}
	}
 
 //        AsyncConfigurer  
 //      ,              
 //             
	@Autowired(required = false)
	void setConfigurers(Collection<AsyncConfigurer> configurers) {
		if (CollectionUtils.isEmpty(configurers)) {
			return;
		}
		if (configurers.size() > 1) {
			throw new IllegalStateException("Only one AsyncConfigurer may exist");
		}
		AsyncConfigurer configurer = configurers.iterator().next();
		this.executor = configurer::getAsyncExecutor;
		this.exceptionHandler = configurer::getAsyncUncaughtExceptionHandler;
	}

}
ProxyAsyncConfiguration 같은 소스 코드 를 다시 보 겠 습 니 다.

@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyAsyncConfiguration extends AbstractAsyncConfiguration {

	@Bean(name = TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME)
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public AsyncAnnotationBeanPostProcessor asyncAdvisor() {
		AsyncAnnotationBeanPostProcessor bpp = new AsyncAnnotationBeanPostProcessor();
 //    AsyncConfigurer                        
 bpp.configure(this.executor, this.exceptionHandler);
		Class<? extends Annotation> customAsyncAnnotation = this.enableAsync.getClass("annotation");
		if (customAsyncAnnotation != AnnotationUtils.getDefaultValue(EnableAsync.class, "annotation")) {
			bpp.setAsyncAnnotationType(customAsyncAnnotation);
		}
		bpp.setProxyTargetClass(this.enableAsync.getBoolean("proxyTargetClass"));
		bpp.setOrder(this.enableAsync.<Integer>getNumber("order"));
		return bpp;
	}

}
이 클래스 자 체 는 하나의 설정 클래스 로 용기 에 AsyncAnnotationBeanPostProcessor 을 추가 하 는 역할 을 합 니 다.이 단계 에 이 르 러 우 리 는 기본적으로 알 수 있 습 니 다.@Async 주 해 는 AsyncAnnotationBeanPostProcessor 이라는 백 엔 드 프로 세 서 를 통 해 하나의 에이전트 대상 을 생 성하 여 이 보 를 실현 하 는 것 입 니 다.그 다음 에 AsyncAnnotationBeanPostProcessor 이 어떻게 에이전트 대상 을 생 성 하 는 지 구체 적 으로 살 펴 보 겠 습 니 다.우 리 는 주로 몇 가 지 를 주목 하면 됩 니 다.
  • 은 생명주기 의 어느 단계 에 완 성 된 대리 입 니까?
  • 절 점 의 논 리 는 어 떻 습 니까?그것 은 어떤 종 류 를 차단 합 니까?
  • 통지 의 논 리 는 어 떻 습 니까?어떻게 비동기 적 인 것 을 실현 합 니까?
  • 위의 몇 가지 문 제 를 바탕 으로 우 리 는 하나하나 분석 했다.
    라 이 프 사이클 의 어느 단계 에 완 성 된 대리 입 니까?
    우 리 는 중점 을 잡 았 습 니 다.AsyncAnnotationBeanPostProcessor 은 백 엔 드 프로세서 입 니 다.우리 가 Spring 에 대해 알 고 있 는 바 에 따 르 면 대략 이 백 엔 드 프로세서 의 postProcessAfterInitialization 방법 에서 대 리 를 완 성 했 습 니 다.이 방법 은 부모 류 AbstractAdvisingBeanPostProcessor 에 위치 하고 구체 적 인 코드 는 다음 과 같 습 니 다.
    
    public Object postProcessAfterInitialization(Object bean, String beanName) {
     //     ,   AOP      ,       
     if (this.advisor == null || bean instanceof AopInfrastructureBean) {
     return bean;
     }
    	
     //         ,      ,               
     //     beforeExistingAdvisors                          
     //    @Async     ,beforeExistingAdvisors     true
     //                    
     if (bean instanceof Advised) {
     Advised advised = (Advised) bean;
     if (!advised.isFrozen() && isEligible(AopUtils.getTargetClass(bean))) {
     if (this.beforeExistingAdvisors) {
     advised.addAdvisor(0, this.advisor);
     }
     else {
     advised.addAdvisor(this.advisor);
     }
     return bean;
     }
     }
    	
     //        Bean     
     if (isEligible(bean, beanName)) {
     ProxyFactory proxyFactory = prepareProxyFactory(bean, beanName);
     if (!proxyFactory.isProxyTargetClass()) {
     evaluateProxyInterfaces(bean.getClass(), proxyFactory);
     }
     proxyFactory.addAdvisor(this.advisor);
     customizeProxyFactory(proxyFactory);
     return proxyFactory.getProxy(getProxyClassLoader());
     }
     return bean;
    }
    아니 나 다 를 까,확실히 이 방법 에서 완 성 된 대리 다.이어서 우 리 는 절 점 의 여과 규칙 이 무엇 인지 생각해 야 한다.
    접점 의 논 리 는 어 떻 습 니까?
    사실 클래스 에 @Async 주 해 를 추가 하거나 클래스 에 @Async 주 해 를 수식 하 는 방법 이 들 어 있 음 을 짐작 하기 어렵 지 않다.이 를 바탕 으로 우 리 는 이 isEligible 방법의 실현 논 리 를 살 펴 보 자.이 방 위 는 AbstractBeanFactoryAwareAdvisingPostProcessor 에 있 고 AsyncAnnotationBeanPostProcessor 의 부류 이기 도 하 며 대응 코드 는 다음 과 같다.
    
    // AbstractBeanFactoryAwareAdvisingPostProcessor isEligible  
    //      
    protected boolean isEligible(Object bean, String beanName) {
     return (!AutoProxyUtils.isOriginalInstance(beanName, bean.getClass()) &&
     super.isEligible(bean, beanName));
    }
    
    protected boolean isEligible(Object bean, String beanName) {
     return isEligible(bean.getClass());
    }
    
    protected boolean isEligible(Class<?> targetClass) {
     Boolean eligible = this.eligibleBeans.get(targetClass);
     if (eligible != null) {
     return eligible;
     }
     if (this.advisor == null) {
     return false;
     }
     //        
     eligible = AopUtils.canApply(this.advisor, targetClass);
     this.eligibleBeans.put(targetClass, eligible);
     return eligible;
    }
    실제로 마지막 으로 advisor 에 따라 대 리 를 진행 할 지 여 부 를 확인 하 는 것 입 니 다.Spring 에서 xml 기반 AOP 의 상세 한 절차 이 글 에서 우 리 는 advisor 가 실제 적 으로 절 점 을 연결 한 통지 라 고 언급 한 적 이 있 습 니 다.그러면 AsyncAnnotationBeanPostProcessor 이 advisor 는 언제 초기 화 되 었 습 니까?우 리 는 AsyncAnnotationBeanPostProcessorsetBeanFactory 방법 을 직접 찾 았 는데 그 소스 코드 는 다음 과 같다.
    
    public void setBeanFactory(BeanFactory beanFactory) {
     super.setBeanFactory(beanFactory);
    	
     //    new   AsyncAnnotationAdvisor
     AsyncAnnotationAdvisor advisor = new AsyncAnnotationAdvisor(this.executor, this.exceptionHandler);
     if (this.asyncAnnotationType != null) {
     advisor.setAsyncAnnotationType(this.asyncAnnotationType);
     }
     advisor.setBeanFactory(beanFactory);
     //       
     this.advisor = advisor;
    }
    AsyncAnnotationAdvisor 의 절 점 일치 규정 이 어떤 지 살 펴 보 자.이런 유형의 buildPointcut 방법 에서 그 소스 코드 는 다음 과 같다.
    
    protected Pointcut buildPointcut(Set<Class<? extends Annotation>> asyncAnnotationTypes) {
     ComposablePointcut result = null;
     for (Class<? extends Annotation> asyncAnnotationType : asyncAnnotationTypes) {
     //                
     Pointcut cpc = new AnnotationMatchingPointcut(asyncAnnotationType, true);
     Pointcut mpc = new AnnotationMatchingPointcut(null, asyncAnnotationType, true);
     if (result == null) {
     result = new ComposablePointcut(cpc);
     }
     else {
     result.union(cpc);
     }
     result = result.union(mpc);
     }
     return (result != null ? result : Pointcut.TRUE);
    }
    코드 는 간단 합 니 다.cpc 와 mpc 두 개의 일치 기 에 따라 일치 합 니 다.첫 번 째 는 클래스 에@Async 주해 가 있 는 지,두 번 째 는@Async 주해 가 있 는 지 확인 하 는 것 입 니 다.
    그러면 지금까지 우 리 는 그것 이 언제 대 리 를 만 들 었 는 지,왜 대상 이 대 리 를 만 들 었 는 지 알 게 되 었 습 니 다.마지막 으로 우 리 는 문 제 를 해결 해 야 합 니 다.대리 의 논 리 는 어 떻 습 니까?비동기 가 어떻게 실현 되 었 습 니까?
    통지 의 논 리 는 어 떻 습 니까?어떻게 비동기 적 인 것 을 실현 합 니까?
    앞에서 도 advisor 는 절 점 을 연결 한 알림 이 라 고 언급 했 습 니 다.앞에서 절 점 을 분 석 했 습 니 다.그러면 지금 우 리 는 그의 통지 논 리 를 살 펴 보고 AsyncAnnotationAdvisor 중의 buildAdvice 방법 을 직접 찾 았 습 니 다.소스 코드 는 다음 과 같 습 니 다.
    
    protected Advice buildAdvice(
     @Nullable Supplier<Executor> executor, @Nullable Supplier<AsyncUncaughtExceptionHandler> exceptionHandler) {
    
     AnnotationAsyncExecutionInterceptor interceptor = new AnnotationAsyncExecutionInterceptor(null);
     interceptor.configure(executor, exceptionHandler);
     return interceptor;
    }
    간단 하 죠.차단 기 를 추 가 했 을 뿐 입 니 다.interceptor 유형의 대상 에 대해 우 리 는 그것 의 핵심 방법 인 invoke 에 관심 을 가지 면 됩 니 다.코드 는 다음 과 같 습 니 다.
    
    public Object invoke(final MethodInvocation invocation) throws Throwable {
     Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
     Method specificMethod = ClassUtils.getMostSpecificMethod(invocation.getMethod(), targetClass);
     final Method userDeclaredMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
    	
     //      ,         
     AsyncTaskExecutor executor = determineAsyncExecutor(userDeclaredMethod);
     if (executor == null) {
     throw new IllegalStateException(
     "No executor specified and no default executor set on AsyncExecutionInterceptor either");
     }
    	
     //              Callable           
     Callable<Object> task = () -> {
     try {
     Object result = invocation.proceed();
     if (result instanceof Future) {
     return ((Future<?>) result).get();
     }
     }
     catch (ExecutionException ex) {
     handleError(ex.getCause(), userDeclaredMethod, invocation.getArguments());
     }
     catch (Throwable ex) {
     handleError(ex, userDeclaredMethod, invocation.getArguments());
     }
     return null;
     };
    	//          
     return doSubmit(task, executor, invocation.getMethod().getReturnType());
    }
    초래 된 문제 및 해결 방안
    문제 1:순환 의존 오류
    이 그림 에서 이 독자 가 묻 는 질문 처럼

    두 가지 로 나 누 어 대답 하 다.
    첫째:순환 의존 은 왜 해결 되 지 않 습 니까?
    이 문 제 는 사실 매우 간단 하 다.라 는 글 에서 나 는 두 가지 측면 에서 순환 의존 하 는 처리 절 차 를 분석 했다.
    단순 대상 간 순환 의존 처리 AOP 대상 간 순환 의존 처리
    이런 사고방식 에 따 르 면 @Async 주해 로 인 한 순환 의존 은 AOP 에 속 하고 처 리 될 수 있어 야 한다.그러나 중요 한 것 은 AOP 대상 간 순환 의존 을 해결 하 는 핵심 방법 은 3 급 캐 시 입 니 다.다음 과 같 습 니 다.

    3 급 캐 시 에 공장 대상 을 캐 시 했 습 니 다.이 공장 대상 은 getEarlyBeanReference 방법 으로 초기 대리 대상 의 인용 을 얻 을 것 입 니 다.그 소스 코드 는 다음 과 같 습 니 다.
    
    protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
     Object exposedObject = bean;
     if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
     for (BeanPostProcessor bp : getBeanPostProcessors()) {
     //         ,  @EnableAsync        
     // AsyncAnnotationBeanPostProcessor       SmartInstantiationAwareBeanPostProcessor
     //            AsyncAnnotationBeanPostProcessor         
     //               Bean               
     if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
     SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
     exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
     }
     }
     }
     return exposedObject;
    }
    위의 코드 순환 의존 문 제 를 보면 분명 하 다.초기 에 노출 된 대상 이 최종 적 으로 용기 에 넣 은 대상 과 같 지 않 기 때문에 잘못 보고 했다.잘못 보고 한 구체 적 인 위 치 를 나 는 Spring 의 순환 의존 도 를 말씀 드 리 겠 습 니 다. 문장 말미 에 이미 분 석 했 으 니 본 고 는 더 이상 군말 하지 않 겠 다.

    해결 방안
    위 독자 가 제시 한 데모 의 경우 B 에 A 를 주입 할 때 @Lazy 주 해 를 하나 추가 하면 된다
    
    @Component
    public class B implements BService {
    	
     @Autowired
    	@Lazy
    	private A a;
    
    	public void doSomething() {
    	}
    }
    이 주해 의 역할 은 B 에 A 를 주입 할 때 A 에 게 대리 대상 을 만들어 B 에 주입 하 는 것 이다.대리 대상 을 진정 으로 호출 하 는 방법 이 있 을 때 바 텀 은 getBean(a) 을 호출 하여 A 대상 을 만 든 다음 에 방법 을 호출 하 는 것 이다.이 주해 의 처리 시 기 는 org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency 방법 에서 이 주 해 를 처리 하 는 코드 는 org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver#buildLazyResolutionProxy 에 있다.이 코드 들 은 사실 제 가 전에 쓴 글 에서 다 분 석 했 어 요.
    Spring Bean 의 라 이 프 사이클 에 대한 이 해 를 말씀 드 리 겠 습 니 다.
    Spring 잡담|Spring 의 Autowire Candidate Resolver
    그래서 본 고 는 더 이상 상세 한 분석 을 하지 않 는 다.
    문제 2:기본 스 레 드 탱크 는 스 레 드 를 다시 사용 하지 않 습 니 다.
    나 는 이것 이 이 주해 가 가장 깊 은 곳 이 라 고 생각한다.하나 도 없다!우 리 는 그것 이 기본적으로 사용 하 는 스 레 드 탱크 가 무엇 인지 살 펴 보 았 다.앞의 소스 코드 분석 에서 우 리 는 스 레 드 탱크 를 사용 하기 로 결정 하 는 방법 이 org.springframework.aop.interceptor.AsyncExecutionAspectSupport#determineAsyncExecutor 이라는 것 을 알 수 있다.그 소스 코드 는 다음 과 같다.
    
    protected AsyncTaskExecutor determineAsyncExecutor(Method method) {
     AsyncTaskExecutor executor = this.executors.get(method);
     if (executor == null) {
     Executor targetExecutor;
     //    @Async           
     String qualifier = getExecutorQualifier(method);
     if (StringUtils.hasLength(qualifier)) {
     targetExecutor = findQualifiedExecutor(this.beanFactory, qualifier);
     }
     else {
     //         
     targetExecutor = this.defaultExecutor.get();
     }
     if (targetExecutor == null) {
     return null;
     }
     executor = (targetExecutor instanceof AsyncListenableTaskExecutor ?
      (AsyncListenableTaskExecutor) targetExecutor : new TaskExecutorAdapter(targetExecutor));
     this.executors.put(method, executor);
     }
     return executor;
    }
    최종 적 으로 org.springframework.aop.interceptor.AsyncExecutionInterceptor#getDefaultExecutor 이라는 방법 으로 호출 될 겁 니 다.
    
    protected Executor getDefaultExecutor(@Nullable BeanFactory beanFactory) {
     Executor defaultExecutor = super.getDefaultExecutor(beanFactory);
     return (defaultExecutor != null ? defaultExecutor : new SimpleAsyncTaskExecutor());
    }
    기본적으로 사용 하 는 스 레 드 풀 은 SimpleAsyncTaskExecutor 임 을 알 수 있 습 니 다.우 리 는 이러한 소스 코드 를 보지 않 고 위의 문서 주석 만 봅 니 다.다음 과 같 습 니 다.

    주로 세 가 지 를 얘 기 했 어 요.
  • 은 모든 퀘 스 트 에 하나의 스 레 드 를 새로 시작 합 니 다
  • 기본 스 레 드 수 제한 하지 않 음
  • 재 활용 스 레 드
  • 이 세 가지 만 감히 쓰 겠 느 냐?당신 의 임무 가 조금 만 오래 걸 리 면 서버 가 정 해 지지 않 는 다 면 OOM 을 드 리 겠 습 니 다.
    해결 방안
    가장 좋 은 방법 은 사용자 정의 스 레 드 탱크 를 사용 하 는 것 입 니 다.주로 몇 가지 설정 방법 이 있 습 니 다.
    이전의 소스 코드 분석 에서 우 리 는 AsyncConfigurer 을 통 해 사용 하 는 스 레 드 풀 을 설정 할 수 있다 는 것 을 알 수 있다.
    다음 과 같다.
    
    public class DmzAsyncConfigurer implements AsyncConfigurer {
     @Override
     public Executor getAsyncExecutor() {
     //          
     }
    }
    @Async 주석 에 사용 할 스 레 드 탱크 의 이름 을 직접 설정 합 니 다.
    다음 과 같다.
    
    public class A implements AService {
    	
    	private B b;
    
    	@Autowired
    	public void setB(B b) {
    		System.out.println(b);
    		this.b = b;
    	}
    
    	@Async("dmzExecutor")
    	public void doSomething() {
    	}
    }
    
    @EnableAsync
    @Configuration
    @ComponentScan("com.dmz.spring.async")
    @Aspect
    public class Config {
    	@Bean("dmzExecutor")
    	public Executor executor(){
    		//          
    		return executor;
    	}
    }
    총결산
    본 고 는 주로 Spring 에서 비동기 주해 의 사용,원리 와 부 딪 힐 수 있 는 문 제 를 소개 하고 모든 문제 에 대해 서도 방안 을 제시 했다.이 글 을 통 해 @Async 주해 의 사용 을 철저히 파악 하고 그 이 유 를 알 수 있 기 를 바 랍 니 다!
    Spring 의 비동기 주해@Async 의 사용,원리 및 사용 시 발생 할 수 있 는 문제 및 해결 방법 에 관 한 이 글 은 여기까지 소개 합 니 다.Spring 비동기 주해@Async 사용 원리 에 관 한 더 많은 내용 은 이전 글 을 검색 하거나 아래 의 관련 글 을 계속 찾 아 보 세 요.앞으로 도 많은 응원 부 탁 드 리 겠 습 니 다!

    좋은 웹페이지 즐겨찾기