问题概述

一个Springboot工程,使用Mybatis-plus作为数据层框架

使用@MapperScan注解扫描Mapper接口

@MapperScan("org.net5ijy.cloud.*")
public class DemoApplication {}

在Controller层使用@Autowired注入Service

public interface LoginService {}

@Service
public class LoginServiceImpl implement LoginService {} @Autowired
private LoginService loginService;

在使用Service方法时抛出statement找不到的异常。

初步分析

  • 首先@MapperScan注解配置的basePackages包范围可能过大,导致Mybatis将Service层的接口也当做Mapper做了扫描

  • 在使用@Autowired注入时,Spring没有使用impl实现类Bean,而是使用了Mybatis的代理,导致Controller层使用的是Mybatis的代理Bean

那么为什么?

  • 为什么@MapperScan注解会扫描Service接口,并没有让他扫描呀

  • 为什么@Autowired注入的是Mybatis的代理Bean,而不是impl实现类Bean

  • 为什么@Autowired注入时Spring容器里面有两个Service的Bean,却没有抛出发现了两个Bean的错

MapperScan注解

要回答第一个问题,首先要了解一下MapperScan注解的实现原理。

注解定义

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
@Repeatable(MapperScans.class)
public @interface MapperScan { /**
* Base packages to scan for MyBatis interfaces.
* Note that only interfaces with at least one method will be
* registered; concrete classes will be ignored.
*/
String[] basePackages() default {}; // Other fields
}

这个注解由mybatis-spring提供,配合MapperScannerRegistrar和MapperScannerConfigurer,可以实现编码方式为Mapper接口创建代理,并注册到Spring容器。

Use this annotation to register MyBatis mapper interfaces when using Java Config. It performs when same work as MapperScannerConfigurer via MapperScannerRegistrar.

Either basePackageClasses() or basePackages() (or its alias value()) may be specified to define specific packages to scan. Since 2.0.4, If specific packages are not defined, scanning will occur from the package of the class that declares this annotation.

Import注解简单说明

Indicates one or more component classes to import — typically @Configuration classes.

Provides functionality equivalent to the <import/> element in Spring XML. Allows for importing @Configuration classes, ImportSelector and ImportBeanDefinitionRegistrar implementations, as well as regular component classes (as of 4.2; analogous to AnnotationConfigApplicationContext.register(java.lang.Class<?>...)).

@Bean definitions declared in imported @Configuration classes should be accessed by using @Autowired injection. Either the bean itself can be autowired, or the configuration class instance declaring the bean can be autowired. The latter approach allows for explicit, IDE-friendly navigation between @Configuration class methods.

May be declared at the class level or as a meta-annotation.

If XML or other non-@Configuration bean definition resources need to be imported, use the @ImportResource annotation instead.

文中的第二段中,允许import一个@Configuration类、ImportSelector的实现类、ImportBeanDefinitionRegistrar的实现类。

ImportBeanDefinitionRegistrar接口:

Interface to be implemented by types that register additional bean definitions when processing @Configuration classes. Useful when operating at the bean definition level (as opposed to @Bean method/instance level) is desired or necessary.

Along with @Configuration and ImportSelector, classes of this type may be provided to the @Import annotation (or may also be returned from an ImportSelector).

该接口定义了两个重载的registerBeanDefinitions方法,可以向Spring容器注册BeanDefinition:

public interface ImportBeanDefinitionRegistrar {

    default void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry,
BeanNameGenerator importBeanNameGenerator) { registerBeanDefinitions(importingClassMetadata, registry);
} default void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
}
}

MapperScannerRegistrar类

MapperScannerRegistrar类实现了ImportBeanDefinitionRegistrar接口,在registerBeanDefinitions方法中,他将MapperScannerConfigurer类注册到了Spring容器中,而MapperScannerConfigurer类实现了BeanDefinitionRegistryPostProcessor接口,这个接口的postProcessBeanDefinitionRegistry方法会在bean definition registry初始化完成后被调用,此时所有的bean definition已经加载完毕,但是bean还没有初始化。

而MapperScannerConfigurer就是在这个postProcessBeanDefinitionRegistry方法中扫描所有的Mapper接口并且注册FactoryBean bean definition的。

@Import的实现原理、MapperScannerRegistrar和MapperScannerConfigurer如何被调用不在本文的讨论范围内,我们只要知道以下两点就可以继续分析下去:

  • 首先MapperScannerRegistrar类会被import进来,他的registerBeanDefinitions会被调用,这时他会注册一个MapperScannerConfigurer到bean definition registry中

  • 其次MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor接口,他的postProcessBeanDefinitionRegistry方法会在bean definition registry初始化完成后被调用,扫描所有的Mapper接口

所以就可以直接看MapperScannerConfigurer类的postProcessBeanDefinitionRegistry方法实现,看一下他是如何扫描Mapper接口的。

MapperScannerConfigurer类

概述

BeanDefinitionRegistryPostProcessor that searches recursively starting from a base package for interfaces and registers them as MapperFactoryBean. Note that only interfaces with at least one method will be registered; concrete classes will be ignored.

The basePackage property can contain more than one package name, separated by either commas or semicolons.

This class supports filtering the mappers created by either specifying a marker interface or an annotation. The annotationClass property specifies an annotation to search for. The markerInterface property specifies a parent interface to search for. If both properties are specified, mappers are added for interfaces that match either criteria. By default, these two properties are null, so all interfaces in the given basePackage are added as mappers.

在文档的第一段,可以看到这个类会在basePackages下递归查找Mapper接口,然后将他们注册为MapperFactoryBean(他实现了FactoryBean接口),他只会扫描接口(至少要有一个方法)而不会扫描类。

Mapper扫描

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
if (this.processPropertyPlaceHolders) {
processPropertyPlaceHolders();
} ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
scanner.setAddToConfig(this.addToConfig);
scanner.setAnnotationClass(this.annotationClass);
scanner.setMarkerInterface(this.markerInterface);
scanner.setSqlSessionFactory(this.sqlSessionFactory);
scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
scanner.setResourceLoader(this.applicationContext);
scanner.setBeanNameGenerator(this.nameGenerator);
scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
if (StringUtils.hasText(lazyInitialization)) {
scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));
} // 设置过滤器
scanner.registerFilters(); // 扫描
scanner.scan(
StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}

ClassPathMapperScanner.scan方法:

public int scan(String... basePackages) {
int beanCountAtScanStart = this.registry.getBeanDefinitionCount(); // 扫描
doScan(basePackages); if (this.includeAnnotationConfig) {
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
} return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}

ClassPathMapperScanner.doScan方法:

public Set<BeanDefinitionHolder> doScan(String... basePackages) {
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages); if (beanDefinitions.isEmpty()) {
// ...
} else {
// 这里也比较重要,内部会将MapperFactoryBean注册到bean definition registry中
// 后面详细说明
processBeanDefinitions(beanDefinitions);
} return beanDefinitions;
}

super.doScan方法:

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
// 配置的basePackages只有一个元素,所以for循环只会循环一次
for (String basePackage : basePackages) {
// 在basePackage下面扫描所有的接口
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
ScopeMetadata scopeMetadata =
this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
String beanName =
this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils
.processCommonDefinitionAnnotations(
(AnnotatedBeanDefinition) candidate);
}
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder =
new BeanDefinitionHolder(candidate, beanName);
definitionHolder = AnnotationConfigUtils
.applyScopedProxyMode(
scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
// 注册bean definition
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
} /* Set<BeanDefinition> candidates = findCandidateComponents(basePackage); 这行代码比较重要,就是在basePackage下面扫描接口,创建BeanDefinition registerBeanDefinition(definitionHolder, this.registry); 这行代码将BeanDefinition注册到bean definition registry */

findCandidateComponents方法:

public Set<BeanDefinition> findCandidateComponents(String basePackage) {
if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
} else {
// 走的是这个分支
return scanCandidateComponents(basePackage);
}
}

scanCandidateComponents方法:

/*

参数basePackage就是配置的mapper scan包

*/
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<>();
try {
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + '/' + this.resourcePattern;
// 这里会把basePackage包和子包下面的所有class文件都找到
Resource[] resources =
getResourcePatternResolver().getResources(packageSearchPath);
// 遍历所有的class文件
for (Resource resource : resources) {
if (resource.isReadable()) {
try {
MetadataReader metadataReader =
getMetadataReaderFactory().getMetadataReader(resource);
// 此处几乎不会过滤掉任何class
// 所以只要是在basePackage包和其子包下面的class都会被扫描
// 然后封装BeanDefinition一并返回
if (isCandidateComponent(metadataReader)) {
ScannedGenericBeanDefinition sbd =
new ScannedGenericBeanDefinition(metadataReader);
sbd.setSource(resource);
if (isCandidateComponent(sbd)) {
// 加到集合等待返回
candidates.add(sbd);
}
}
} catch (Throwable ex) {
throw new BeanDefinitionStoreException("", ex);
}
}
}
} catch (IOException ex) {
throw new BeanDefinitionStoreException("", ex);
}
return candidates;
} /* 内部细节就不再详细记录了,确实很复杂 */

super.doScan方法执行完毕之后,又回到ClassPathMapperScanner.doScan方法:

public Set<BeanDefinitionHolder> doScan(String... basePackages) {
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages); if (beanDefinitions.isEmpty()) {
// ...
} else {
// 回到这里
processBeanDefinitions(beanDefinitions);
} return beanDefinitions;
}

processBeanDefinitions方法:

private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
GenericBeanDefinition definition;
for (BeanDefinitionHolder holder : beanDefinitions) {
definition = (GenericBeanDefinition) holder.getBeanDefinition();
String beanClassName = definition.getBeanClassName(); // the mapper interface is the original class of the bean
// but, the actual class of the bean is MapperFactoryBean
definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // 重新设置beanClass
// this.mapperFactoryBeanClass是org.mybatis.spring.mapper.MapperFactoryBean
// 他是MapperFactoryBean的FactoryBean的实现
// Spring在创建了FactoryBean实现类的实例之后,会调用他的T getObject()方法获取真实的Bean
// 然后将这个Bean放入容器
// MapperFactoryBean内部会为Mapper接口创建Proxy
// 多数的接口扫描的框架都是利用Spring FactoryBean + Proxy方式实现的
// MapperFactoryBean内部创建代理的逻辑不在本文讨论范围,暂时省略
definition.setBeanClass(this.mapperFactoryBeanClass); definition.getPropertyValues().add("addToConfig", this.addToConfig); // 以下设置sqlSessionFactory和sqlSessionTemplate的代码在多数情况下都不会执行,因为没有配置 boolean explicitFactoryUsed = false;
if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
definition.getPropertyValues().add("sqlSessionFactory",
new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionFactory != null) {
definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
explicitFactoryUsed = true;
} if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
if (explicitFactoryUsed) {
LOGGER.warn("");
}
definition.getPropertyValues().add("sqlSessionTemplate",
new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionTemplate != null) {
if (explicitFactoryUsed) {
LOGGER.warn("");
}
definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
explicitFactoryUsed = true;
} if (!explicitFactoryUsed) {
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}
definition.setLazyInit(lazyInitialization);
}
}

到此为止,Mapper扫描逻辑就完成了。

解决了第一个疑问

所以,我们就可以回答第一个疑问了:为什么MapperScan注解会扫描Service接口,并没有让他扫描呀?

因为我们在@MapperScan注解配置的basePackages包含了Service接口所在包,Mybatis在doScan时把这些接口也当成了Mapper接口,做了扫描,并且将BeanDefinition注册到了Spring容器。

@Autowired注解原理

要回答第二个和第三个问题,需要连接@Autowired注解的原理。

@Autowired注解是使用AutowiredAnnotationBeanPostProcessor类实现的。

AutowiredAnnotationBeanPostProcessor类

他是InstantiationAwareBeanPostProcessor接口的实现类。

InstantiationAwareBeanPostProcessor接口:

/*

Subinterface of BeanPostProcessor that adds a before-instantiation callback, and a callback after instantiation but before explicit properties are set or autowiring occurs.

Typically used to suppress default instantiation for specific target beans, for example to create proxies with special TargetSources (pooling targets, lazily initializing targets, etc), or to implement additional injection strategies such as field injection.

*/
public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor { default Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName)
throws BeansException {
return null;
} default boolean postProcessAfterInstantiation(Object bean, String beanName)
throws BeansException {
return true;
} default PropertyValues postProcessProperties(
PropertyValues pvs, Object bean, String beanName) throws BeansException { return null;
} @Deprecated
default PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName)
throws BeansException { return pvs;
}
}

通常用于修改特定Bean的默认初始化行为,例如创建代理、依赖注入等。

其中postProcessProperties(PropertyValues pvs, Object bean, String beanName)方法被用来实现@Autowired依赖注入。

在哪里调用postProcessProperties方法

以下为简单的容器初始化和创建Bean流程:

  • 启动类run方法

  • SpringApplication.refresh方法

  • AbstractApplicationContext.refresh方法

  • AbstractApplicationContext.finishBeanFactoryInitialization方法

  • DefaultListableBeanFactory.preInstantiateSingletons方法

  • AbstractBeanFactory.doGetBean方法

  • AbstractAutowireCapableBeanFactory.createBean(String, RootBeanDefinition, Object[])方法

  • AbstractAutowireCapableBeanFactory.doCreateBean方法

  • AbstractAutowireCapableBeanFactory.populateBean方法

在AbstractAutowireCapableBeanFactory.populateBean方法中有依赖注入的代码实现:

protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {

    // ...

    PropertyDescriptor[] filteredPds = null;
if (hasInstAwareBpps) {
if (pvs == null) {
pvs = mbd.getPropertyValues();
}
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp =
(InstantiationAwareBeanPostProcessor) bp; // 这里调用postProcessProperties方法做依赖注入
// 可以使用debug方式跟踪进去
PropertyValues pvsToUse =
ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
if (filteredPds == null) {
filteredPds =
filterPropertyDescriptorsForDependencyCheck(
bw, mbd.allowCaching);
}
pvsToUse = ibp.postProcessPropertyValues(
pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
return;
}
}
pvs = pvsToUse;
}
}
} // ...
}

postProcessProperties方法实现

postProcessProperties方法

public PropertyValues postProcessProperties(
PropertyValues pvs, Object bean, String beanName) { // 获取依赖注入元信息,包括目标Bean的类型,需要注入的Field信息
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, "", ex);
}
return pvs;
}

InjectionMetadata.inject方法

public void inject(Object target, String beanName, PropertyValues pvs) throws Throwable {

    Collection<InjectedElement> checkedElements = this.checkedElements;
Collection<InjectedElement> elementsToIterate =
(checkedElements != null ? checkedElements : this.injectedElements); if (!elementsToIterate.isEmpty()) {
for (InjectedElement element : elementsToIterate) {
// 遍历,逐一注入
element.inject(target, beanName, pvs);
}
}
}

element.inject方法:

protected void inject(Object bean, String beanName, PropertyValues pvs) {
Field field = (Field) this.member;
Object value;
// 如果是第一次进来,都是false
if (this.cached) {
value = resolvedCachedArgument(beanName, this.cachedFieldValue);
} else {
DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
desc.setContainingClass(bean.getClass());
Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
TypeConverter typeConverter = beanFactory.getTypeConverter();
try {
// 从容器里面获取依赖Bean
// 后续详细说明
value = beanFactory
.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
} catch (BeansException ex) {
throw new UnsatisfiedDependencyException(...);
}
synchronized (this) {
if (!this.cached) {
Object cachedFieldValue = null;
if (value != null || this.required) {
cachedFieldValue = desc;
registerDependentBeans(beanName, autowiredBeanNames);
if (autowiredBeanNames.size() == 1) {
String autowiredBeanName = autowiredBeanNames.iterator().next();
if (beanFactory.containsBean(autowiredBeanName) &&
beanFactory.isTypeMatch(
autowiredBeanName, field.getType())) {
cachedFieldValue = new ShortcutDependencyDescriptor(
desc, autowiredBeanName, field.getType());
}
}
}
this.cachedFieldValue = cachedFieldValue;
this.cached = true;
}
}
}
if (value != null) {
// 反射注入
ReflectionUtils.makeAccessible(field);
field.set(bean, value);
}
}

beanFactory.resolveDependency方法

public Object resolveDependency(
DependencyDescriptor descriptor, String requestingBeanName,
Set<String> autowiredBeanNames, TypeConverter typeConverter) { 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) {
// 去获取依赖Bean
result = doResolveDependency(
descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
}
return result;
}
}

doResolveDependency方法:

public Object doResolveDependency(
DependencyDescriptor descriptor, String beanName,
Set<String> autowiredBeanNames, TypeConverter typeConverter) { 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());
try {
return converter
.convertIfNecessary(value, type, descriptor.getTypeDescriptor());
} catch (UnsupportedOperationException ex) {
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;
} // 从这里开始,是关键的逻辑 // 从容器里面查找与注入类型匹配的Bean,从我们的编码方式可以确定有两个:
// 一个是loginService -> Mybatis代理Bean
// 一个是loginServiceImpl -> 我们编写的实现类Bean
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) {
// 如果找到了多个Bean
// 就尝试获取一个最匹配的Bean
autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
if (autowiredBeanName == null) {
if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
// 如果没有找到Bean
// 则抛出一个expected single matching bean but found ${N}的错
return descriptor.resolveNotUnique(
descriptor.getResolvableType(), matchingBeans);
} else {
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);
} // 如果此时Bean还没有初始化
// 需要使用Spring容器初始化一下
// 就是调用的beanFactory.getBean(beanName)方法,这样就回到了Spring的创建Bean的流程
// 不在本文讨论范围,略过
if (instanceCandidate instanceof Class) {
instanceCandidate =
descriptor.resolveCandidate(autowiredBeanName, type, this);
}
Object result = instanceCandidate;
if (result instanceof NullBean) {
if (isRequired(descriptor)) {
raiseNoMatchingBeanFound(
type, descriptor.getResolvableType(), descriptor);
}
result = null;
}
if (!ClassUtils.isAssignableValue(type, result)) {
throw new BeanNotOfRequiredTypeException(
autowiredBeanName, type, instanceCandidate.getClass());
}
// 返回
return result;
} finally {
ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
}
} protected String determineAutowireCandidate(
Map<String, Object> candidates, DependencyDescriptor descriptor) { Class<?> requiredType = descriptor.getDependencyType(); // 如果有Primay的Bean,就直接使用他
String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);
if (primaryCandidate != null) {
return primaryCandidate;
}
// 优先级判断
String priorityCandidate =
determineHighestPriorityCandidate(candidates, requiredType);
if (priorityCandidate != null) {
return priorityCandidate;
} for (Map.Entry<String, Object> entry : candidates.entrySet()) {
String candidateName = entry.getKey();
Object beanInstance = entry.getValue();
// ||左侧为false
// ||右侧尝试匹配field名和BeanName,如果匹配就使用这个Bean
// 在我们的场景下,返回的就是Mybatis的代理Bean了
if ((beanInstance != null &&
this.resolvableDependencies.containsValue(beanInstance)) ||
matchesBeanName(candidateName, descriptor.getDependencyName())) {
return candidateName;
}
}
return null;
}

问题解答

为什么@Autowired注入的是Mybatis的代理,而不是impl实现类Bean?为什么@Autowired注入时Spring容器里面有两个Service的Bean,却没有抛出发现了两个Bean的错?

因为在发现容器里面存在某个接口的多个Bean时,就尝试使用Primary、优先级、Bean名称去做精确的匹配,如果匹配到了就是这个Bean来注入,如果没有找到才抛出expected single matching bean but found ${N}的错。

在本例中,Spring容器中存在两个LoginService接口的Bean:一个是loginService -> Mybatis代理Bean,另一个是loginServiceImpl -> 我们编写的实现类Bean。而待注入的field名称与Mybatis代理Bean的名称一致,所以就使用了这个代理Bean来注入。

如何解决这个问题

使用@Resource("loginServiceImpl")方式注入

@Resource("loginServiceImpl")
private LoginService loginService;

使用@Autowired + @Qualifier方式注入

@Autowired
@Qualifier("loginServiceImpl")
private LoginService loginService;

或者

@Autowired
private LoginService loginServiceImpl;

把@MapperScan注解的扫描范围改到精确的Mapper包

这种方式其实是最好的。

@MapperScan("org.net5ijy.cloud.mapper")
public class DemoApplication {}

Autowired注入Service变成了biaomidou的Mapper代理的更多相关文章

  1. Springboot在工具类(Util)中使用@Autowired注入Service

    1. 使用@Component注解标记工具类MailUtil: 2. 使用@Autowired注入我们需要的bean: 3. 在工具类中编写init()函数,并使用@PostConstruct注解标记 ...

  2. Spring使用Quartz定时调度Job无法Autowired注入Service的解决方案

    1)自定义JobFactory,通过spring的AutowireCapableBeanFactory进行注入,例如: public class MyJobFactory extends  org.s ...

  3. spring整合Jersey 无法注入service的问题

    现象: action中的@autowired注入service或dao失败,报空指针异常 原因: 造成该问题的原因是你并没有做好spring和jersey的整合工作,检查你的web.xml文件,jer ...

  4. Spring Task中的定时任务无法注入service的解决办法

    1.问题 因一个项目(使用的是Spring+SpringMVC+hibernate框架)需要在spring task定时任务中调用数据库操作,在使用 @Autowired注入service时后台报错, ...

  5. Spring Boot Service注入为null mapper注入为null @Component注解下@Value获取不到值 WebsocketServer类里无法注入service

    最近搞了一下websocket前台(这个网上有很多的教程这里就不班门弄斧啦) 以及前后台的交互 和后台的bug(搞了两天) 也是状态频发 bug不断 下面说一说问题. Websocket主类里面无法注 ...

  6. IntelliJ IDEA中Mapper接口通过@Autowired注入报错的正确解决方式

    转载请注明来源:四个空格 » IntelliJ IDEA中Mapper接口通过@Autowired注入报错的正确解决方式: 环境 ideaIU-2018.3.4.win: 错误提示: Could no ...

  7. 关于工具类静态方法调用@Autowired注入的service类问题

    @Component //此处注解不能省却(0) 1 public class NtClient { 2 /** 3 * 日志 4 */ 5 private static String clazzNa ...

  8. 欲哭无泪的@Autowired注入对象为NULL

    欲哭无泪啊...一下午的时间就这么被浪费了...一个基于spring mvc和spring data jpa的小项目,当我写完一个controller的测试用例后,一运行却报空指针,跟了下是一个dao ...

  9. 如何在Java Filter 中注入 Service

    在项目中遇到一个问题,在 Filter中注入 Serivce失败,注入的service始终为null.如下所示: public class WeiXinFilter implements Filter ...

  10. springboot 静态方法注入service

    p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; line-height: 16.0px; font: 14.0px Arial; color: #3f3f3f; bac ...

随机推荐

  1. 手动安装pinia、给项目添加pinia实例

    用你喜欢的js包管理器安装pinia: yarn add pinia # 或者使用 npm npm install pinia 创建一个 pinia 实例 (根 store) 并将其传递给应用: 编辑 ...

  2. 数字孪生和GIS融合后能够为城市交通带来哪些便利?

    数字孪生和GIS的融合对于城市交通领域带来了诸多便利,从智能交通管理到出行体验的提升,为城市交通带来了全新的发展机遇. 首先,数字孪生技术与GIS的结合可以实现智能交通管理.通过GIS建立城市交通网络 ...

  3. 神经网络优化篇:详解其他正则化方法(Other regularization methods)

    其他正则化方法 除了\(L2\)正则化和随机失活(dropout)正则化,还有几种方法可以减少神经网络中的过拟合: 一.数据扩增 假设正在拟合猫咪图片分类器,如果想通过扩增训练数据来解决过拟合,但扩增 ...

  4. rcs群发软件系统功能设计与应用,rcs群发软件系统,rcs群发软件

    随着科技的不断发展,人们对于通讯方式的需求也在不断变化,传统的短信.电话已经无法满足人们对于高效.便捷.实时的通讯需求,正是在这样的背景下,富通讯解决方案(Rich Communication Sui ...

  5. 部署堡垒机5——安装Core

    部署jumpserver服务核心组件Core 一.前期准备 一个后台程序,基本上都是需要依赖于数据库才能运行,后台程序在启动的时候,代码就回去连接数据库,保证数据库,正确启动,且可以正确连接,否则后台 ...

  6. MySQL运维实战(1.3)安装部署:源码编译安装

    作者:俊达 引言 在大多数情况下,我们不需要自己编译MySQL源码,因为编译的MySQL和二进制包的内容基本一致.然而,有些特殊情况可能需要我们采用源码编译的方式安装MySQL: 安装非标准版本的My ...

  7. win11 右击还原 win10的

    以管理员身份 打开 powershell, 然后输入如下代码 .\reg.exe add "HKCU\Software\Classes\CLSID\{86ca1aa0-34aa-4e8b-a ...

  8. 微服务架构下,DLI的部署和运维有何奥秘?

    摘要:探讨DLI两个问题:如何在生产环境中部署与运维实现快速迭代上线,如何实现监控告警来提升整体运维能力. 华为云数据湖探索DLI是支持多模引擎的Serverless大数据计算服务,其很好的实现了Se ...

  9. API生态的发展与机遇:从5000组数据看中国API生态与开发者现状

    摘要:华为云联合多家单位发布了<中国API生态与开发者现状调研报告(2020年)>,旨在通过API生态.API开发者.使用者.API全生命周期管理等多视角展现我国API发展的现状与机遇,力 ...

  10. GaussDB(DWS)性能调优:indexscan导致的性能问题识别与优化

    摘要:通常跑批加工场景下,都是大数量做关联操作,通常不建议使用索引.有些时候因为计划误判导致使用索引的可能会导致严重的性能问题.本文从一个典型的索引导致性能的场景重发,剖析此类问题的特征,定位方法和解 ...