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实例初始化成功:
核心调用如下: