问题概述

一个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. 数字孪生结合GIS会给矿业带来怎样的改变

    数字孪生技术和GIS的结合为矿业带来了革命性的改变.矿业作为重要的经济支柱,其发展与资源的开采.生产过程的管理密切相关.通过数字孪生和GIS的融合,矿业行业可以实现更高效.可持续的运营和管理,带来许多 ...

  2. Python——第五章:os模块、sys模块

    os 模块 os 模块提供了很多允许你的程序与操作系统直接交互的功能 import os 得到当前工作目录,即当前Python脚本工作的目录路径: os.getcwd() 返回指定目录下的所有文件和目 ...

  3. 善用 vscode 的批量和模板技巧来提效

    vs code 其实有很多实用的技巧可以在日常工作中带来很大的提效,但可能是开发中没有相应的痛点场景,因此有些技巧接触的人不多 本篇就来介绍下多光标的批量操作和模板代码两种技巧在日常工作中的提效 涉及 ...

  4. 前世今生:Kubernete 是如何火起来的?

    本课时,我们就开始正式进入 Kubernetes 的学习,或许你已经听过或简单了解过 Kubernetes,它是一款由 Google 开源的容器编排管理工具,而我们想要深入地掌握 Kubernetes ...

  5. OPPO关停自研芯片公司哲库,这对行业将产生什么影响?

    OPPO什么时候关停自研芯片公司哲库? 公元2023年5月12日,OPPO关停了自研芯片公司哲库.这也是汶川大地震的日子,而OPPO创始人是四川人,真是冥冥之中自有天意.OPPO公司在一份声明中表示, ...

  6. 2021-01-16:我截获了登录token的话,是不是就获得了登录状态,这样就不安全了。如何保证安全?

    福哥答案2021-01-06: 知乎答案: 首先,Token 一般放在 Header 或者 Cookies 中,Http 是明文传输,Https 是密文传输.可以一定程度防止Token 截获. 第二, ...

  7. Java 将PDF转为OFD

    OFD格式一种国产文件格式,在一些对文档格式有着严格的企业中用得比较多.下面,通过Java程序展示如何将PDF文件转为OFD格式. 代码思路:加载PDF源文档,调用Spire.Pdf.jar提供的sa ...

  8. 懂分析、会预测,你见过这样的华为云DAS吗?

    摘要:数字化时代下,华为云数据管理服务DAS基于AI技术,于近期推出了智能SQL分析(包括慢SQL发现.SQL透视).workload级别索引推荐.存储空间预测等运维特性,加上原有的数据库运维能力,构 ...

  9. NanoDet:这是个小于4M超轻量目标检测模型

    摘要:NanoDet 是一个速度超快和轻量级的移动端 Anchor-free 目标检测模型. 前言 YOLO.SSD.Fast R-CNN等模型在目标检测方面速度较快和精度较高,但是这些模型比较大,不 ...

  10. 论文解读丨CIKM'22 MARINA:An MLP-Attention Model for Multivariate Time-Series Analysis

    摘要:华为云数据库创新Lab在论文<MARINA: An MLP-Attention Model for Multivariate Time-Series Analysis>中提出了华为自 ...