前言
最近在使用@Async
注解进行异步操作的时候时,想过一下@Async
注解的原理,这种需要@Enable*
注解开启作用的注解,原理应该是大同小异。之前已经分析过了@Scheluded
注解了,本以为这次看@Async
注解会很轻松,但是还遇到了很多问题。
我们知道在调用带有@Async
的方法的时候,会将这个方法作为一个任务放入到线程池中执行。
如果我们想要自己配置线程池的属性,只需要实现AsyncConfigurer
接口即可。
公司的底层框架已经把线程池相关配置配置在了一个Configuration
类中了,显然,这个类会实现上述的接口。
@Configuration
public class ExecutorPoolConfig implements AsyncConfigurer {
@Autowired
Executor commonExecutor;
/**
* 配置@Async线程池,使用通用线程池配置
*/
@Override
public Executor getAsyncExecutor() {
return commonExecutor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new AsyncUncaughtExceptionHandler() {
@Override
public void handleUncaughtException(Throwable ex, Method method, Object... params) {
LOGGER.error("异步线程执行任务{}异常: {}", method.getName(), ExceptionUtil.getStackTrace(ex));
}
};
}
@Bean
public Executor commonExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(poolSize);
executor.setMaxPoolSize(poolMaxSize);
executor.setQueueCapacity(queueCapacity);
// 开启线程任务的关闭等待
executor.setWaitForTasksToCompleteOnShutdown(waitForTasksToCompleteOnShutdown);
executor.setAwaitTerminationSeconds(awaitTerminationSeconds);
// 默认使用beanName
executor.setThreadNamePrefix("IYCCommonPool-");
// rejection-policy:当pool已经达到max size的时候,由调用者所在的线程来执行
executor.setRejectedExecutionHandler(new MyRejectedCallerRunsPolicy());
executor.initialize();
return executor;
}
}
当我第一次看到
@Autowired
Executor commonExecutor;
就在想,如果我在自己的服务里面同样构造了一个Executor
类型的Bean,到底这个@Autowired
会注入哪个Bean呢?经过测试,是会注入到我们自己构造的Bean的。这也是我们期待的结果,我们在自己的服务里构造新的线程池的时候,肯定是希望使用的是我们的线程池,而不是默认的线程池。我们希望我们的配置可以覆盖默认的配置,然而也确实做到了。但是为什么会覆盖呢?
@Autowired
注入原理
我们来看获取Bean实例的方法
AbstractAutowireCapableBeanFactory#doCreateBean
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
throws BeanCreationException {
// Instantiate the bean.
//初始化Bean。使用相应的构造方法来初始化Bean对象
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
//对Bean的一些属性进行注入,这行代码是今天分析的主要部分
populateBean(beanName, mbd, instanceWrapper);
if (exposedObject != null) {
//这行代码之前已经分析过了
//执行Bean指定的初始化方法,这块也会对Bean执行符合条件的BeanPostProcessor,著名的Spring AOP思想就是在这块代码中实现的
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
}
}
populateBean
protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {
PropertyValues pvs = mbd.getPropertyValues();
if (hasInstAwareBpps || needsDepCheck) {
PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
if (hasInstAwareBpps) {
//
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
//在这里处理属性注入
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvs == null) {
return;
}
}
}
}
applyPropertyValues(beanName, mbd, bw, pvs);
}
这个方法的名字叫填充Bean,处理属性注入也是在这个方法中完成的,这个方法中很长,我们只关注我们要分析的部分,ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
@Override
public PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
try {
metadata.inject(bean, beanName, pvs);
}
catch (BeanCreationException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
}
return pvs;
}
选择合适的注入方式来完成属性注入的工作,Spring的流程实在是太多了,不一一分析了,有兴趣的可以Debug跟着跑一遍,我这里就不纠结这些细节了。
最后沿着执行链会执行到DefaultListableBeanFactory#doResolveDependency
public Object doResolveDependency(DependencyDescriptor descriptor, String beanName,
Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {
InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
try {
Object shortcut = descriptor.resolveShortcut(this);
if (shortcut != null) {
return shortcut;
}
Class<?> type = descriptor.getDependencyType();
Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
if (value != null) {
if (value instanceof String) {
String strVal = resolveEmbeddedValue((String) value);
BeanDefinition bd = (beanName != null && containsBean(beanName) ? getMergedBeanDefinition(beanName) : null);
value = evaluateBeanDefinitionString(strVal, bd);
}
TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
return (descriptor.getField() != null ?
converter.convertIfNecessary(value, type, descriptor.getField()) :
converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
}
Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
if (multipleBeans != null) {
return multipleBeans;
}
//寻找候选者,如果该Filed上不仅有@Autowired注解,还有@Qualifier注解,那么候选者最多只会有一个了
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
if (matchingBeans.isEmpty()) {
if (isRequired(descriptor)) {
raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
}
return null;
}
String autowiredBeanName;
Object instanceCandidate;
//如果有多个候选者,那么需要选择最合适的候选者
if (matchingBeans.size() > 1) {
//如何选择合适的候选者
autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
if (autowiredBeanName == null) {
if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
return descriptor.resolveNotUnique(type, matchingBeans);
}
else {
// In case of an optional Collection/Map, silently ignore a non-unique case:
// possibly it was meant to be an empty collection of multiple regular beans
// (before 4.3 in particular when we didn't even look for collection beans).
return null;
}
}
instanceCandidate = matchingBeans.get(autowiredBeanName);
}
else {
// We have exactly one match.
Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
autowiredBeanName = entry.getKey();
instanceCandidate = entry.getValue();
}
if (autowiredBeanNames != null) {
autowiredBeanNames.add(autowiredBeanName);
}
return (instanceCandidate instanceof Class ?
descriptor.resolveCandidate(autowiredBeanName, type, this) : instanceCandidate);
}
finally {
ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
}
}
通过Field的类型和Field上的注解信息来获取合适的候选者
1.寻找候选者,通过findAutowireCandidates(beanName, type, descriptor);
寻找Spring环境中类型与type相同的Bean,如果Field上还有@Qualifier
注解,那么最多只可能有一个候选者(Spring中同名Bean默认会被覆盖,所以最多只能有一个候选者)。如果没有该注解,那么这里获取到的matchingBeans
就可能是多个了。
protected Map<String, Object> findAutowireCandidates(
String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {
String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this, requiredType, true, descriptor.isEager());
Map<String, Object> result = new LinkedHashMap<String, Object>(candidateNames.length);
for (Class<?> autowiringType : this.resolvableDependencies.keySet()) {
if (autowiringType.isAssignableFrom(requiredType)) {
Object autowiringValue = this.resolvableDependencies.get(autowiringType);
autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);
if (requiredType.isInstance(autowiringValue)) {
result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);
break;
}
}
}
for (String candidate : candidateNames) {
//这里先会把不是把不是和当前Bean直接相关的候选者加入到CandidateEntry
if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {
addCandidateEntry(result, candidate, descriptor, requiredType);
}
}
if (result.isEmpty() && !indicatesMultipleBeans(requiredType)) {
// Consider fallback matches if the first pass failed to find anything...
DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch();
for (String candidate : candidateNames) {
if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, fallbackDescriptor)) {
addCandidateEntry(result, candidate, descriptor, requiredType);
}
}
//当没有和当前Bean不直接相关的候选者时
if (result.isEmpty()) {
// Consider self references as a final pass...
// but in the case of a dependency collection, not the very same bean itself.
//就需要选择和当前Bean在同一个FacoryBean的候选者中加入到CandidateEntry
for (String candidate : candidateNames) {
if (isSelfReference(beanName, candidate) &&
(!(descriptor instanceof MultiElementDescriptor) || !beanName.equals(candidate)) &&
//当有@Qualifier注解存在时,这个方法会去判断是否是和@Qualifier注解里的值匹配的候选者,是的话,才加入
isAutowireCandidate(candidate, fallbackDescriptor)) {
addCandidateEntry(result, candidate, descriptor, requiredType);
}
}
}
}
return result;
}
private boolean isSelfReference(String beanName, String candidateName) {
return (beanName != null && candidateName != null &&
//如果候选者和当前Bean的名字相同 或者
(beanName.equals(candidateName) ||
//候选者在当前Map of bean definition objects中
(containsBeanDefinition(candidateName) &&
//并候选者的FactoryBean的名称和当前BeanDe名称相同时,返回true
beanName.equals(getMergedLocalBeanDefinition(candidateName).getFactoryBeanName()))));
}
那么在什么时候候选者的FactoryBean会是当前Bean呢?在使用注解代替传统的XML配置来配置Bean的时候,会将在配置类中的Bean的FactoryBean设置为他所在的配置类。
在Spring中,当在处理Bean的属性的注入的时候,当有候选者是配置在当前Bean这个类里面的时候,他的优先级是最低的。也就是说最开始的的commonExecutor
这个Executor
类型的Bean的优先级是最低的,只要有任意一个在其他的类里面配置的Executor
类型的Bean,都会覆盖掉这个在ExecutorPoolConfig
中配置的Bean。到这里已经解决了文初的疑问,但是还存在一种情况,那就是如果在其他的类中有多个该类型的Bean,那我们会选择哪个呢?
2.如果我们获取到了多个matchingBeans
,那么我们需要选出优先级最高的那个类,通过determineAutowireCandidate(matchingBeans, descriptor);
来选取最合适的Bean。
protected String determineAutowireCandidate(Map<String, Object> candidates, DependencyDescriptor descriptor) {
Class<?> requiredType = descriptor.getDependencyType();
//先看有没有候选者带有@Primary注解
String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);
if (primaryCandidate != null) {
return primaryCandidate;
}
//再看有没有优先级之分
//当beanInstance是Class类型时,查看是否有```javax.annotation.Priority```类型的注解
//否则看是否有javax.annotation.Priority类型注解,如果没有,那么再看是否有org.springframework.core.annotation.Order类型的注解.
//那什么时候beanInstance会是
String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType);
if (priorityCandidate != null) {
return priorityCandidate;
}
// Fallback
//如果都没有,那么就看有没有候选者的Name和属性名相同
for (Map.Entry<String, Object> entry : candidates.entrySet()) {
String candidateName = entry.getKey();
Object beanInstance = entry.getValue();
if ((beanInstance != null && this.resolvableDependencies.containsValue(beanInstance)) ||
matchesBeanName(candidateName, descriptor.getDependencyName())) {
return candidateName;
}
}
//如果也没有的话,那么就返回null
return null;
}
一旦该方法返回null的时候,就会导致整个方法返回null或者是直接抛出异常。
if (matchingBeans.size() > 1) {
autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
//一旦返回为null的时候,就会导致方法直接返回了
if (autowiredBeanName == null) {
//
if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
//抛出异常
return descriptor.resolveNotUnique(type, matchingBeans);
}
else {
// In case of an optional Collection/Map, silently ignore a non-unique case:
// possibly it was meant to be an empty collection of multiple regular beans
// (before 4.3 in particular when we didn't even look for collection beans).
return null;
}
}
}
所以当其他类中有多个Executor
类型的Bean时:
1.看这些Bean有没有带有@Primary
注解的,有的话就取这个。
2.再看有没有带有Order
注解或者Priority
,有的话就遍历所有的候选者,返回优先级最高的候选者。
3.最后在看下是否有候选者的BeanName和Field名相同,如果有,则返回匹配的候选者。
4.如果都没找到合适的候选者的话,返回null,抛出异常。
总结
当Spring环境中拥有多个相同类型的Bean时,在使用@AutoWired
注解去引用的时候,是需要注意的。
- 最好是使用
@Qualifier
去指定想要使用的Bean - 退而求其次的方法,就是在某个Bean上加上
@Primary
注解,指定这个Bean是优先级最高的Bean,在使用@AutoWired
注解去引用这一类型的Bean的时候,优先取这个,也可以在Bean上加上@Order
注解或者@Priority
表名Bean的优先级,实在不行也要有一个Bean的名字与使用@AutoWired
注解的Field或Method的Name相同,否则就要报错了。 - 当Spring环境中有多个相同类型的Bean的时候,如果使用
@AutoWired
注解的Field或Method恰好和其中的一个Bean是同一个FactoryBean
,那么这个Bean的匹配优先级是最低的,这一特性可以用来覆盖某些默认的配置。