Spring:FactoryBean预加载逻辑以及自定义实现Mybatis的接口扫描

Spring:FactoryBean预加载逻辑以及自定义实现Mybatis的接口扫描

1 前言

参考Mybatis框架的@Mapper注解扫描Mapper接口的业务逻辑,其中集成Spring的逻辑里使用到了Spring框架的FactoryBean拓展点,本文针对Spring FactoryBean的加载流程进行分析和理解。

本文参考源码依赖:

<parent><artifactId>spring-boot-starter-parent</artifactId><groupId>org.springframework.boot</groupId><version>2.5.4</version>
</parent><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.3.0</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId>
<!--            <version>3.1.14</version>--></dependency></dependencies>

tips: idea中,如果Download Source只是下载了Spring-beans依赖,没有下载Spring-boot依赖的source,那么ctrl+鼠标左键点击方法时,不会提示该方法在依赖Spring-boot中的使用地方,只会提示Spring-beans依赖中的提示地方。

比如:Spring-beans(5.3.9)中的DefaultListableBeanFactory,即Bean工厂类下的findAnnotationOnBean方法:

@Override
@Nullable
public <A extends Annotation> A findAnnotationOnBean(String beanName, Class<A> annotationType)throws NoSuchBeanDefinitionException {return findMergedAnnotationOnBean(beanName, annotationType).synthesize(MergedAnnotation::isPresent).orElse(null);
}

该方法在下载了Spring-boot(2.5.4)依赖的source后,ctrl+鼠标左键可以提示并找到该方法所使用的地方,即AnnotationDependsOnDatabaseInitializationDetector类:

class AnnotationDependsOnDatabaseInitializationDetector implements DependsOnDatabaseInitializationDetector {@Overridepublic Set<String> detect(ConfigurableListableBeanFactory beanFactory) {Set<String> dependentBeans = new HashSet<>();for (String beanName : beanFactory.getBeanDefinitionNames()) {if (beanFactory.findAnnotationOnBean(beanName, DependsOnDatabaseInitialization.class) != null) {dependentBeans.add(beanName);}}return dependentBeans;}}

上述代码片段也是本文的切入点,即Spring框架针对FactoryBean的加载,是有初始化(以及缓存)的设计的,并非是实际调用时才会去生成对应的FactoryBean对象

2 Spring之FactoryBean加载逻辑

上述代码片段,beanFactory.findAnnotationOnBean(beanName, DependsOnDatabaseInitialization.class) != null),调用的是DefaultListableBeanFactory的findAnnotationOnBean方法,如下:

private <A extends Annotation> MergedAnnotation<A> findMergedAnnotationOnBean(String beanName, Class<A> annotationType)

核心FactoryBean的init逻辑,需要看getType(beanName):

Class<?> beanType = getType(beanName);

getType(beanName)方法中,如下的getTypeForFactoryBean(beanName, mbd, allowFactoryBeanInit)是处理FactoryBean的逻辑:

if (beanClass != null && FactoryBean.class.isAssignableFrom(beanClass)) {if (!BeanFactoryUtils.isFactoryDereference(name)) {// If it's a FactoryBean, we want to look at what it creates, not at the factory class.return getTypeForFactoryBean(beanName, mbd, allowFactoryBeanInit).resolve();}else {return beanClass;}
}
else {return (!BeanFactoryUtils.isFactoryDereference(name) ? beanClass : null);
}

getTypeForFactoryBean(String beanName, RootBeanDefinition mbd, boolean allowInit)方法源码片段:

protected ResolvableType getTypeForFactoryBean(String beanName, RootBeanDefinition mbd, boolean allowInit) {ResolvableType result = getTypeForFactoryBeanFromAttributes(mbd);if (result != ResolvableType.NONE) {return result;}if (allowInit && mbd.isSingleton()) {try {FactoryBean<?> factoryBean = doGetBean(FACTORY_BEAN_PREFIX + beanName, FactoryBean.class, null, true);Class<?> objectType = getTypeForFactoryBean(factoryBean);return (objectType != null ? ResolvableType.forClass(objectType) : ResolvableType.NONE);}catch (BeanCreationException ex) {if (ex.contains(BeanCurrentlyInCreationException.class)) {logger.trace(LogMessage.format("Bean currently in creation on FactoryBean type check: %s", ex));}else if (mbd.isLazyInit()) {logger.trace(LogMessage.format("Bean creation exception on lazy FactoryBean type check: %s", ex));}else {logger.debug(LogMessage.format("Bean creation exception on eager FactoryBean type check: %s", ex));}onSuppressedException(ex);}}return ResolvableType.NONE;
}

getTypeForFactoryBean方法会调用如下逻辑,即doGetBean(FACTORY_BEAN_PREFIX + beanName, FactoryBean.class, null, true),这里就会init初始化FactoryBean:

if (allowInit && mbd.isSingleton()) {try {FactoryBean<?> factoryBean = doGetBean(FACTORY_BEAN_PREFIX + beanName, FactoryBean.class, null, true);Class<?> objectType = getTypeForFactoryBean(factoryBean);return (objectType != null ? ResolvableType.forClass(objectType) : ResolvableType.NONE);}catch (BeanCreationException ex) {if (ex.contains(BeanCurrentlyInCreationException.class)) {logger.trace(LogMessage.format("Bean currently in creation on FactoryBean type check: %s", ex));}else if (mbd.isLazyInit()) {logger.trace(LogMessage.format("Bean creation exception on lazy FactoryBean type check: %s", ex));}else {logger.debug(LogMessage.format("Bean creation exception on eager FactoryBean type check: %s", ex));}onSuppressedException(ex);}
}

实际是DatabaseInitializationDependencyConfigurer的内部类:DependsOnDatabaseInitializationPostProcessor,调用postProcessBeanFactory来调用的FactoryBean的初始加载进入this.singletonObjects中的(this.singletonObjects归属于DefaultSingletonBeanRegistry类,doGetBean方法中可见)。

详细参考:DatabaseInitializationDependencyConfigurer.DependsOnDatabaseInitializationPostProcessor:

private Collection<String> detectDependsOnInitializationBeanNames(ConfigurableListableBeanFactory beanFactory)

doGetBean方法中部分代码片段如下,getSingleton方法会创建Bean并放入this.singletonObjects中:

protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)throws BeansException 
// Create bean instance.
if (mbd.isSingleton()) {sharedInstance = getSingleton(beanName, () -> {try {return createBean(beanName, mbd, args);}catch (BeansException ex) {// Explicitly remove instance from singleton cache: It might have been put there// eagerly by the creation process, to allow for circular reference resolution.// Also remove any beans that received a temporary reference to the bean.destroySingleton(beanName);throw ex;}});beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}

上面的getSingleton方法中,有如下的处理逻辑:

if (newSingleton) {addSingleton(beanName, singletonObject);
}

addSingleton即注册singletonObject,即单例Bean对象到this.singletonObjects,后续获取Bean时,如果这里存在则直接从其中获取即可(后续获取bean时,this.singletonObjects含有值,则直接返回该bean):

protected void addSingleton(String beanName, Object singletonObject) {synchronized (this.singletonObjects) {this.singletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);this.earlySingletonObjects.remove(beanName);this.registeredSingletons.add(beanName);}
}

getSingleton完整方法如下:

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {Assert.notNull(beanName, "Bean name must not be null");synchronized (this.singletonObjects) {Object singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {if (this.singletonsCurrentlyInDestruction) {throw new BeanCreationNotAllowedException(beanName,"Singleton bean creation not allowed while singletons of this factory are in destruction " +"(Do not request a bean from a BeanFactory in a destroy method implementation!)");}if (logger.isDebugEnabled()) {logger.debug("Creating shared instance of singleton bean '" + beanName + "'");}beforeSingletonCreation(beanName);boolean newSingleton = false;boolean recordSuppressedExceptions = (this.suppressedExceptions == null);if (recordSuppressedExceptions) {this.suppressedExceptions = new LinkedHashSet<>();}try {singletonObject = singletonFactory.getObject();newSingleton = true;}catch (IllegalStateException ex) {// Has the singleton object implicitly appeared in the meantime ->// if yes, proceed with it since the exception indicates that state.singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {throw ex;}}catch (BeanCreationException ex) {if (recordSuppressedExceptions) {for (Exception suppressedException : this.suppressedExceptions) {ex.addRelatedCause(suppressedException);}}throw ex;}finally {if (recordSuppressedExceptions) {this.suppressedExceptions = null;}afterSingletonCreation(beanName);}if (newSingleton) {addSingleton(beanName, singletonObject);}}return singletonObject;}
}

调用createBean(beanName, mbd, args)方法是创建FactoryBean的实际方法,核心方法逻辑看doCreateBean(beanName, mbdToUse, args):

try {Object beanInstance = doCreateBean(beanName, mbdToUse, args);if (logger.isTraceEnabled()) {logger.trace("Finished creating instance of bean '" + beanName + "'");}return beanInstance;
}
catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {// A previously detected exception with proper bean creation context already,// or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.throw ex;
}
catch (Throwable ex) {throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
}

可以看到doCreateBean(beanName, mbdToUse, args)方法中,实际的FactoryBean对象,是从this.factoryBeanInstanceCache中获取的(同时清除该FactoryBean对象缓存):

在这里插入图片描述

然后接下来,直接从BeanWrapperImpl对象中获取到该FactoryBean:

在这里插入图片描述

另外,this.factoryBeanInstanceCache的值的获取,其实在Spring-beans包下的AbstractAutowireCapableBeanFactory类的方法getSingletonFactoryBeanForTypeCheck(String beanName, RootBeanDefinition mbd)中有实现:

@Nullable
private FactoryBean<?> getSingletonFactoryBeanForTypeCheck(String beanName, RootBeanDefinition mbd) {synchronized (getSingletonMutex()) {BeanWrapper bw = this.factoryBeanInstanceCache.get(beanName);if (bw != null) {return (FactoryBean<?>) bw.getWrappedInstance();}Object beanInstance = getSingleton(beanName, false);if (beanInstance instanceof FactoryBean) {return (FactoryBean<?>) beanInstance;}if (isSingletonCurrentlyInCreation(beanName) ||(mbd.getFactoryBeanName() != null && isSingletonCurrentlyInCreation(mbd.getFactoryBeanName()))) {return null;}Object instance;try {// Mark this bean as currently in creation, even if just partially.beforeSingletonCreation(beanName);// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.instance = resolveBeforeInstantiation(beanName, mbd);if (instance == null) {bw = createBeanInstance(beanName, mbd, null);instance = bw.getWrappedInstance();}}catch (UnsatisfiedDependencyException ex) {// Don't swallow, probably misconfiguration...throw ex;}catch (BeanCreationException ex) {// Don't swallow a linkage error since it contains a full stacktrace on// first occurrence... and just a plain NoClassDefFoundError afterwards.if (ex.contains(LinkageError.class)) {throw ex;}// Instantiation failure, maybe too early...if (logger.isDebugEnabled()) {logger.debug("Bean creation exception on singleton FactoryBean type check: " + ex);}onSuppressedException(ex);return null;}finally {// Finished partial creation of this bean.afterSingletonCreation(beanName);}FactoryBean<?> fb = getFactoryBean(beanName, instance);if (bw != null) {this.factoryBeanInstanceCache.put(beanName, bw);}return fb;}
}

执行getSingletonFactoryBeanForTypeCheck(String beanName, RootBeanDefinition mbd)方法时,可见初始在FactoryBean的缓存factoryBeanInstanceCache中没有获取到,且getSingleton(beanName, false)方法也没有获取到该bean,因此接着往下执行:

在这里插入图片描述

执行到核心生成FactoryBean的逻辑,在下面的bw = createBeanInstance(beanName, mbd, null)方法中,生成的对象为BeanWrapperImpl:

在这里插入图片描述

若对上述代码有印象的话,其实这个方法,就是刚才doCreateBean方法中调用过的,判断FactoryBean的缓存中不存在该Bean时,即instanceWrapper == null时,再次调用createBeanInstance(beanName, mbd, args)方法:

BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {instanceWrapper = createBeanInstance(beanName, mbd, args);
}

在这里插入图片描述

那么核心分析FactoryBean的创建逻辑,就应该是createBeanInstance方法了,Spring-beans依赖中的AbstractAutowireCapableBeanFactory类的createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)方法如下:

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {// Make sure bean class is actually resolved at this point.Class<?> beanClass = resolveBeanClass(mbd, beanName);if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Bean class isn't public, and non-public access not allowed: " + beanClass.getName());}Supplier<?> instanceSupplier = mbd.getInstanceSupplier();if (instanceSupplier != null) {return obtainFromSupplier(instanceSupplier, beanName);}if (mbd.getFactoryMethodName() != null) {return instantiateUsingFactoryMethod(beanName, mbd, args);}// Shortcut when re-creating the same bean...boolean resolved = false;boolean autowireNecessary = false;if (args == null) {synchronized (mbd.constructorArgumentLock) {if (mbd.resolvedConstructorOrFactoryMethod != null) {resolved = true;autowireNecessary = mbd.constructorArgumentsResolved;}}}if (resolved) {if (autowireNecessary) {return autowireConstructor(beanName, mbd, null, null);}else {return instantiateBean(beanName, mbd);}}// Candidate constructors for autowiring?Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {return autowireConstructor(beanName, mbd, ctors, args);}// Preferred constructors for default construction?ctors = mbd.getPreferredConstructors();if (ctors != null) {return autowireConstructor(beanName, mbd, ctors, null);}// No special handling: simply use no-arg constructor.return instantiateBean(beanName, mbd);
}

首先从RootBeanDefinition中获取到beanClass,在自定义BeanDefinition时,比如我们自己写一个ImportBeanDefinitionRegistrar的实现类,重写registerBeanDefinitions方法注册BeanDefinition时,也需要设置该BeanDefinition的beanClass,作为这个Bean的Class对象(因为Spring底层框架,需要根据Class对象获取对应的生成Bean的Constructor方法来反射生成Bean):

在这里插入图片描述

继续执行到方法determineConstructorsFromBeanPostProcessors(beanClass, beanName),但是没有决策到Constructor:

在这里插入图片描述

最后执行instantiateBean(beanName, mbd)方法,生成FactoryBean对象:

在这里插入图片描述

AbstractAutowireCapableBeanFactory的instantiateBean(String beanName, RootBeanDefinition mbd)方法如下:

protected BeanWrapper instantiateBean(String beanName, RootBeanDefinition mbd) {try {Object beanInstance;if (System.getSecurityManager() != null) {beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () -> getInstantiationStrategy().instantiate(mbd, beanName, this),getAccessControlContext());}else {beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, this);}BeanWrapper bw = new BeanWrapperImpl(beanInstance);initBeanWrapper(bw);return bw;}catch (Throwable ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);}
}

这里的初始化策略,是使用的Spring-beans包下的org.springframework.beans.factory.support.CglibSubclassingInstantiationStrategy(继承了org.springframework.beans.factory.support.SimpleInstantiationStrategy):

在这里插入图片描述

在这里插入图片描述

SimpleInstantiationStrategy的Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner)方法如下:

@Override
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {// Don't override the class with CGLIB if no overrides.if (!bd.hasMethodOverrides()) {Constructor<?> constructorToUse;synchronized (bd.constructorArgumentLock) {constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;if (constructorToUse == null) {final Class<?> clazz = bd.getBeanClass();if (clazz.isInterface()) {throw new BeanInstantiationException(clazz, "Specified class is an interface");}try {if (System.getSecurityManager() != null) {constructorToUse = AccessController.doPrivileged((PrivilegedExceptionAction<Constructor<?>>) clazz::getDeclaredConstructor);}else {constructorToUse = clazz.getDeclaredConstructor();}bd.resolvedConstructorOrFactoryMethod = constructorToUse;}catch (Throwable ex) {throw new BeanInstantiationException(clazz, "No default constructor found", ex);}}}return BeanUtils.instantiateClass(constructorToUse);}else {// Must generate CGLIB subclass.return instantiateWithMethodInjection(bd, beanName, owner);}
}

下面可以看到,初始化前,依然获取到RootBeanDefinition的beanClass,判断如果是interface,则bean的初始化直接抛出异常Specified class is an interface,从这里可知,Mybatis的@Mapper接口扫描的逻辑,虽然使用的interface,但是Mybatis在将其注册为BeanDefinition时,Mybatis底层实际还是将其包装为FactoryBean的形式(并通过JDK动态代理的方式为接口方法实现增删改查的逻辑),因为beanClass为interface类型的bean,在Spring框架中是无法被初始化的

在这里插入图片描述

然后根据Class的getDeclaredConstructor()方法,通过beanClass获取到该Class声明的无参Constructor方法(即前面提到的,Spring框架中的BeanDefinition必须声明beanClass,用于反射获取bean实例的构造方法等等,这里Spring是取的Class的无参构造方法,故而一般我们在Spring框架中声明bean时,该Class需要具有默认的无参构造方法):

在这里插入图片描述

同时将该无参构造方法,设置为RootBeanDefinition的resolvedConstructorOrFactoryMethod,最后调用BeanUtils.instantiateClass(constructorToUse)即可生成该对象,instantiateClass(Constructor<T> ctor, Object… args)方法本质即调用constructor.newInstance(args)来反射生成实例对象:

public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {Assert.notNull(ctor, "Constructor must not be null");try {ReflectionUtils.makeAccessible(ctor);if (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(ctor.getDeclaringClass())) {return KotlinDelegate.instantiateClass(ctor, args);}else {Class<?>[] parameterTypes = ctor.getParameterTypes();Assert.isTrue(args.length <= parameterTypes.length, "Can't specify more arguments than constructor parameters");Object[] argsWithDefaultValues = new Object[args.length];for (int i = 0 ; i < args.length; i++) {if (args[i] == null) {Class<?> parameterType = parameterTypes[i];argsWithDefaultValues[i] = (parameterType.isPrimitive() ? DEFAULT_TYPE_VALUES.get(parameterType) : null);}else {argsWithDefaultValues[i] = args[i];}}return ctor.newInstance(argsWithDefaultValues);}}catch (InstantiationException ex) {throw new BeanInstantiationException(ctor, "Is it an abstract class?", ex);}catch (IllegalAccessException ex) {throw new BeanInstantiationException(ctor, "Is the constructor accessible?", ex);}catch (IllegalArgumentException ex) {throw new BeanInstantiationException(ctor, "Illegal arguments for constructor", ex);}catch (InvocationTargetException ex) {throw new BeanInstantiationException(ctor, "Constructor threw exception", ex.getTargetException());}
}

最后实例化完成,将其包裹为BeanWrapperImpl对象,如下,然后instantiateBean(beanName, mbd)方法返回该BeanWrapperImpl对象:

BeanWrapper bw = new BeanWrapperImpl(beanInstance);

最后关键步骤,将该包裹了初始化的FactoryBean对象的BeanWrapperImpl对象,置入this.factoryBeanInstanceCache,即FactoryBean缓存中:

在这里插入图片描述

上述逻辑,调用为AbstractAutowireCapableBeanFactory类下的protected ResolvableType getTypeForFactoryBean(String beanName, RootBeanDefinition mbd, boolean allowInit)方法,如下:

protected ResolvableType getTypeForFactoryBean(String beanName, RootBeanDefinition mbd, boolean allowInit) {// Check if the bean definition itself has defined the type with an attributeResolvableType result = getTypeForFactoryBeanFromAttributes(mbd);if (result != ResolvableType.NONE) {return result;}ResolvableType beanType =(mbd.hasBeanClass() ? ResolvableType.forClass(mbd.getBeanClass()) : ResolvableType.NONE);// For instance supplied beans try the target type and bean classif (mbd.getInstanceSupplier() != null) {result = getFactoryBeanGeneric(mbd.targetType);if (result.resolve() != null) {return result;}result = getFactoryBeanGeneric(beanType);if (result.resolve() != null) {return result;}}// Consider factory methodsString factoryBeanName = mbd.getFactoryBeanName();String factoryMethodName = mbd.getFactoryMethodName();// Scan the factory bean methodsif (factoryBeanName != null) {if (factoryMethodName != null) {// Try to obtain the FactoryBean's object type from its factory method// declaration without instantiating the containing bean at all.BeanDefinition factoryBeanDefinition = getBeanDefinition(factoryBeanName);Class<?> factoryBeanClass;if (factoryBeanDefinition instanceof AbstractBeanDefinition &&((AbstractBeanDefinition) factoryBeanDefinition).hasBeanClass()) {factoryBeanClass = ((AbstractBeanDefinition) factoryBeanDefinition).getBeanClass();}else {RootBeanDefinition fbmbd = getMergedBeanDefinition(factoryBeanName, factoryBeanDefinition);factoryBeanClass = determineTargetType(factoryBeanName, fbmbd);}if (factoryBeanClass != null) {result = getTypeForFactoryBeanFromMethod(factoryBeanClass, factoryMethodName);if (result.resolve() != null) {return result;}}}// If not resolvable above and the referenced factory bean doesn't exist yet,// exit here - we don't want to force the creation of another bean just to// obtain a FactoryBean's object type...if (!isBeanEligibleForMetadataCaching(factoryBeanName)) {return ResolvableType.NONE;}}// If we're allowed, we can create the factory bean and call getObjectType() earlyif (allowInit) {FactoryBean<?> factoryBean = (mbd.isSingleton() ?getSingletonFactoryBeanForTypeCheck(beanName, mbd) :getNonSingletonFactoryBeanForTypeCheck(beanName, mbd));if (factoryBean != null) {// Try to obtain the FactoryBean's object type from this early stage of the instance.Class<?> type = getTypeForFactoryBean(factoryBean);if (type != null) {return ResolvableType.forClass(type);}// No type found for shortcut FactoryBean instance:// fall back to full creation of the FactoryBean instance.return super.getTypeForFactoryBean(beanName, mbd, true);}}if (factoryBeanName == null && mbd.hasBeanClass() && factoryMethodName != null) {// No early bean instantiation possible: determine FactoryBean's type from// static factory method signature or from class inheritance hierarchy...return getTypeForFactoryBeanFromMethod(mbd.getBeanClass(), factoryMethodName);}result = getFactoryBeanGeneric(beanType);if (result.resolve() != null) {return result;}return ResolvableType.NONE;
}

即getSingletonFactoryBeanForTypeCheck(beanName, mbd):

if (allowInit) {FactoryBean<?> factoryBean = (mbd.isSingleton() ?getSingletonFactoryBeanForTypeCheck(beanName, mbd) :getNonSingletonFactoryBeanForTypeCheck(beanName, mbd));

上述的分析可知,在Spring框架设计中,allowInit的前提下,在通过getSingletonFactoryBeanForTypeCheck方法(可以理解为FactoryBean类型预检的方法)获取FactoryBean的type时,就已经做了FactoryBean的初始化,同时置入缓存中,减少了下次初始化FactoryBean时还需重新初始化的额外开销。同时,getSingletonFactoryBeanForTypeCheck(String beanName, RootBeanDefinition mbd)方法没有清除缓存的处理逻辑,无则创建并置入缓存,有则返回,只有后续调用doGetBean方法时,才会清除该FactoryBean缓存,并将其重新置入this.singletonObjects中。

上述调用getTypeForFactoryBean(beanName, mbd, allowFactoryBeanInit),发生在org.springframework.beans.factory.support.AbstractBeanFactory类中的isTypeMatch方法中

protected boolean isTypeMatch(String name, ResolvableType typeToMatch, boolean allowFactoryBeanInit)throws NoSuchBeanDefinitionException {String beanName = transformedBeanName(name);boolean isFactoryDereference = BeanFactoryUtils.isFactoryDereference(name);// Check manually registered singletons.Object beanInstance = getSingleton(beanName, false);if (beanInstance != null && beanInstance.getClass() != NullBean.class) {if (beanInstance instanceof FactoryBean) {if (!isFactoryDereference) {Class<?> type = getTypeForFactoryBean((FactoryBean<?>) beanInstance);return (type != null && typeToMatch.isAssignableFrom(type));}else {return typeToMatch.isInstance(beanInstance);}}else if (!isFactoryDereference) {if (typeToMatch.isInstance(beanInstance)) {// Direct match for exposed instance?return true;}else if (typeToMatch.hasGenerics() && containsBeanDefinition(beanName)) {// Generics potentially only match on the target class, not on the proxy...RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);Class<?> targetType = mbd.getTargetType();if (targetType != null && targetType != ClassUtils.getUserClass(beanInstance)) {// Check raw class match as well, making sure it's exposed on the proxy.Class<?> classToMatch = typeToMatch.resolve();if (classToMatch != null && !classToMatch.isInstance(beanInstance)) {return false;}if (typeToMatch.isAssignableFrom(targetType)) {return true;}}ResolvableType resolvableType = mbd.targetType;if (resolvableType == null) {resolvableType = mbd.factoryMethodReturnType;}return (resolvableType != null && typeToMatch.isAssignableFrom(resolvableType));}}return false;}else if (containsSingleton(beanName) && !containsBeanDefinition(beanName)) {// null instance registeredreturn false;}// No singleton instance found -> check bean definition.BeanFactory parentBeanFactory = getParentBeanFactory();if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {// No bean definition found in this factory -> delegate to parent.return parentBeanFactory.isTypeMatch(originalBeanName(name), typeToMatch);}// Retrieve corresponding bean definition.RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();// Setup the types that we want to match againstClass<?> classToMatch = typeToMatch.resolve();if (classToMatch == null) {classToMatch = FactoryBean.class;}Class<?>[] typesToMatch = (FactoryBean.class == classToMatch ?new Class<?>[] {classToMatch} : new Class<?>[] {FactoryBean.class, classToMatch});// Attempt to predict the bean typeClass<?> predictedType = null;// We're looking for a regular reference but we're a factory bean that has// a decorated bean definition. The target bean should be the same type// as FactoryBean would ultimately return.if (!isFactoryDereference && dbd != null && isFactoryBean(beanName, mbd)) {// We should only attempt if the user explicitly set lazy-init to true// and we know the merged bean definition is for a factory bean.if (!mbd.isLazyInit() || allowFactoryBeanInit) {RootBeanDefinition tbd = getMergedBeanDefinition(dbd.getBeanName(), dbd.getBeanDefinition(), mbd);Class<?> targetType = predictBeanType(dbd.getBeanName(), tbd, typesToMatch);if (targetType != null && !FactoryBean.class.isAssignableFrom(targetType)) {predictedType = targetType;}}}// If we couldn't use the target type, try regular prediction.if (predictedType == null) {predictedType = predictBeanType(beanName, mbd, typesToMatch);if (predictedType == null) {return false;}}// Attempt to get the actual ResolvableType for the bean.ResolvableType beanType = null;// If it's a FactoryBean, we want to look at what it creates, not the factory class.if (FactoryBean.class.isAssignableFrom(predictedType)) {if (beanInstance == null && !isFactoryDereference) {beanType = getTypeForFactoryBean(beanName, mbd, allowFactoryBeanInit);predictedType = beanType.resolve();if (predictedType == null) {return false;}}}else if (isFactoryDereference) {// Special case: A SmartInstantiationAwareBeanPostProcessor returned a non-FactoryBean// type but we nevertheless are being asked to dereference a FactoryBean...// Let's check the original bean class and proceed with it if it is a FactoryBean.predictedType = predictBeanType(beanName, mbd, FactoryBean.class);if (predictedType == null || !FactoryBean.class.isAssignableFrom(predictedType)) {return false;}}// We don't have an exact type but if bean definition target type or the factory// method return type matches the predicted type then we can use that.if (beanType == null) {ResolvableType definedType = mbd.targetType;if (definedType == null) {definedType = mbd.factoryMethodReturnType;}if (definedType != null && definedType.resolve() == predictedType) {beanType = definedType;}}// If we have a bean type use it so that generics are consideredif (beanType != null) {return typeToMatch.isAssignableFrom(beanType);}// If we don't have a bean type, fallback to the predicted typereturn typeToMatch.isAssignableFrom(predictedType);
}

调用getTypeForFactoryBean(beanName, mbd, allowFactoryBeanInit)的方法:

// If it's a FactoryBean, we want to look at what it creates, not the factory class.
if (FactoryBean.class.isAssignableFrom(predictedType)) {if (beanInstance == null && !isFactoryDereference) {beanType = getTypeForFactoryBean(beanName, mbd, allowFactoryBeanInit);predictedType = beanType.resolve();if (predictedType == null) {return false;}}
}

在这里插入图片描述

上述的FactoryBean demo参考类:

/*** @author xiaoxu* @date 2024-01-02* java_demo:com.xiaoxu.test.impo.SelectorFactoryBean*/
@Component
public class SelectorFactoryBean implements FactoryBean<SelectorProxy> {@Overridepublic SelectorProxy getObject() throws Exception {SelectorProxy proxy = new SelectorProxy();System.out.println("获取FactoryBean SelectorProxy.");return proxy;}@Overridepublic Class<?> getObjectType() {return SelectorProxy.class;}@Overridepublic boolean isSingleton() {return FactoryBean.super.isSingleton();}
}

上述调用的isTypeMatch(beanName, type, allowFactoryBeanInit)方法,实际是调用的org.springframework.beans.factory.support.DefaultListableBeanFactory类的private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit)方法:

在这里插入图片描述

这里是mybatis框架中,根据type(此处为mybatis的SqlSessionTemplate类)来获取所有匹配该类型的beanDefinitionNames,可见doGetBeanNamesForType方法,直接对FactoryBean做了bean实例缓存的初始化了:

如果上述没有匹配到,那么给FactoryBean的beanName前缀加上"&"再次匹配,如果还是没有匹配到,那么就不添加该beanName(当然此处主要关心FactoryBean的初始化逻辑):

在这里插入图片描述

实际调用方法为org.springframework.beans.factory.support.DefaultListableBeanFactory#getBeanNamesForType(@Nullable Class<?> type, boolean includeNonSingletons, boolean allowEagerInit):

在这里插入图片描述

在这里插入图片描述

结论:由此可知getBeanNamesForType(@Nullable Class<?> type)方法,默认就会执行FactoryBean的init操作,将其创建的FactoryBean对象置入缓存中(注意这里创建的是FactoryBean本身,非FactoryBean调用getObject()方法获取的对象)。

该方法由Mybatis的Spring自动配置类MybatisAutoConfiguration$AutoConfiguredMapperScannerRegistrar声明并调用的:

在这里插入图片描述

AutoConfiguredMapperScannerRegistrar类,即Mybatis注册MapperScannerConfigurer的BeanDefinition的ImportBeanDefinitionRegistrar实现类:

在这里插入图片描述

如下可见,这里处理的逻辑,就是Spring针对ImportBeanDefinitionRegistrar的拓展点处理逻辑:

在这里插入图片描述

小结:

(1) 通过调用getBeanNamesForType(@Nullable Class<?> type)方法,对于FactoryBean处理类型type是否匹配时(参考内部方法:getTypeForFactoryBean(beanName, mbd, allowFactoryBeanInit)),Spring心想,这里已经处理过FactoryBean(并且都初始化过实例对象了),那么下次处理不就可以方便些了么?于是将其置入缓存this.factoryBeanInstanceCache中,减少反射调用的开销。

上述是Mybatis框架在注册扫描BeanDefinition,即MapperScannerConfigurer时,调用了factory.getBeanNamesForType(type)方法时处理的逻辑,此时Spring框架还在处理各个自动配置类,还处于执行的初期。

AutoConfiguredMapperScannerRegistrar代码片段:

if (injectSqlSession && this.beanFactory instanceof ListableBeanFactory) {ListableBeanFactory listableBeanFactory = (ListableBeanFactory)this.beanFactory;Optional<String> sqlSessionTemplateBeanName = Optional.ofNullable(this.getBeanNameForType(SqlSessionTemplate.class, listableBeanFactory));Optional<String> sqlSessionFactoryBeanName = Optional.ofNullable(this.getBeanNameForType(SqlSessionFactory.class, listableBeanFactory));if (!sqlSessionTemplateBeanName.isPresent() && sqlSessionFactoryBeanName.isPresent()) {builder.addPropertyValue("sqlSessionFactoryBeanName", sqlSessionFactoryBeanName.get());} else {builder.addPropertyValue("sqlSessionTemplateBeanName", sqlSessionTemplateBeanName.orElse("sqlSessionTemplate"));}}

(2)后续AnnotationDependsOnDatabaseInitializationDetector类执行detect方法时,需要判断bean的Class上,是否具有@DependsOnDatabaseInitialization注解。

通过调用findAnnotationOnBean(String beanName, Class<A> annotationType)方法,即内部调用的Class<?> getType(String name)方法,getType(String name)方法其中又会调用到getTypeForFactoryBean(String beanName, RootBeanDefinition mbd, boolean allowInit)方法,用于获取bean的Class,此时bean也做了初始化,那么Spring又想,这里也需要处理FactoryBean(并且初始化实例对象),如果缓存this.factoryBeanInstanceCache中存在该FactoryBean的对象,那我直接用不就好了?于是就清除this.factoryBeanInstanceCache缓存中该对象(remove方法,同时返回清除的对象),如果返回结果不为null,那么我就直接置入this.singletonObjects中,如果为null,那我就手动调用下createBeanInstance(beanName, mbd, args)创建就好了。

这里会调用doGetBean(FACTORY_BEAN_PREFIX + beanName, FactoryBean.class, null, true)方法。

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

这里的调用发生在DependsOnDatabaseInitializationPostProcessor的postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)方法处,该类实现了BeanFactoryPostProcessor接口,亦是Spring的拓展点之一,调用的时间节点晚于上述(1)点中的自动配置类注册BeanDefinition的时间节点,故而是后发生的。

逻辑参考invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory),如下:

在这里插入图片描述

3 结合FactoryBean自定义注解实现Bean扫描

下面以我自定义实现扫描的CustomerMapper为例,根据自定义的SqlMapper注解,将该CustomerMapper接口,注册为FactoryBean的BeanDefinition:

在这里插入图片描述
test:

package com.xiaoxu.test.impo.test;import com.xiaoxu.test.impo.ifc.SqlMapper;/*** @author xiaoxu* @date 2024-02-23*/
@SqlMapper
public interface CustomerMapper {void queryCustomerById(String id);}
package com.xiaoxu.test.impo.test;import com.xiaoxu.test.impo.ifc.SqlMapper;/*** @author xiaoxu* @date 2024-02-20*/
@SqlMapper
public interface FruitSqlMapper {void queryFruitById();}

在这里插入图片描述

autoconfigure:

package com.xiaoxu.test.impo.autoconfigure;import com.xiaoxu.test.impo.core.XImportRegistrar;
import com.xiaoxu.test.impo.ifc.EnableSqlMapperProxy;
import com.xiaoxu.test.impo.ifc.RegistrarImport;
import com.xiaoxu.test.impo.ifc.XImport;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Configuration;/*** @author xiaoxu* @date 2023-12-26* java_demo:com.xiaoxu.test.impo.autoconfigure.XImportAutoConfiguration*/
@RegistrarImport
public class XImportAutoConfiguration {@Configuration@XImport@EnableSqlMapperProxy@ConditionalOnMissingBean(XImportRegistrar.class)public static class XImportAutoSelector implements InitializingBean {public XImportAutoSelector() {}@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("Not found registrar for registering sqlMapper.");}}}

core:

ClassPathSqlMapperScanner :

package com.xiaoxu.test.impo.core;import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.AssignableTypeFilter;
import org.springframework.core.type.filter.TypeFilter;import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.Set;/*** @author xiaoxu* @date 2024-01-22* java_demo:com.xiaoxu.test.impo.core.ClassPathSqlMapperScanner*/
public class ClassPathSqlMapperScanner extends ClassPathBeanDefinitionScanner {private Class<? extends SqlMapperFactoryBean> sqlMapperFactoryBeanClass;static final String FACTORY_BEAN_OBJECT_TYPE = "factoryBeanObjectType";private Class<?> markerSqlMapperClazz;private Class<? extends Annotation> detectClass;public ClassPathSqlMapperScanner(BeanDefinitionRegistry registry) {super(registry, false);}@Overrideprotected Set<BeanDefinitionHolder> doScan(String... basePackages) {Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);if (beanDefinitions.isEmpty()) {System.out.println("No Sql Mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");} else {this.processBeanDefinitions(beanDefinitions);}return beanDefinitions;}private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitions) {System.out.println("【SqlMapper】开始处理beandefinition'" + beanDefinitionHolder.getBeanName());BeanDefinition beanDefinition = beanDefinitionHolder.getBeanDefinition();String beanClassName = beanDefinition.getBeanClassName();beanDefinition.setBeanClassName(SqlMapperFactoryBean.class.getName());beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName);}}public void setSqlMapperFactoryBeanClass(Class<? extends SqlMapperFactoryBean> sqlMapperFactoryBeanClass) {this.sqlMapperFactoryBeanClass = sqlMapperFactoryBeanClass;}public void setMarkerSqlMapperClazz(Class<?> markerSqlMapperClazz) {this.markerSqlMapperClazz = markerSqlMapperClazz;}public void setDetectClass(Class<? extends Annotation> detectClass) {this.detectClass = detectClass;}public void registerFilters() {boolean acceptAllClazz = true;if (this.detectClass != null) {this.addIncludeFilter(new AnnotationTypeFilter(this.detectClass));acceptAllClazz = false;}if (this.markerSqlMapperClazz != null) {this.addIncludeFilter(new AssignableTypeFilter(this.markerSqlMapperClazz));}if (acceptAllClazz) {this.addIncludeFilter(new TypeFilter() {@Overridepublic boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {return true;}});}this.addExcludeFilter(((metadataReader, metadataReaderFactory) -> {String className = metadataReader.getClassMetadata().getClassName();return className.endsWith("package-info");}));}@Overrideprotected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {AnnotationMetadata metadata = beanDefinition.getMetadata();return (metadata.isInterface() || metadata.isConcrete()) && metadata.isIndependent();}
}

DetectorScanner:

package com.xiaoxu.test.impo.core;import com.google.common.collect.Lists;
import com.xiaoxu.test.impo.ifc.EnableSqlMapperProxy;
import com.xiaoxu.test.impo.infrastructure.AttributeUtil;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.boot.autoconfigure.AutoConfigurationPackages;
import org.springframework.context.annotation.ScannedGenericBeanDefinition;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;/*** @author xiaoxu* @date 2024-01-18* java_demo:com.xiaoxu.test.impo.core.DetectorScanner*/
public class DetectorScanner implements BeanDefinitionRegistryPostProcessor, BeanFactoryAware {static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";private String resourcePattern = DEFAULT_RESOURCE_PATTERN;private static final String classConcat = ".";private ResourcePatternResolver resourcePatternResolver;private MetadataReaderFactory metadataReaderFactory;private Class<? extends SqlMapperFactoryBean> sqlMapperFactoryBeanClass;private Class<?> markerClazz;private BeanNameGenerator nameGenerator;private final List<TypeFilter> includeFilters = new ArrayList<>();private final boolean useDefaultFilters;private AnnotationMetadata metadata;private AnnotationAttributes attributes;private BeanFactory beanFactory;private Object detectObject;private String basePackage;public DetectorScanner(boolean useDefaultFilters, @Nullable List<TypeFilter> detectProxyFilters) {this.useDefaultFilters = useDefaultFilters;this.nameGenerator = null;if (this.useDefaultFilters) {registerDefaultFilters();} else {this.includeFilters.addAll(Optional.ofNullable(detectProxyFilters).orElse(Lists.newArrayList()));}}@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {System.out.println("开始注册DetectorScanner");if (AutoConfigurationPackages.has(beanFactory)) {List<String> packages = AutoConfigurationPackages.get(this.beanFactory);System.out.println("packages is :{" + packages + "}.");System.out.println("先打印原有属性值:" + this.attributes);ConfigurableListableBeanFactory beanFactory = (ConfigurableListableBeanFactory) this.beanFactory;String scannedPath = getScannedPath();System.out.println("扫描:" + scannedPath);try {String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +resolveBasePackage(scannedPath) + "/" + resourcePattern;loadDetectObject(beanFactory, getResources(packageSearchPath));System.out.println("detect:" + this.detectObject);if (getDetectObject() != null) {ClassPathSqlMapperScanner scanner = new ClassPathSqlMapperScanner(registry);scanner.setSqlMapperFactoryBeanClass(this.sqlMapperFactoryBeanClass);scanner.setBeanNameGenerator(this.nameGenerator);scanner.setMarkerSqlMapperClazz(this.markerClazz);scanner.setDetectClass(((Class<? extends Annotation>) getDetectObject()));scanner.registerFilters();scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, " ,;\n\t"));}} catch (IOException ex) {throw new RuntimeException("I/O failure during classpath scanning", ex);}}}@SuppressWarnings("rawtypes")private String getScannedPath() {String scannedPath = null;Object value = this.attributes.get("value");String path = (String) this.attributes.get("path");if (StringUtils.hasText(path)) {scannedPath = path;}if (!StringUtils.hasText(scannedPath) && Object.class != value) {String name = ((Class) value).getName();scannedPath = name.substring(0, name.lastIndexOf((char) 46));}if (!StringUtils.hasText(scannedPath)) {String metadataClassName;scannedPath = this.metadata != null ?((metadataClassName = this.metadata.getClassName()).substring(0, metadataClassName.lastIndexOf((char) 46))): "";}if (!StringUtils.hasText(scannedPath)) {throw new RuntimeException("DetectorScanner needs Value Or Path to transfer Path, now detect stopped.");}return scannedPath;}private Resource[] getResources(String packageSearchPath) throws IOException {return getResourcePatternResolver().getResources(packageSearchPath);}private void loadDetectObject(ConfigurableListableBeanFactory beanFactory, Resource[] resources) throws IOException {ResourceDetectHolder[] resourceDetectHolders = new ResourceDetectHolder[0];try {for (Resource resource : resources) {if (resource.isReadable()) {MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);resourceDetectHolders = ArrayUtils.addAll(resourceDetectHolders, loadResourceHolder(beanFactory, resource, metadataReader));}}} catch (IOException e) {throw new RuntimeException("IO error:" + e.getMessage());}if (resourceDetectHolders.length <= 0) {throw new RuntimeException("No detect proxy resource holder found.");}if (resourceDetectHolders.length > 1) {throw new RuntimeException("More than one detect proxy resource holder found.");}if (ArrayUtils.isEmpty(resourceDetectHolders))throw new RuntimeException("Could not found detect proxy object.");this.detectObject = resourceDetectHolders[0].detectProxy;}private ResourceDetectHolder[] loadResourceHolder(ConfigurableListableBeanFactory beanFactory, Resource resource, MetadataReader metadataReader) throws IOException {List<ResourceDetectHolder> resourceDetectHolders = Lists.newArrayList();for (TypeFilter tf : this.includeFilters) {if (tf.match(metadataReader, getMetadataReaderFactory())) {Object detectProxy = findDetectProxy(beanFactory, metadataReader);resourceDetectHolders.add(new ResourceDetectHolder(resource, detectProxy));}}return resourceDetectHolders.toArray(new ResourceDetectHolder[0]);}private Object findDetectProxy(ConfigurableListableBeanFactory beanFactory, MetadataReader metadataReader) {String beanWrapName = getBeanWrapName(metadataReader);BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanWrapName);AnnotationAttributes attributes = new AnnotationAttributes();if (beanDefinition instanceof ScannedGenericBeanDefinition) {attributes = AttributeUtil.getAttributes(((ScannedGenericBeanDefinition) beanDefinition).getMetadata(), getSelectorDetectType(), false);}Assert.notNull(attributes.get("value"), () -> {return "No Proxy Object found. Is " + beanWrapName + "has config " + getSelectorDetectType().getName() + "it ?";});return Optional.ofNullable(attributes.get("value")).orElseThrow(() -> new RuntimeException("non null detect proxy value allowed."));}private boolean isCandidateCondition(AnnotationMetadata metadata) {return metadata.isConcrete() && metadata.isIndependent() && metadata.getEnclosingClassName() != null;}protected String resolveBasePackage(String basePackage) {return ClassUtils.convertClassNameToResourcePath(basePackage);}@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {System.out.println("调用【postProcessBeanFactory】");}private String getBeanWrapName(MetadataReader metadataReader) {ClassMetadata classMetadata = metadataReader.getClassMetadata();String enclosingName = classMetadata.getEnclosingClassName();enclosingName = enclosingName == null ? "" : enclosingName;String className = classMetadata.getClassName();return enclosingName.substring(enclosingName.lastIndexOf((char) 46) + 1)+ classConcat + className.substring(className.lastIndexOf((char) 36) + 1);}@Overridepublic void setBeanFactory(@NonNull BeanFactory beanFactory) throws BeansException {this.beanFactory = beanFactory;}private ResourcePatternResolver getResourcePatternResolver() {if (this.resourcePatternResolver == null) {this.resourcePatternResolver = new PathMatchingResourcePatternResolver();}return this.resourcePatternResolver;}public MetadataReaderFactory getMetadataReaderFactory() {if (this.metadataReaderFactory == null) {this.metadataReaderFactory = new CachingMetadataReaderFactory();}return metadataReaderFactory;}@SuppressWarnings("all")private void registerDefaultFilters() {this.includeFilters.add(new TypeFilter() {@Overridepublic boolean match(@NonNull MetadataReader metadataReader, @NonNull MetadataReaderFactory metadataReaderFactory) throws IOException {AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();if (isCandidateCondition(metadata)) {return metadata.hasAnnotation(getSelectorDetectType().getName());}return false;}});}public void setSqlMapperFactoryBeanClass(Class<? extends SqlMapperFactoryBean> sqlMapperFactoryBeanClass) {this.sqlMapperFactoryBeanClass = sqlMapperFactoryBeanClass;}public void setAttributes(AnnotationAttributes attributes) {this.attributes = attributes;}public void setMetadata(AnnotationMetadata metadata) {this.metadata = metadata;}public void setBasePackage(String basePackage) {this.basePackage = basePackage;}private Class<?> getSelectorDetectType() {return EnableSqlMapperProxy.class;}public Object getDetectObject() {return detectObject;}static class ResourceDetectHolder {final Resource resource;final Object detectProxy;public ResourceDetectHolder(Resource resource, Object detectProxy) {this.resource = resource;this.detectProxy = detectProxy;}public Resource getResource() {return resource;}public Object getDetectProxy() {return detectProxy;}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;ResourceDetectHolder that = (ResourceDetectHolder) o;return Objects.equals(resource, that.resource) && Objects.equals(detectProxy, that.detectProxy);}@Overridepublic int hashCode() {return Objects.hash(resource, detectProxy);}}}

SqlMapperBeanNameGenerator:

package com.xiaoxu.test.impo.core;import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.util.Assert;/*** @author xiaoxu* @date 2024-02-02* java_demo:com.xiaoxu.test.impo.core.SqlMapperBeanNameGenerator*/
public class SqlMapperBeanNameGenerator implements BeanNameGenerator {private static final String TAG = "ByXiaoxu";private static final String CON = "AutoMapper$";private Class<?> sqlMapperInterface;private Predicate names;public static SqlMapperBeanNameGenerator getSqlMapperBeanNameGenerator(Class<?> sqlMapperInterface) {return new SqlMapperBeanNameGenerator(sqlMapperInterface);}private SqlMapperBeanNameGenerator(Class<?> sqlMapperInterface) {Assert.notNull(sqlMapperInterface, () -> "sqlMapperInterface is null.");this.sqlMapperInterface = sqlMapperInterface;this.names = new Predicate() {@Overridepublic boolean evaluate(String name) {return false;}};}@Overridepublic String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {String beanClassName = definition.getBeanClassName();Assert.state(beanClassName != null, "No bean class name set");String base = beanClassName + CON + this.getTag() + Integer.toHexString(this.sqlMapperInterface.hashCode());String attempt = base;for (int var9 = 2; names.evaluate(attempt); attempt = attempt + "_" + var9++) {}return attempt;}private String getTag() {return TAG;}public void setNames(Predicate names) {Assert.state(names != null, () -> "names could not be null.");this.names = names;}public interface Predicate {boolean evaluate(String name);}}

SqlMapperFactoryBean<T>:

package com.xiaoxu.test.impo.core;import org.springframework.beans.factory.FactoryBean;/*** @author xiaoxu* @date 2024-01-22* java_demo:com.xiaoxu.test.impo.core.SqlMapperFactoryBean*/
public class SqlMapperFactoryBean<T> implements FactoryBean<T> {private Class<T> sqlMapperClazz;public SqlMapperFactoryBean() {}private SqlMapperFactoryBean(Class<T> sqlMapperClazz) {this.sqlMapperClazz = sqlMapperClazz;}@Overridepublic T getObject() throws Exception {return null;}@Overridepublic Class<?> getObjectType() {return null;}@Overridepublic boolean isSingleton() {return FactoryBean.super.isSingleton();}
}

WrapBeanNameGenerator:

package com.xiaoxu.test.impo.core;import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.util.Assert;/*** @author xiaoxu* @date 2023-12-21* java_demo:com.xiaoxu.test.impo.core.WrapBeanNameGenerator*/
public class WrapBeanNameGenerator implements BeanNameGenerator {public static final WrapBeanNameGenerator INSTANCE = new WrapBeanNameGenerator();private static final String SUFFIX = "ByXiaoxu";private static final String CON = "Auto$";@Overridepublic String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {String beanClassName = definition.getBeanClassName();Assert.state(beanClassName != null, "No bean class name set");return beanClassName + CON + SUFFIX;}
}

XImportRegistrar:

package com.xiaoxu.test.impo.core;import com.google.common.collect.Lists;
import com.xiaoxu.test.impo.ifc.RegistrarImport;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.boot.autoconfigure.AutoConfigurationPackages;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.StringUtils;import java.util.List;/*** @author xiaoxu* @date 2023-12-21* java_demo:com.xiaoxu.test.impo.core.XImportRegistrar*/
public class XImportRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware, BeanClassLoaderAware, EnvironmentAware {private static final String DEFAULT_FILTER_CONFIG = "useDefaultFilter";private ClassLoader classLoader;private BeanFactory beanFactory;private Environment environment;@Overridepublic void setBeanClassLoader(ClassLoader classLoader) {this.classLoader = classLoader;}@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {this.beanFactory = beanFactory;}@Overridepublic void setEnvironment(Environment environment) {this.environment = environment;}@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {if (!AutoConfigurationPackages.has(beanFactory)) {return;}System.out.println("路径如果@AutoConfigurationPackage注解不配置basePackages以及Class路径,那么默认取启动类@SpringBootApplication的所在包");List<String> packages = AutoConfigurationPackages.get(this.beanFactory);System.out.println("packages is :{" + packages + "}.");System.out.println("annotated is:" + importingClassMetadata);System.out.println(importingClassMetadata.getClassName());System.out.println("Registrar开始注册DetectorScanner(有路径)");BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(DetectorScanner.class);// role 2 means internal working.builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);AnnotationAttributes annotationAttributes = null;if (importingClassMetadata.hasAnnotation(RegistrarImport.class.getName())) {System.out.println("我有注解RegistrarImport:");annotationAttributes = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(RegistrarImport.class.getName(), false));System.out.println(annotationAttributes);builder.addPropertyValue("attributes", annotationAttributes);builder.addPropertyValue("metadata", importingClassMetadata);}builder.addPropertyValue("sqlMapperFactoryBeanClass", SqlMapperFactoryBean.class);builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(packages));builder.addConstructorArgValue(Boolean.parseBoolean(System.getProperty(DEFAULT_FILTER_CONFIG, "true")));builder.addConstructorArgValue(Lists.newArrayList());AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();String detectorBeanName = WrapBeanNameGenerator.INSTANCE.generateBeanName(beanDefinition, registry);System.out.println("开始注册DetectorScanner name is(有路径):" + detectorBeanName);registry.registerBeanDefinition(detectorBeanName, beanDefinition);}}

XImportSelector:

package com.xiaoxu.test.impo.core;import com.xiaoxu.test.impo.ifc.EnableSqlMapperProxy;
import org.springframework.context.annotation.DeferredImportSelector;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;import java.util.Arrays;
import java.util.stream.Collectors;/*** @author xiaoxu* @date 2023-12-22* java_demo:com.xiaoxu.test.impo.core.XImportSelector*/
public class XImportSelector implements DeferredImportSelector {private AnnotationAttributes detectAttributes;@Overridepublic String[] selectImports(AnnotationMetadata annotationMetadata) {System.out.println("3. 获取外部父类");return new String[]{annotationMetadata.getEnclosingClassName()};}public void setDetectAttributes(AnnotationAttributes annotationAttributes) {this.detectAttributes = annotationAttributes;}public AnnotationAttributes getDetectAttributes() {return detectAttributes;}@Overridepublic Class<? extends Group> getImportGroup() {return XImportSelector.BundleGroup.class;}private static class BundleGroup implements DeferredImportSelector.Group {private AnnotationMetadata metadata;private AnnotationAttributes attributes;@Overridepublic void process(AnnotationMetadata metadata, DeferredImportSelector selector) {this.metadata = metadata;System.out.println("1. 处理bundle");System.out.println(metadata);System.out.println(selector);AnnotationAttributes attributes = ((XImportSelector) selector).getAttributes(metadata);this.attributes = attributes;
//            factory处理不同类型的分别处理  filter  实际需要操作的配置类  为XImportSelector(DeferredImportSelector selector)
//            填充参数等等}@Overridepublic Iterable<Entry> selectImports() {System.out.println("2. 执行innnerselectImports");XImportSelector xImportSelector = new XImportSelector();String[] importNames = xImportSelector.selectImports(this.metadata);xImportSelector.setDetectAttributes(this.attributes);return Arrays.stream(importNames).map(name -> new Group.Entry(this.metadata, name)).collect(Collectors.toList());}}protected AnnotationAttributes getAttributes(AnnotationMetadata annotationMetadata) {String name = this.getSelectorDetectType().getName();AnnotationAttributes attributes = AnnotationAttributes.fromMap(annotationMetadata.getAnnotationAttributes(name, true));Assert.notNull(attributes, () -> {return "No auto-configuration attributes found. Is " + annotationMetadata.getClassName() + " annotated with " + ClassUtils.getShortName(name) + "?";});return attributes;}private Class<?> getSelectorDetectType() {return EnableSqlMapperProxy.class;}}

ifc:

package com.xiaoxu.test.impo.ifc;import java.lang.annotation.*;@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface EnableSqlMapperProxy {Class<?> value() default SqlMapper.class;
}
package com.xiaoxu.test.impo.ifc;import com.xiaoxu.test.impo.core.XImportRegistrar;
import org.springframework.context.annotation.Import;import java.lang.annotation.*;@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({XImportRegistrar.class})
public @interface RegistrarImport {Class<?> value() default Object.class;String path() default "";Class<?> type() default SqlDetector.class;}
package com.xiaoxu.test.impo.ifc;import java.lang.annotation.*;@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface SqlDetector {/*** @return sql*/String sql();/*** @return 类型*/String type();}
package com.xiaoxu.test.impo.ifc;import java.lang.annotation.*;@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface SqlMapper {/*** @return 唯一id*/String uniqueId() default "";}
package com.xiaoxu.test.impo.ifc;import com.xiaoxu.test.impo.core.XImportBeanPostProcessor;
import com.xiaoxu.test.impo.core.XImportSelector;
import org.springframework.context.annotation.Import;import java.lang.annotation.*;@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
@Inherited
@Import({XImportSelector.class, XImportBeanPostProcessor.class})
public @interface XImport {
}

infrastructure:

package com.xiaoxu.test.impo.infrastructure;import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;/*** @author xiaoxu* @date 2024-01-18* java_demo:com.xiaoxu.test.impo.infrastructure.AttributeUtil*/
public class AttributeUtil {public static AnnotationAttributes getAttributes(AnnotationMetadata annotationMetadata, Class<?> type, boolean classValueAsString) {String name = type.getName();AnnotationAttributes attributes = AnnotationAttributes.fromMap(annotationMetadata.getAnnotationAttributes(name, classValueAsString));Assert.notNull(attributes, () -> {return "No annotated attributes found. Is " + annotationMetadata.getClassName() + " annotated with " + ClassUtils.getShortName(name) + "?";});return attributes;}}

上述代码可示,Spring扫描到的customerMapper的FactoryBean(以及fruitSqlMapper),即如上自定义注解@SqlMapper扫描生成的。

SpringBoot启动类如下:

在这里插入图片描述

启动SpringBoot项目,效果如下:

在这里插入图片描述

上述的代码实现参考自Mybatis的@Mapper接口扫描逻辑,同时也可以看出,Mybatis扫描Mapper接口所在包路径时,是按照SpringBoot项目的bean扫描路径来实现的,所以也会加入到Spring的Bean管理中,实现的形式采用的Spring的FactoryBean,故而我们平时开发中,可以直接使用@Autowired或@Resource等注解进行Mybatis的Mapper接口方法调用。

另外上述的FactoryBean的实现需要注意,比如如下的调用:

在这里插入图片描述

启动时报错:Bean named ‘customerMapper’ is expected to be of type ‘com.xiaoxu.test.impo.test.CustomerMapper’ but was actually of type ‘org.springframework.beans.factory.support.NullBean’

在这里插入图片描述

这是因为SqlMapperFactoryBean<T>的getObject()方法返回值为null导致的,如下修改即可:

测试接口增加原已定义的注解:SqlDetector

package com.xiaoxu.test.impo.test;import com.xiaoxu.test.impo.ifc.SqlDetector;
import com.xiaoxu.test.impo.ifc.SqlMapper;/*** @author xiaoxu* @date 2024-02-23*/
@SqlMapper
public interface CustomerMapper {@SqlDetector(type = "select", sql = "select * from customer")void queryCustomerById(String id);}
package com.xiaoxu.test.impo.test;import com.xiaoxu.test.impo.ifc.SqlDetector;
import com.xiaoxu.test.impo.ifc.SqlMapper;/*** @author xiaoxu* @date 2024-02-20*/
@SqlMapper
public interface FruitSqlMapper {@SqlDetector(type = "select", sql = "select * from fruit")void queryFruitById();}

新增代理类SqlMapperProxy<T>:

在这里插入图片描述

SqlMapperProxy<T>:

package com.xiaoxu.test.impo.core;import com.xiaoxu.test.impo.ifc.SqlDetector;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;/*** @author xiaoxu* @date 2024-03-01* java_demo:com.xiaoxu.test.impo.core.SqlMapperProxy*/
public class SqlMapperProxy<T> implements InvocationHandler {Class<T> mapper;Map<String, Method> cache;public SqlMapperProxy(Class<T> mapper) {this.mapper = mapper;this.cache = Arrays.stream(this.mapper.getDeclaredMethods()).filter(m -> m.isAnnotationPresent(SqlDetector.class)).collect(Collectors.toMap(Method::getName, Function.identity(), (k1, k2) -> k1));}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Method cachedMethod = this.cache.get(method.getName());if (cachedMethod != null) {SqlDetector sqlDetector = cachedMethod.getAnnotation(SqlDetector.class);System.out.println("执行" + sqlDetector.type() + "语句:" + sqlDetector.sql());} else {//否则执行Object的toString\hashCode\equals方法// 但是因为这里是接口,没有toString,简单自定义下if (method.getName().equalsIgnoreCase("toString")) {return "@SqlDetector{" + method.getName() + "} " + this.mapper.getCanonicalName();}}return null;}
}

同时修改SqlMapperFactoryBean<T>:

package com.xiaoxu.test.impo.core;import org.springframework.beans.factory.FactoryBean;import java.lang.reflect.Proxy;/*** @author xiaoxu* @date 2024-01-22* java_demo:com.xiaoxu.test.impo.core.SqlMapperFactoryBean*/
public class SqlMapperFactoryBean<T> implements FactoryBean<T> {private Class<T> sqlMapperClazz;public SqlMapperFactoryBean() {}private SqlMapperFactoryBean(Class<T> sqlMapperClazz) {this.sqlMapperClazz = sqlMapperClazz;}@Overridepublic T getObject() throws Exception {return (T) Proxy.newProxyInstance(SqlMapperFactoryBean.class.getClassLoader(),new Class[]{this.sqlMapperClazz}, new SqlMapperProxy(this.sqlMapperClazz));}@Overridepublic Class<?> getObjectType() {return this.sqlMapperClazz;}@Overridepublic boolean isSingleton() {return FactoryBean.super.isSingleton();}
}

重新执行结果如下:

在这里插入图片描述

单测亦执行成功:

在这里插入图片描述

最后的思考

参考Mybatis框架的Mapper接口扫描逻辑可知,每个接口生成的FactoryBean的构造方法参数是Class,但是我们赋值时设置为全限定类名的String字符串,而Spring可以正常生成bean而没有报错。

同样场景,上述我的逻辑中,构造方法参数设置为类名字符串,实际定义的SqlMapperFactoryBean类中只有含有Class参数的构造方法(另一个是无参构造方法),为什么没有问题呢?因为Spring底层支持使用全限定类名转换成Class对象的功能,如此便可以匹配到对应的Constructor并生成bean。

参考如下org.springframework.beans.factory.support.ConstructorResolver类,用于处理无参Constructor以及含参Constructor,且通过Mybatis框架可得,注册BeanDefinition时,Mybatis框架添加的Constructor的参数为全限定类名的字符串String,依赖Spring框架可以自动将其转换成Class,即可以适配mybatis的public MapperFactoryBean(Class<T> mapperInterface)构造方法,如下可示:

private ArgumentsHolder createArgumentArray(String beanName, RootBeanDefinition mbd, @Nullable ConstructorArgumentValues resolvedValues,BeanWrapper bw, Class<?>[] paramTypes, @Nullable String[] paramNames, Executable executable,boolean autowiring, boolean fallback) throws UnsatisfiedDependencyException {

org.mybatis.spring.mapper.MapperFactoryBean如下:

public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {private Class<T> mapperInterface;private boolean addToConfig = true;public MapperFactoryBean() {}public MapperFactoryBean(Class<T> mapperInterface) {this.mapperInterface = mapperInterface;}

在这里插入图片描述

如下可见,originalValue为类的全限定名称String,但是构造方法的参数为Class,

在这里插入图片描述
在这里插入图片描述

判断是否支持转换,无法转换,则继续往下执行:

在这里插入图片描述

在这里插入图片描述

往下执行时,在org.springframework.beans.TypeConverterDelegate中,最终将全限定类名字符串,转换成为Class对象:

@Nullable
private Object doConvertValue(@Nullable Object oldValue, @Nullable Object newValue,@Nullable Class<?> requiredType, @Nullable PropertyEditor editor) {Object convertedValue = newValue;if (editor != null && !(convertedValue instanceof String)) {// Not a String -> use PropertyEditor's setValue.// With standard PropertyEditors, this will return the very same object;// we just want to allow special PropertyEditors to override setValue// for type conversion from non-String values to the required type.try {editor.setValue(convertedValue);Object newConvertedValue = editor.getValue();if (newConvertedValue != convertedValue) {convertedValue = newConvertedValue;// Reset PropertyEditor: It already did a proper conversion.// Don't use it again for a setAsText call.editor = null;}}catch (Exception ex) {if (logger.isDebugEnabled()) {logger.debug("PropertyEditor [" + editor.getClass().getName() + "] does not support setValue call", ex);}// Swallow and proceed.}}Object returnValue = convertedValue;if (requiredType != null && !requiredType.isArray() && convertedValue instanceof String[]) {// Convert String array to a comma-separated String.// Only applies if no PropertyEditor converted the String array before.// The CSV String will be passed into a PropertyEditor's setAsText method, if any.if (logger.isTraceEnabled()) {logger.trace("Converting String array to comma-delimited String [" + convertedValue + "]");}convertedValue = StringUtils.arrayToCommaDelimitedString((String[]) convertedValue);}if (convertedValue instanceof String) {if (editor != null) {// Use PropertyEditor's setAsText in case of a String value.if (logger.isTraceEnabled()) {logger.trace("Converting String to [" + requiredType + "] using property editor [" + editor + "]");}String newTextValue = (String) convertedValue;return doConvertTextValue(oldValue, newTextValue, editor);}else if (String.class == requiredType) {returnValue = convertedValue;}}return returnValue;
}

在这里插入图片描述

转换成功:

在这里插入图片描述

并返回Constructor方法的参数args:

在这里插入图片描述

通过ctor.newInstance(argsWithDefaultValues)方法,将该Bean实例初始化成功:

在这里插入图片描述

核心调用如下:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/718003.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

leetcode10正则表达式匹配

leetcode10正则表达式匹配 思路python 思路 难点1 如何理解特殊字符 ’ * ’ 的作用&#xff1f; 如何正确的利用特殊字符 ’ . ’ 和 ’ * ’ &#xff1f; * 匹配零个或多个前面的那一个元素 "a*" 可表示的字符为不同数目的 a&#xff0c;包括&#xff1a; "…

【大厂AI课学习笔记NO.65】机器学习框架和深度学习框架

笔记思维脑图已上传&#xff0c;访问我的主页可下载。 https://download.csdn.net/download/giszz/88868909 广义上&#xff0c;机器学习框架包含了深度学习框架。 本质上&#xff0c;机器学习框架涵盖分类、回归、聚类、异常检测和数据准备等各种学习方法。 深度学习框架涵…

SpringBoot启动扩展应用:干预优化+加快启动时间

一、SpringBoot启动配置原理简述 本内容直接查看分析SpringBoot启动配置原理&#xff0c;传送门&#xff1a; 二、SpringBoot启动过程干预 Spring Boot启动过程中我们可以实现以下干预工作&#xff1a; 修改Spring Boot默认的配置属性。使用ConfigurationProperties和Enable…

python celery beat实现定时任务

在Celery在python中的应用除了实现异步任务&#xff08;async task)外也可以执行定时任务(beat) 1.Celery定时任务是什么&#xff1f; Celery默认任务单元由任务生产者触发,但有时可能需要其自动触发, 而beat进程正是负责此类任务,能够自动触发定时/周期性任务. 只需要在配置…

吴恩达deeplearning.ai:学习曲线决定下一步怎么做

以下内容有任何不理解可以翻看我之前的博客哦&#xff1a;吴恩达deeplearning.ai专栏 学习曲线是一种图形表示方法&#xff0c;用于展示模型在训练过程中的学习表现&#xff0c;即模型的训练集和验证集上的性能如何随着训练时间的增加而变化。可以帮助我们了解模型的学习进度。…

制作耳机壳的UV树脂和塑料材质哪一个成本更高一些?

总体来说&#xff0c;制作耳机壳的UV树脂的成本可能会略高于塑料材质。 原材料成本&#xff1a;UV树脂通常是通过复杂的合成过程制成的。这些过程不仅需要大量的能源投入&#xff0c;还需要较高水平的技术和设备支持&#xff0c;因此原材料成本较高。相比之下&#xff0c;塑料…

04-prometheus服务的动态发现

一、概述 目前&#xff0c;我们每增加一个被监控的节点&#xff0c;就需要修改prometheus的配置文件&#xff0c;然后重新加载prometheus服务&#xff0c;这种方式比较繁琐&#xff0c;每次新增、删除被监控节点都需要重新操作一遍&#xff0c;不适合生产环境的大规模监控架构&…

Go-zero中分布式事务的实现(DTM分布式事务管理器,在一个APi中如何调用两个不同服务的rpc层,并保证两个不同服务之间的业务逻辑同时成功)

涉及到的相关技术 1.DTM分布式事务管理器,解决跨数据库、跨服务、跨语言栈更新数据的一致性问题。 2.SAGA事务模式,SAGA事务模式是DTM中常用的一种模式,简单易上手.(当然还有其它更多的事务模式,这里采用的SAGA只不过是其中一种较为简单的方法) 3.Go-zero框架,ETCD服务注册... …

Windows 2012 设置 nginx 开机自启动(适用于windows2012/10)

Windows 2012 设置 nginx 开机自启动&#xff08;适用于windows2012/10&#xff09;https://www.cnblogs.com/xuegqcto/articles/7521483.html 在windows server 2012上安装nginx&#xff0c;同时配置开机自启动服务&#xff08;推荐使用“Windows Service Wrapper”工具&…

【Linux】线程概念|线程理解|线程控制

文章目录 线程概念Linux中线程是否存在的讨论线程创建和线程控制线程的终止和等待&#xff08;三种终止方式 pthread_join()的void**retval&#xff09; 线程概念 线程就是进程内部的一个执行流&#xff0c;线程在进程内运行&#xff0c;线程在进程的地址空间内运行&#xff0…

LeetCode-第14题-最长公共前缀

1.题目描述 编写一个函数来查找字符串数组中的最长公共前缀。 如果不存在公共前缀&#xff0c;返回空字符串 ""。 2.样例描述 3.思路描述 按字符串数组每个数组的长度&#xff0c;将字符串数组从小到大排序&#xff1b;他们的公共前缀一定小于或等于最长元素长度…

2024年智能驾驶年度策略:自动驾驶开始由创造型行业转向工程型行业

感知模块技术路径已趋于收敛&#xff0c;自动驾驶从创造型行业迈向工程型行业。在特斯拉的引领下&#xff0c;国内主机厂2022年以来纷纷跟随特斯拉相继提出“重感知、轻地图”技术方案&#xff0c;全球自动驾驶行业感知模块技术路径从百花齐放开始走向收敛。我们认为主机厂智能…

2023.3.3周报

目录 摘要 一、文献阅读 1、题目 2、摘要 3、模型架构 4、文献解读 一、Introduction 二、实验 三、结论 二、PINN 一、PINN比传统数值方法有哪些优势 二、PINN方法 三、正问题与反问题 三、PINN实验 一、数学方程 二、模型搭建 总结 摘要 本周我阅读了一篇…

Postman上传文件的操作方法

前言 调用某个接口&#xff0c;测试上传文件功能。一时间不知如何上传文件&#xff0c;本文做个操作记录&#xff0c;期望与你有益。 步骤一、设置Headers key:Content-Type value:multipart/form-data 步骤二、设置Body 选择form-data key:file下拉框选择file类型value&…

STM32(8)NVIC编程

中断源由部分片上外设产生 在misc.h中找&#xff0c;杂项 配置NVIC GPIO和AFIO不能产生中断源&#xff0c;但能通过EXTI&#xff0c;由EXTI产生中断源 NVIC不需要开启时钟&#xff0c;因为NVIC模块位于内核内部&#xff0c;芯片一上电就能工作。 中断响应函数 中断向量表在启…

Java:JVM基础

文章目录 参考JVM内存区域程序计数器虚拟机栈本地方法栈堆方法区符号引用与直接引用运行时常量池字符串常量池直接内存 参考 JavaGuide JVM内存区域 程序计数器 程序计数器是一块较小的内存空间&#xff0c;可以看做是当前线程所执行的字节码的行号指示器&#xff0c;各线程…

Unity 常用的4种灯光、制作镜子、灯光的调用修改数值、

创建灯光时&#xff0c;一般用4种&#xff1a;定向光、点光源、聚光、区域光、 定向光&#xff1a;太阳 点光源&#xff1a;灯泡 聚光灯&#xff1a;手电筒 区域光&#xff1a;烘焙-贴图 灯光选择已烘焙 需要先选择被烘焙的物体&#xff0c;然后再选择Contribute GI 等待进…

java中的set

Set Set集合概述和特点 不可以存储重复元素 没有索引,不能使用普通for循环遍历 哈希值 哈希值简介 是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值 如何获取哈希值 Object类中的public int hashCode()&#xff1a;返回对象的哈希码值。 哈希值的特点 同一个…

分布式ID生成算法|雪花算法 Snowflake | Go实现

写在前面 在分布式领域中&#xff0c;不可避免的需要生成一个全局唯一ID。而在近几年的发展中有许多分布式ID生成算法&#xff0c;比较经典的就是 Twitter 的雪花算法(Snowflake Algorithm)。当然国内也有美团的基于snowflake改进的Leaf算法。那么今天我们就来介绍一下雪花算法…

计算机视觉基础知识(二)---数字图像

像素 像素是分辨率的单位;构成位图图像的最基本单元;每个像素都有自己的颜色; 图像分辨率 单位英寸内的像素点数;单位为PPI(Pixels Per Inch),为像素每英寸;PPI表示每英寸对角线上所拥有的像素数目:,x:长度像素数目,y:宽度像素数目,Z:屏幕大小;屏幕尺寸(大小)指的是对角线长…