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 {

	@Override
	public 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 attribute
	ResolvableType 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 class
	if (mbd.getInstanceSupplier() != null) {
		result = getFactoryBeanGeneric(mbd.targetType);
		if (result.resolve() != null) {
			return result;
		}
		result = getFactoryBeanGeneric(beanType);
		if (result.resolve() != null) {
			return result;
		}
	}

	// Consider factory methods
	String factoryBeanName = mbd.getFactoryBeanName();
	String factoryMethodName = mbd.getFactoryMethodName();

	// Scan the factory bean methods
	if (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() early
	if (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 registered
		return 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 against
	Class<?> 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 type
	Class<?> 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 considered
	if (beanType != null) {
		return typeToMatch.isAssignableFrom(beanType);
	}

	// If we don't have a bean type, fallback to the predicted type
	return 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> {
    @Override
    public SelectorProxy getObject() throws Exception {
        SelectorProxy proxy = new SelectorProxy();
        System.out.println("获取FactoryBean SelectorProxy.");
        return proxy;
    }

    @Override
    public Class<?> getObjectType() {
        return SelectorProxy.class;
    }

    @Override
    public 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() {
        }

        @Override
        public 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);
    }

    @Override
    protected 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() {
                @Override
                public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
                    return true;
                }
            });
        }

        this.addExcludeFilter(((metadataReader, metadataReaderFactory) -> {
            String className = metadataReader.getClassMetadata().getClassName();
            return className.endsWith("package-info");
        }));
    }

    @Override
    protected 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()));
        }
    }

    @Override
    public 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);
    }

    @Override
    public 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);
    }

    @Override
    public 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() {
            @Override
            public 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;
        }

        @Override
        public 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);
        }

        @Override
        public 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() {
            @Override
            public boolean evaluate(String name) {
                return false;
            }
        };
    }

    @Override
    public 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;
    }

    @Override
    public T getObject() throws Exception {
        return null;
    }

    @Override
    public Class<?> getObjectType() {
        return null;
    }

    @Override
    public 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$";

    @Override
    public 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;

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        this.classLoader = classLoader;
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

    @Override
    public 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;

    @Override
    public 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;
    }

    @Override
    public Class<? extends Group> getImportGroup() {
        return XImportSelector.BundleGroup.class;
    }

    private static class BundleGroup implements DeferredImportSelector.Group {

        private AnnotationMetadata metadata;
        private AnnotationAttributes attributes;

        @Override
        public 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)
//            填充参数等等
        }


        @Override
        public 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));
    }

    @Override
    public 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;
    }

    @Override
    public T getObject() throws Exception {
        return (T) Proxy.newProxyInstance(SqlMapperFactoryBean.class.getClassLoader(),
                new Class[]{this.sqlMapperClazz}, new SqlMapperProxy(this.sqlMapperClazz));
    }

    @Override
    public Class<?> getObjectType() {
        return this.sqlMapperClazz;
    }

    @Override
    public 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实例初始化成功:

在这里插入图片描述

核心调用如下:

在这里插入图片描述

  • 19
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: ``` @Configuration @AutoConfigureAfter(MyBatisConfig.class) public class MyBatisMapperScannerConfig { @Bean public MapperScannerConfigurer mapperScannerConfigurer() { MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer(); mapperScannerConfigurer.setSqlSessionFactoryBeanName("sqlSessionFactory"); mapperScannerConfigurer.setBasePackage("com.example.mapper"); return mapperScannerConfigurer; } } ``` 这是一个 Spring Boot Starter 中使用 MyBatis 扫描 mapper 文件的示例代码。 首先在配置类 MyBatisMapperScannerConfig 中声明了一个名为 mapperScannerConfigurer 的 bean。 然后使用 setSqlSessionFactoryBeanName 方法指定 MyBatis 的 SqlSessionFactory bean 的名称。 最后使用 setBasePackage 方法指定 mapper 文件所在的包。 其中 MyBatisConfig.class 为Mybatis配置类,需要配置数据源和SqlSessionFactory,具体可以参考Mybatis官方文档 ``` @Configuration @MapperScan("com.example.mapper") public class MyBatisConfig { @Autowired private DataSource dataSource; @Bean public SqlSessionFactory sqlSessionFactory() throws Exception { SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(dataSource); return sqlSessionFactoryBean.getObject(); } } ``` 另外还可以使用 @MapperScan 注解来简化配置。 ``` 这种方式可以省略 MyBatisMapperScannerConfig 配置类,在MybatisConfig.class中配置即可 ### 回答2: 自定义Spring Boot Starter是为了方便在Spring Boot项目中集成MyBatis数据库操作。以下是一个代码示例: 首先,在你的自定义Starter项目中创建一个自动配置类,例如`MyBatisAutoConfiguration`。在该类中,你可以使用Spring Boot的自动配置特性来配置MyBatis的相关属性,例如数据库连接信息、Mapper扫描等。同时,你也可以定义一些默认的配置项,方便用户进行使用。 ```java @Configuration @EnableConfigurationProperties(MyBatisProperties.class) @ConditionalOnClass(SqlSessionFactory.class) @AutoConfigureAfter(DataSourceAutoConfiguration.class) public class MyBatisAutoConfiguration { @Autowired private MyBatisProperties properties; @Autowired private ResourceLoader resourceLoader; @Bean @ConditionalOnMissingBean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { SqlSessionFactoryBean factory = new SqlSessionFactoryBean(); factory.setDataSource(dataSource); factory.setMapperLocations(this.properties.resolveMapperLocations()); return factory.getObject(); } @Bean @ConditionalOnMissingBean public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) { return new SqlSessionTemplate(sqlSessionFactory); } @Bean @ConditionalOnMissingBean public MapperScannerConfigurer mapperScannerConfigurer() { MapperScannerConfigurer scanner = new MapperScannerConfigurer(); scanner.setSqlSessionFactoryBeanName("sqlSessionFactory"); scanner.setBasePackage("your.mapper.package"); return scanner; } } ``` 然后,你需要定义一个配置类`MyBatisProperties`来接收用户的自定义配置,例如数据库连接信息、Mapper扫描路径等。用户在自己的Spring Boot项目中,可以通过配置文件来配置这些属性。 ```java @ConfigurationProperties(prefix = "mybatis") public class MyBatisProperties { private String[] mapperLocations; // getters and setters public Resource[] resolveMapperLocations() { // 根据 mapperLocations 配置的路径解析为 Resource 数组 return Arrays.stream(this.mapperLocations) .map(location -> this.resourceLoader.getResource(location)) .toArray(Resource[]::new); } } ``` 最后,在你的自定义Starter项目中创建一个`spring.factories`文件,并在该文件中添加自定义Starter的自动配置类。 ``` org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.example.MyBatisAutoConfiguration ``` 用户在自己的Spring Boot项目中,只需要引入你的自定义Starter依赖,然后在配置文件中配置相关属性,即可使用MyBatis进行数据库操作。 以上就是一个自定义Spring Boot Starter操作MyBatis数据库的代码示例。希望对你有帮助! ### 回答3: 自定义 Spring Boot Starter 可以简化在项目中使用 MyBatis 操作数据库的配置和依赖管理。下面是一个简单的代码示例,演示如何自定义 Spring Boot Starter 并使用它来操作 MyBatis 数据库。 首先,创建一个 Maven 项目,并添加以下依赖项: ```xml <dependencies> <!-- Spring Boot Starter --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <!-- MyBatis Starter --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>${mybatis.version}</version> </dependency> <!-- 添加数据库驱动,这里以 MySQL 为例 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> </dependencies> ``` 然后,创建一个 Spring Boot Starter 模块,在该模块中进行自定义配置和依赖管理。创建一个类,例如 `MyBatisStarterAutoConfiguration`,在该类中配置数据源和 MyBatis 相关的 bean: ```java @Configuration @ConditionalOnClass({ DataSourceProperties.class, SqlSessionFactory.class }) @EnableConfigurationProperties(MyBatisStarterProperties.class) public class MyBatisStarterAutoConfiguration { private final MyBatisStarterProperties properties; public MyBatisStarterAutoConfiguration(MyBatisStarterProperties properties) { this.properties = properties; } @Bean @ConfigurationProperties(prefix = "spring.datasource") public DataSource dataSource() { return DataSourceBuilder.create().build(); } @Bean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean(); factoryBean.setDataSource(dataSource); return factoryBean.getObject(); } } ``` 接下来,创建一个 properties 类,用于配置 MyBatis 配置文件的路径,默认为 `classpath:mybatis-config.xml`: ```java @ConfigurationProperties(prefix = "mybatis") public class MyBatisStarterProperties { private String configLocation = "classpath:mybatis-config.xml"; public String getConfigLocation() { return configLocation; } public void setConfigLocation(String configLocation) { this.configLocation = configLocation; } } ``` 在资源文件目录中,配置 MyBatis 的配置文件 `mybatis-config.xml`: ```xml <configuration> <!-- 根据需要配置 MyBatis 的其他相关配置 --> </configuration> ``` 最后,在项目的入口类中引入自定义的 Starter: ```java @SpringBootApplication @Import({ MyBatisStarterAutoConfiguration.class }) public class MyApplication { public static void main(String[] args) { SpringApplication.run(MyApplication.class, args); } } ``` 以上就是一个简单的示例,通过自定义 Spring Boot Starter 来操作 MyBatis 数据库。使用自定义的 Starter,我们可以将数据库和 MyBatis 的配置集中在一个模块中,使得项目的配置更加清晰和简洁。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值