在springboot项目中发送应用内事件,发现业务并没有如预期方式处理,查看日志发现有报错:
发现通过容器上下文工具类getBean的方式获取ApplicationEventPublisher失败;代码如下:
上下文工具类:
@Component
public class AppSpringContextUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
AppSpringContextUtil.applicationContext = applicationContext;
}
/**
* 根据类型获取bean
*/
public static <T> T getBean(Class<T> requiredType) {
return applicationContext.getBean(requiredType);
}
}
发送事件:
/**
* 发送自定义事件
* @param type
* @param buzzId
*/
default void sendCustomEvent(Integer type,String buzzId) {
try {
CustomEventBO eventBO = new CustomEventBO();
eventBO.setType(type);
eventBO.setBuzzId(buzzId);
AppSpringContextUtil.getBean(ApplicationEventPublisher.class).publishEvent(eventBO);
} catch (Exception e) {
log.warn("sendCustomEvent occur error;type={},buzzId={}",type,buzzId,e);
}
}
在业务bean中通过@Autowired的方式,然后调用发送事件能力,就能获取到ApplicationEventPublisher对应的bean:
业务bean:
@Component
public class TestCustomEvent implements ISendCustomEvent {
@Autowired
ApplicationEventPublisher applicationEventPublisher;
public void testSendEvent(int type,String buzzId) {
this.sendCustomEvent(applicationEventPublisher,type,buzzId);
}
}
把发送事件上下文获取ApplicationEventPublisher的方式改为传参:
default void sendCustomEvent(ApplicationEventPublisher eventPublisher,Integer type,String buzzId) {
try {
CustomEventBO eventBO = new CustomEventBO();
eventBO.setType(type);
eventBO.setBuzzId(buzzId);
eventPublisher.publishEvent(eventBO);
} catch (Exception e) {
log.warn("sendCustomEvent occur error;type={},buzzId={}",type,buzzId,e);
}
}
这样就可以正常发送事件了。那么问题来了,为什么对于ApplicationEventPublisher,为什么@Autowired方式可以获取,上下文getBean方式获取不到?
springboot项目启动的时候,会调用AbstractApplicationContext的refresh方法:
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
//...省略...
}
然后会调用做一些初始化准备:
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// Tell the internal bean factory to use the context's class loader etc.
beanFactory.setBeanClassLoader(getClassLoader());
//...省略 ...
// BeanFactory interface not registered as resolvable type in a plain factory.
// MessageSource registered (and found for autowiring) as a bean.
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
//...省略 ...
}
中间几个registerResolvableDependency大概意思是使用相应的定义类注册一个特殊的依赖项类型。这适用于工厂上下文引用,这些引用应该是可自动注册的,但在工厂中没有定义为bean。也即是你可以使用对应的bean,但是beanFactory中并没有对应定义的bean。
public void registerResolvableDependency(Class<?> dependencyType, @Nullable Object autowiredValue) {
Assert.notNull(dependencyType, "Dependency type must not be null");
if (autowiredValue != null) {
if (!(autowiredValue instanceof ObjectFactory || dependencyType.isInstance(autowiredValue))) {
throw new IllegalArgumentException("Value [" + autowiredValue +
"] does not implement specified dependency type [" + dependencyType.getName() + "]");
}
this.resolvableDependencies.put(dependencyType, autowiredValue);
}
}
从registerResolvableDependency方法定义中可以看到,这里实际把特殊类型的依赖添加到resolvableDependencies(本身是个map)本地缓存中。
而对于项目中自己定义的bean会注入到上下文和beanFactory中,这也是区别的地方。
对于@Autowired如何生效和bean如何注入的,我们之前的文章有介绍《@Autowired注解原理分析》,此处不展开详细分析。
@Autowired注入获取是由AutowiredAnnotationBeanPostProcessor驱动,会调用AutowiredFieldElement的inject方法注入,然后会调用到其resolveFieldValue方法:
private Object resolveFieldValue(Field field, Object bean, @Nullable String beanName) {
DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
desc.setContainingClass(bean.getClass());
Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
Assert.state(beanFactory != null, "No BeanFactory available");
TypeConverter typeConverter = beanFactory.getTypeConverter();
Object value;
try {
value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
}
catch (BeansException ex) {
throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
}
//省略放入缓存部分
return value;
}
此处的beanFactory类型是DefaultListableBeanFactory,resolveDependency方法实现如下:
public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
if (Optional.class == descriptor.getDependencyType()) {
return createOptionalDependency(descriptor, requestingBeanName);
}
else if (ObjectFactory.class == descriptor.getDependencyType() ||
ObjectProvider.class == descriptor.getDependencyType()) {
return new DependencyObjectProvider(descriptor, requestingBeanName);
}
else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);
}
else {
Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
descriptor, requestingBeanName);
if (result == null) {
result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
}
return result;
}
}
接着会调用doResolveDependency方法查找依赖:
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
try {
//...省略部分代码...
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
if (matchingBeans.isEmpty()) {
if (isRequired(descriptor)) {
raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
}
return null;
}
//...省略部分代码...
return result;
}
finally {
ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
}
}
这里调用了@Autowired注入依赖和核心方法findAutowireCandidates:
protected Map<String, Object> findAutowireCandidates(
@Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {
String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this, requiredType, true, descriptor.isEager());
Map<String, Object> result = CollectionUtils.newLinkedHashMap(candidateNames.length);
for (Map.Entry<Class<?>, Object> classObjectEntry : this.resolvableDependencies.entrySet()) {
Class<?> autowiringType = classObjectEntry.getKey();
if (autowiringType.isAssignableFrom(requiredType)) {
Object autowiringValue = classObjectEntry.getValue();
autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);
if (requiredType.isInstance(autowiringValue)) {
result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);
break;
}
}
}
for (String candidate : candidateNames) {
if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {
addCandidateEntry(result, candidate, descriptor, requiredType);
}
}
//...省略部分代码...
return result;
}
从代码中可以看出,对于@Autowired方式的属性获取和依赖注入,会同时从BeanDefinitionNames和resolvableDependencies两个地方寻找依赖。从BeanDefinitionNames获取的核心代码是DefaultListableBeanFactory的getBeanNamesForType方法(上下文getBean方式也会用到):
public String[] getBeanNamesForType(@Nullable Class<?> type, boolean includeNonSingletons, boolean allowEagerInit) {
if (!isConfigurationFrozen() || type == null || !allowEagerInit) {
return doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, allowEagerInit);
}
Map<Class<?>, String[]> cache =
(includeNonSingletons ? this.allBeanNamesByType : this.singletonBeanNamesByType);
String[] resolvedBeanNames = cache.get(type);
if (resolvedBeanNames != null) {
return resolvedBeanNames;
}
resolvedBeanNames = doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, true);
if (ClassUtils.isCacheSafe(type, getBeanClassLoader())) {
cache.put(type, resolvedBeanNames);
}
return resolvedBeanNames;
}
我们通过debug看到实现ApplicationContextAware注入的上下文类型是AnnotationConfigServletWebServerApplicationContext:
调用getBean会调用AbstractApplicationContext的getBean方法:
public <T> T getBean(Class<T> requiredType) throws BeansException {
assertBeanFactoryActive();
return getBeanFactory().getBean(requiredType);
}
然后会调用DefaultListableBeanFactory的getBean方法:
public <T> T getBean(Class<T> requiredType, @Nullable Object... args) throws BeansException {
Assert.notNull(requiredType, "Required type must not be null");
Object resolved = resolveBean(ResolvableType.forRawClass(requiredType), args, false);
if (resolved == null) {
throw new NoSuchBeanDefinitionException(requiredType);
}
return (T) resolved;
}
接着调用私有resolveBean方法:
private <T> T resolveBean(ResolvableType requiredType, @Nullable Object[] args, boolean nonUniqueAsNull) {
NamedBeanHolder<T> namedBean = resolveNamedBean(requiredType, args, nonUniqueAsNull);
if (namedBean != null) {
return namedBean.getBeanInstance();
}
//...省略部分代码...
return null;
}
resolveBean接着会调用resolveNamedBean,然后调用getBeanNamesForType方法:
public String[] getBeanNamesForType(ResolvableType type) {
return getBeanNamesForType(type, true, true);
}
最后,也会进入如前边@Autowired会调用的getBeanNamesForType方法:
public String[] getBeanNamesForType(@Nullable Class<?> type, boolean includeNonSingletons, boolean allowEagerInit) {
if (!isConfigurationFrozen() || type == null || !allowEagerInit) {
return doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, allowEagerInit);
}
Map<Class<?>, String[]> cache =
(includeNonSingletons ? this.allBeanNamesByType : this.singletonBeanNamesByType);
String[] resolvedBeanNames = cache.get(type);
//...省略...
return resolvedBeanNames;
}
结合前边所述,对于特殊类型的类,比如ApplicationEventPublisher,只会注册到resolvableDependencies,所以getBean的方式无法获取到,也就解释了异常报错的原因。
从AbstractApplicationContext的prepareBeanFactory方法可以看出目前有以下几个bean不能通过getBean方式获取:
当然如果自己也可以定义一些bean通过registerResolvableDependency注入,从而实现只能@Autowired方式注入,不能getBean方式获取。
当然框架这样设计,一定有其存在的意义,通过翻阅官方文档和其他资料,总结出以下几点如此设计的原因。
总的来说,将特殊组件类似ApplicationEventPublisher设计为不能通过getBean方式获取有助于保持其职责的单一性,减少混淆和不必要的依赖,并更好地控制其生命周期。
本文分享自 PersistentCoder 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!