Sping源码(九)—— Bean的初始化(非懒加载)— Bean的创建方式(factoryMethod)
序言
前面文章介绍了在Spring中多种创建Bean实例的方式,包括采用FactoryBean的方式创建对象、使用反射创建对象、自定义BeanFactoryPostProcessor。
这篇文章继续介绍Spring中创建Bean的形式之一——factoryMethod。方法用的不多,感兴趣可以当扩展了解。
doCreateBean
同样是doCreateBean
下的createBeanInstance()
。
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)throws BeanCreationException {// Instantiate the bean.//这个beanWrapper是用来持有创建出来的bean对象的BeanWrapper instanceWrapper = null;//如果是单例对象,从factoryBeanInstanceCache缓存中移除该信息if (mbd.isSingleton()) {// 如果是单例对象,从factoryBean实例缓存中移除当前bean定义信息instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);}// 没有就创建实例if (instanceWrapper == null) {// 根据执行bean使用对应的策略创建新的实例,如,工厂方法,构造函数主动注入、简单初始化instanceWrapper = createBeanInstance(beanName, mbd, args);}// 去除无用代码..... }
createBeanInstance
我们在上篇中介绍了通过Supplier
创建Bean的方式,而代码中Supplier判断的下面,就是我们本篇文章要介绍的通过factoryMethod
方式创建Bean。
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {// Make sure bean class is actually resolved at this point.// 锁定class,根据设置的class属性或者根据className来解析classClass<?> beanClass = resolveBeanClass(mbd, beanName);// 如果beanClass != null// 并且,访问修饰符不是public修饰, 抛异常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());}// 获取定义的SupplierSupplier<?> instanceSupplier = mbd.getInstanceSupplier();if (instanceSupplier != null) {return obtainFromSupplier(instanceSupplier, beanName);}if (mbd.getFactoryMethodName() != null) {return instantiateUsingFactoryMethod(beanName, mbd, args);}return instantiateBean(beanName, mbd);}
测试类
我们这里准备两个测试类,一个是静态工厂的方式,一个是普通的方式进行Bean实例的创建。
PersonStaticFactory
通过静态方法创建Person对象。
public class PersonStaticFactory {public static Person getPerson(String name){Person person = new Person();person.setName(name);person.setAge(22);return person;}
}
PersonInstanceFactory
普通方法创建Bean实例。
public class PersonInstanceFactory {public Person getPerson(String name){Person person = new Person();person.setAge(18);person.setName(name);return person;}
}
Person
平平无奇Person对象。
public class Person {private String name;private int age;// 省略 get set toString和构造函数
}
factoryMethod.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="personInstanceFactory" class="org.springframework.factoryMethod.PersonInstanceFactory"></bean><bean id="person" class="org.springframework.factoryMethod.PersonStaticFactory" factory-method="getPerson"><constructor-arg name="name" value="zhangsan"/></bean><bean id="person2" class="org.springframework.factoryMethod.Person"factory-bean="personInstanceFactory" factory-method="getPerson"><constructor-arg name="name" value="wangwu"></constructor-arg></bean>
</beans>
main
测试main方法,让我们看看在getBean()中都做了什么操作。
public static void main(String[] args) {ApplicationContext ac = new ClassPathXmlApplicationContext("factoryMethod.xml");Person person1 =(Person) ac.getBean("person");Person person2 =(Person)ac.getBean("person2");System.out.println(person1);System.out.println(person2);
}
instantiateUsingFactoryMethod()
方法入口。我们这里以 person2 的创建为例。
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {// Make sure bean class is actually resolved at this point.// 锁定class,根据设置的class属性或者根据className来解析classClass<?> beanClass = resolveBeanClass(mbd, beanName);// 如果beanClass != null// 并且,访问修饰符不是public修饰, 抛异常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());}// 获取定义的SupplierSupplier<?> instanceSupplier = mbd.getInstanceSupplier();if (instanceSupplier != null) {return obtainFromSupplier(instanceSupplier, beanName);}if (mbd.getFactoryMethodName() != null) {return instantiateUsingFactoryMethod(beanName, mbd, args);}return instantiateBean(beanName, mbd);}
让我们顺着Supplier
的判断顺着往下看。
此时来到了person
对象的加载,根据xml文件的配置,此时的beanName所指的person2是Person对象。
创建构造器解析器对象,调用instantiateUsingFactoryMethod()方法进行解析。
protected BeanWrapper instantiateUsingFactoryMethod(String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs) {return new ConstructorResolver(this).instantiateUsingFactoryMethod(beanName, mbd, explicitArgs);
}
instantiateUsingFactoryMethod
源码很长,我们这块分段来看。
代码中的第一部分主要是判断当前的BeanDefinition
是否包含factoryBeanName
,并以此来区分是否是static修饰,并设置标志位。
public BeanWrapper instantiateUsingFactoryMethod(String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs) {// 创建 BeanWrapperImpl 对象BeanWrapperImpl bw = new BeanWrapperImpl();//初始化beanWrapper对象,这里面获取了所有的customerEditor进行注册this.beanFactory.initBeanWrapper(bw);Object factoryBean;Class<?> factoryClass;boolean isStatic;//获取factoryBeanNameString factoryBeanName = mbd.getFactoryBeanName();//如果factoryBeanName不为nullif (factoryBeanName != null) {//如果配置的 factoryBeanName与beanName名称相同,则抛出异常if (factoryBeanName.equals(beanName)) {throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName,"factory-bean reference points back to the same bean definition");}//从工厂中获取当前factoryBeanName对应的bean对象factoryBean = this.beanFactory.getBean(factoryBeanName);//如果是单例模式,并且容器中已经存在该bean对象,则抛出异常if (mbd.isSingleton() && this.beanFactory.containsSingleton(beanName)) {//意味着此时工厂中已经包含了beanName对应的实例对象,再生成则重复了。,抛出异常throw new ImplicitlyAppearedSingletonException();}// 获取factoryBean的Class对象factoryClass = factoryBean.getClass();// 标志位设置为false 表示不是静态方法。isStatic = false;}else {// It's a static factory method on the bean class.// 这是bean类上的静态工厂方法// 如果mbd没有指定bean类if (!mbd.hasBeanClass()) {throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName,"bean definition declares neither a bean class nor a factory-bean reference");}factoryBean = null;factoryClass = mbd.getBeanClass();// 标志位设置为 true 表示为静态方法。isStatic = true;}}
我们这里的person2是普通工厂,并且会在代码this.beanFactory.getBean(factoryBeanName);
中通过factoryBeanName
来创建我们所配置的PersonInstanceFactory
对象。
声明变量并看调用getBean()
方法时是否传了显示的参数。我们这里调用getBean
时,什么都没传。所以expliciArgs为null。
//声明一个要使用的工厂方法,默认为nullMethod factoryMethodToUse = null;//声明一个用于存储不同形式的参数值的ArgumentsHolder,默认为nullArgumentsHolder argsHolderToUse = null;// 声明一个要使用的参数值数组,默认为nullObject[] argsToUse = null;// 如果调用getBean方法时,显示的传了参数,则此时explicitArgs != null// 则argsToUser引用explicitArgsif (explicitArgs != null) {argsToUse = explicitArgs;}else {//如果没有传// 声明一个要解析的args[] , 默认为null。Object[] argsToResolve = null;synchronized (mbd.constructorArgumentLock) {// factoryMethodToUse引用mbd中已经解析构造器或工厂方法对象factoryMethodToUse = (Method) mbd.resolvedConstructorOrFactoryMethod;//如果此时factoryMethodToUse不为null,并且mbd已解析构造函数参数(默认为false)if (factoryMethodToUse != null && mbd.constructorArgumentsResolved) {// Found a cached factory method...//找到了缓存工厂方法//argsToUse引用完全解析构的造器函数参数argsToUse = mbd.resolvedConstructorArguments;//如果依然为nullif (argsToUse == null) {//引用mbd准备好的构造函数参数值argsToResolve = mbd.preparedConstructorArguments;}}}//如果argsToResolve不为nullif (argsToResolve != null) {//解析mbd中缓存好的参数值argsToUse = resolvePreparedArguments(beanName, mbd, bw, factoryMethodToUse, argsToResolve, true);}}
获取所有候选方法并进行过滤。
// 如果没解析过,就获取factoryClass的用户定义类型,因为此时factoryClass可能是CGLIB动态代理类型,// 所以要获取用父类的类型。如果工厂方法是唯一的,就是没重载的,就获取解析的工厂方法,如果不为空,就添加到一个不可变列表里,// 如果为空的话,就要去找出factoryClass的以及父类的所有的方法,进一步找出方法修饰符一致且名字跟工厂方法名字相同的且是bean注解的方法,并放入列表里。if (factoryMethodToUse == null || argsToUse == null) {// Need to determine the factory method...// Try all methods with this name to see if they match the given arguments.// 获取工厂类的所有候选工厂方法factoryClass = ClassUtils.getUserClass(factoryClass);// 定义一个用于存储候选方法的集合List<Method> candidates = null;// 如果mbd所配置工厂方法时唯一if (mbd.isFactoryMethodUnique) {// 如果factoryMethodToUse为nullif (factoryMethodToUse == null) {// 获取mbd解析后的工厂方法对象factoryMethodToUse = mbd.getResolvedFactoryMethod();}// 如果factoryMethodToUse不为nullif (factoryMethodToUse != null) {// Collections.singletonList()返回的是不可变的集合,但是这个长度的集合只有1,可以减少内存空间。但是返回的值依然是Collections的内部实现类,// 同样没有add的方法,调用add,set方法会报错// 新建一个不可变,只能存一个对象的集合,将factoryMethodToUse添加进行,然后让candidateList引用该集合candidates = Collections.singletonList(factoryMethodToUse);}}// 如果candidateList为nullif (candidates == null) {// 让candidateList引用一个新的ArrayListcandidates = new ArrayList<>();// 根据mbd的是否允许访问非公共构造函数和方法标记【RootBeanDefinition.isNonPublicAccessAllowed】来获取factoryClass的所有候选方法Method[] rawCandidates = getCandidateMethods(factoryClass, mbd);// 遍历rawCandidates,元素名为candidatefor (Method candidate : rawCandidates) {// 如果candidate的修饰符与isStatic一致且candidate有资格作为mdb的工厂方法if (Modifier.isStatic(candidate.getModifiers()) == isStatic && mbd.isFactoryMethod(candidate)) {// 将candidate添加到candidateList中candidates.add(candidate);}}}// 候选方法只有一个且没有构造函数时,就直接使用该候选方法生成与beanName对应的Bean对象封装到bw中返回出去// 如果candidateList只有一个元素且没有传入构造函数值且mbd也没有构造函数参数值if (candidates.size() == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) {// 获取candidateList中唯一的方法Method uniqueCandidate = candidates.get(0);// 如果uniqueCandidate是不需要参数if (uniqueCandidate.getParameterCount() == 0) {// 让mbd缓存uniqueCandidate【{@link RootBeanDefinition#factoryMethodToIntrospect}】mbd.factoryMethodToIntrospect = uniqueCandidate;// 使用mdb的构造函数字段的通用锁【{@link RootBeanDefinition#constructorArgumentLock}】进行加锁以保证线程安全synchronized (mbd.constructorArgumentLock) {// 让mbd缓存已解析的构造函数或工厂方法【{@link RootBeanDefinition#resolvedConstructorOrFactoryMethod}】mbd.resolvedConstructorOrFactoryMethod = uniqueCandidate;// 让mbd标记构造函数参数已解析【{@link RootBeanDefinition#constructorArgumentsResolved}】mbd.constructorArgumentsResolved = true;// 让mbd缓存完全解析的构造函数参数【{@link RootBeanDefinition#resolvedConstructorArguments}】mbd.resolvedConstructorArguments = EMPTY_ARGS;}// 使用factoryBean生成的与beanName对应的Bean对象,并将该Bean对象保存到bw中bw.setBeanInstance(instantiate(beanName, mbd, factoryBean, uniqueCandidate, EMPTY_ARGS));// 将bw返回出去return bw;}}// 如果有多个工厂方法的话进行排序操作if (candidates.size() > 1) { // explicitly skip immutable singletonListcandidates.sort(AutowireUtils.EXECUTABLE_COMPARATOR);}
因为所有的对象的父类都是Object,所以会获取很多Object中的方法,经过过滤后,这里candidates只保留了getPerson()
过滤后,只剩getPerson()
方法。
根据candidate数量,如果只有一个且符合条件,则进行对象的创建,如果有多个则进行排序。
如果有构造函数,则获取构造函数的属性和值。
// 候选方法只有一个且没有构造函数时,就直接使用该候选方法生成与beanName对应的Bean对象封装到bw中返回出去// 如果candidateList只有一个元素且没有传入构造函数值且mbd也没有构造函数参数值if (candidates.size() == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) {// 获取candidateList中唯一的方法Method uniqueCandidate = candidates.get(0);// 如果uniqueCandidate是不需要参数if (uniqueCandidate.getParameterCount() == 0) {// 让mbd缓存uniqueCandidate【{@link RootBeanDefinition#factoryMethodToIntrospect}】mbd.factoryMethodToIntrospect = uniqueCandidate;// 使用mdb的构造函数字段的通用锁【{@link RootBeanDefinition#constructorArgumentLock}】进行加锁以保证线程安全synchronized (mbd.constructorArgumentLock) {// 让mbd缓存已解析的构造函数或工厂方法【{@link RootBeanDefinition#resolvedConstructorOrFactoryMethod}】mbd.resolvedConstructorOrFactoryMethod = uniqueCandidate;// 让mbd标记构造函数参数已解析【{@link RootBeanDefinition#constructorArgumentsResolved}】mbd.constructorArgumentsResolved = true;// 让mbd缓存完全解析的构造函数参数【{@link RootBeanDefinition#resolvedConstructorArguments}】mbd.resolvedConstructorArguments = EMPTY_ARGS;}// 使用factoryBean生成的与beanName对应的Bean对象,并将该Bean对象保存到bw中bw.setBeanInstance(instantiate(beanName, mbd, factoryBean, uniqueCandidate, EMPTY_ARGS));// 将bw返回出去return bw;}}// 如果有多个工厂方法的话进行排序操作if (candidates.size() > 1) { // explicitly skip immutable singletonListcandidates.sort(AutowireUtils.EXECUTABLE_COMPARATOR);}// ConstructorArgumentValues:构造函数参数值的Holder,通常作为BeanDefinition的一部分,支持构造函数参数列表中特定索引的值// 以及按类型的通用参数匹配// 定义一个用于存放解析后的构造函数参数值的ConstructorArgumentValues对象ConstructorArgumentValues resolvedValues = null;// 定义一个mbd是否支持使用构造函数进行自动注入的标记boolean autowiring = (mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR);// 定义一个最小类型差异权重,默认是Integer最大值int minTypeDiffWeight = Integer.MAX_VALUE;// 定义一个存储摸棱两可的工厂方法的Set集合,以用于抛出BeanCreationException时描述异常信息Set<Method> ambiguousFactoryMethods = null;// 定义一个最少参数数,默认为0int minNrOfArgs;// 如果explicitArgs不为nullif (explicitArgs != null) {// minNrOfArgs引用explicitArgs的数组长度minNrOfArgs = explicitArgs.length;}else {// We don't have arguments passed in programmatically, so we need to resolve the// arguments specified in the constructor arguments held in the bean definition.// 我们没有以编程方式传递参数,因此我们需要解析BeanDefinition中保存的构造函数参数中指定的参数// 如果mbd有构造函数参数值if (mbd.hasConstructorArgumentValues()) {// 获取mbd的构造函数参数值HolderConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();// 对resolvedValues实例化resolvedValues = new ConstructorArgumentValues();// 将cargs解析后值保存到resolveValues中,并让minNrOfArgs引用解析后的最小(索引参数值数+泛型参数值数)minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);}else {// 意味着mbd没有构造函数参数值时,将minNrOfArgs设为0minNrOfArgs = 0;}}
获取构造函数的属性和值。
获取参数列表并进行封装。
// 定义一个用于UnsatisfiedDependencyException的列表LinkedList<UnsatisfiedDependencyException> causes = null;// 遍历candidates,元素名为candidatefor (Method candidate : candidates) {// 获取参数的个数int parameterCount = candidate.getParameterCount();// 如果paramTypes的数组长度大于等于minNrOfArgsif (parameterCount >= minNrOfArgs) {// ArgumentsHolder:用于保存参数数组// 定义一个封装参数数组的ArgumentsHolder对象ArgumentsHolder argsHolder;// 获取candidate的参数类型数组Class<?>[] paramTypes = candidate.getParameterTypes();// 如果explicitArgs不为nullif (explicitArgs != null) {// Explicit arguments given -> arguments length must match exactly.// 给定的显示参数->参数长度必须完全匹配// 如果paramTypes的长度与explicitArgsd额长度不相等if (paramTypes.length != explicitArgs.length) {// 跳过当次循环中剩下的步骤,执行下一次循环。continue;}// 实例化argsHolder,封装explicitArgs到argsHolderargsHolder = new ArgumentsHolder(explicitArgs);}else {// Resolved constructor arguments: type conversion and/or autowiring necessary.// 已解析的构造函数参数:类型转换 and/or 自动注入时必须的try {// 定义用于保存参数名的数组String[] paramNames = null;// 获取beanFactory的参数名发现器ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();// 如果pnd不为nullif (pnd != null) {// 通过pnd解析candidate的参数名paramNames = pnd.getParameterNames(candidate);}// 将resolvedValues转换成一个封装着参数数组ArgumentsHolder实例,当candidate只有一个时,支持可在抛// 出没有此类BeanDefinition的异常返回null,而不抛出异常argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw,paramTypes, paramNames, candidate, autowiring, candidates.size() == 1);}// 捕捉UnsatisfiedDependencyExceptioncatch (UnsatisfiedDependencyException ex) {if (logger.isTraceEnabled()) {logger.trace("Ignoring factory method [" + candidate + "] of bean '" + beanName + "': " + ex);}// Swallow and try next overloaded factory method.// 吞下并尝试下一个重载的工厂方法// 如果cause为nullif (causes == null) {// 对cause进行实例化成LinkedList对象causes = new LinkedList<>();}// 将ex添加到causes中causes.add(ex);// 跳过本次循环体中余下尚未执行的语句,立即进行下一次的循环continue;}}
Spring会计算权重,选出符合条件的方法来进行实例对象的创建。
// mbd支持的构造函数解析模式,默认使用宽松模式:// 1. 严格模式如果摸棱两可的构造函数在转换参数时都匹配,则抛出异常// 2. 宽松模式将使用"最接近类型匹配"的构造函数// 如果bd支持的构造函数解析模式时宽松模式,引用获取类型差异权重值,否则引用获取Assignabliity权重值int typeDiffWeight = (mbd.isLenientConstructorResolution() ?argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));// Choose this factory method if it represents the closest match.// 如果它表示最接近的匹配项,则选择此工厂方法// 如果typeDiffWeight小于minTypeDiffWeightif (typeDiffWeight < minTypeDiffWeight) {// 让factoryMethodToUser引用candidatefactoryMethodToUse = candidate;// 让argsHolderToUse引用argsHolderargsHolderToUse = argsHolder;// 让argToUse引用argsHolder的经过转换后参数值数组argsToUse = argsHolder.arguments;// 让minTypeDiffWeight引用typeDiffWeightminTypeDiffWeight = typeDiffWeight;// 将ambiguousFactoryMethods置为nullambiguousFactoryMethods = null;}// Find out about ambiguity: In case of the same type difference weight// for methods with the same number of parameters, collect such candidates// and eventually raise an ambiguity exception.// However, only perform that check in non-lenient constructor resolution mode,// and explicitly ignore overridden methods (with the same parameter signature).// 找出歧义:如果具有相同数量参数的方法具有相同的类型差异权重,则收集此类候选想并最终引发歧义异常。// 但是,仅在非宽松构造函数解析模式下执行该检查,并显示忽略的方法(具有相同的参数签名)// 如果factoryMethodToUse不为null且typeDiffWeight与minTypeDiffWeight相等// 且mbd指定了严格模式解析构造函数且paramTypes的数组长度与factoryMethodToUse的参数数组长度相等且// paramTypes的数组元素与factoryMethodToUse的参数数组元素不相等else if (factoryMethodToUse != null && typeDiffWeight == minTypeDiffWeight &&!mbd.isLenientConstructorResolution() &¶mTypes.length == factoryMethodToUse.getParameterCount() &&!Arrays.equals(paramTypes, factoryMethodToUse.getParameterTypes())) {// 如果ambiguousFactoryMethods为nullif (ambiguousFactoryMethods == null) {// 初始化ambiguousFactoryMethods为LinkedHashSet实例ambiguousFactoryMethods = new LinkedHashSet<>();// 将factoryMethodToUse添加到ambiguousFactoryMethods中ambiguousFactoryMethods.add(factoryMethodToUse);}// 将candidate添加到ambiguousFactoryMethods中ambiguousFactoryMethods.add(candidate);}}}// 整合无法筛选出候选方法或者无法解析出要使用的参数值的情况,抛出BeanCreationException并加以描述// 如果factoryMethodToUse为null或者argsToUse为nullif (factoryMethodToUse == null || argsToUse == null) {// 如果causes不为nullif (causes != null) {// 从cause中移除最新的UnsatisfiedDependencyExceptionUnsatisfiedDependencyException ex = causes.removeLast();// 遍历causes,元素为causefor (Exception cause : causes) {// 将cause添加到该Bean工厂的抑制异常列表【{@link DefaultSingletonBeanRegistry#suppressedExceptions】中this.beanFactory.onSuppressedException(cause);}// 重新抛出exthrow ex;}// 定义一个用于存放参数类型的简单类名的ArrayList对象,长度为minNrOfArgsList<String> argTypes = new ArrayList<>(minNrOfArgs);// 如果explicitArgs不为nullif (explicitArgs != null) {// 遍历explicitArgs.元素为argfor (Object arg : explicitArgs) {// 如果arg不为null,将arg的简单类名添加到argTypes中;否则将"null"添加到argTypes中argTypes.add(arg != null ? arg.getClass().getSimpleName() : "null");}}// 如果resolvedValues不为nullelse if (resolvedValues != null) {// 定义一个用于存放resolvedValues的泛型参数值和方法参数值的LinkedHashSet对象Set<ValueHolder> valueHolders = new LinkedHashSet<>(resolvedValues.getArgumentCount());// 将resolvedValues的方法参数值添加到valueHolders中valueHolders.addAll(resolvedValues.getIndexedArgumentValues().values());// 将resolvedValues的泛型参数值添加到valueHolders中valueHolders.addAll(resolvedValues.getGenericArgumentValues());// 遍历valueHolders,元素为valuefor (ValueHolder value : valueHolders) {// 如果value的参数类型不为null,就获取该参数类型的简单类名;否则(如果value的参数值不为null,即获取该参数值的简单类名;否则为"null")String argType = (value.getType() != null ? ClassUtils.getShortName(value.getType()) :(value.getValue() != null ? value.getValue().getClass().getSimpleName() : "null"));// 将argType添加到argTypes中argTypes.add(argType);}}// 将argType转换成字符串,以","隔开元素.用于描述Bean创建异常String argDesc = StringUtils.collectionToCommaDelimitedString(argTypes);// 抛出BeanCreationException:找不到匹配的工厂方法:工厂Bean'mbd.getFactoryBeanName()';工厂方法// 'mbd.getFactoryMethodName()(argDesc)'.检查是否存在具体指定名称和参数的方法,并且该方法时静态/非静态的.throw new BeanCreationException(mbd.getResourceDescription(), beanName,"No matching factory method found: " +(mbd.getFactoryBeanName() != null ?"factory bean '" + mbd.getFactoryBeanName() + "'; " : "") +"factory method '" + mbd.getFactoryMethodName() + "(" + argDesc + ")'. " +"Check that a method with the specified name " +(minNrOfArgs > 0 ? "and arguments " : "") +"exists and that it is " +(isStatic ? "static" : "non-static") + ".");}// 如果factoryMethodToUse时无返回值方法else if (void.class == factoryMethodToUse.getReturnType()) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Invalid factory method '" + mbd.getFactoryMethodName() +"': needs to have a non-void return type!");}// 如果ambiguousFactoryMethods不为nullelse if (ambiguousFactoryMethods != null) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Ambiguous factory method matches found in bean '" + beanName + "' " +"(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities): " +ambiguousFactoryMethods);}// 将筛选出来的工厂方法和解析出来的参数值缓存到mdb中// 如果explicitArgs为null且argsHolderToUser不为nullif (explicitArgs == null && argsHolderToUse != null) {// 让mbd的唯一方法候选【{@link RootBeanDefinition#factoryMethodToIntrospect}】引用factoryMethodToUsembd.factoryMethodToIntrospect = factoryMethodToUse;// 将argsHolderToUse所得到的参数值属性缓存到mbd对应的属性中argsHolderToUse.storeCache(mbd, factoryMethodToUse);}}// 使用factoryBean生成与beanName对应的Bean对象,并将该Bean对象保存到bw中bw.setBeanInstance(instantiate(beanName, mbd, factoryBean, factoryMethodToUse, argsToUse));// 将bw返回出去return bw;
相关文章:
![](https://img-blog.csdnimg.cn/direct/8fe2a00b3de24464af1dd8d5c7840838.png)
Sping源码(九)—— Bean的初始化(非懒加载)— Bean的创建方式(factoryMethod)
序言 前面文章介绍了在Spring中多种创建Bean实例的方式,包括采用FactoryBean的方式创建对象、使用反射创建对象、自定义BeanFactoryPostProcessor。 这篇文章继续介绍Spring中创建Bean的形式之一——factoryMethod。方法用的不多,感兴趣可以当扩展了解。…...
![](https://www.ngui.cc/images/no-images.jpg)
绝对全网首发,利用Disruptor EventHandler实现在多线程下顺序执行任务
disruptor有两种任务处理器,一个是EventHandler ,另一个是WorkHandler. EventHandler可以彼此独立消费同一个队列中的任务,WorkHandler可以共同竞争消费同一个队列中的任务。也就是说,假设任务队列中有a、b、c、d三个事件,eventHa…...
![](https://www.ngui.cc/images/no-images.jpg)
单例设计模式双重检查的作用
先看双重校验锁的写法 public class Singleton {/*volatile 修饰,singleton new Singleton() 可以拆解为3步:1、分配对象内存(给singleton分配内存)2、调用构造器方法,执行初始化(调用 Singleton 的构造函数来初始化成员变量&am…...
![](https://img-blog.csdnimg.cn/img_convert/5eec07cce999b236023a15c0ebc124a9.png)
NGINX_十二 nginx 地址重写 rewrite
十二 nginx 地址重写 rewrite 1 什么是Rewrite Rewrite对称URL Rewrite,即URL重写,就是把传入Web的请求重定向到其他URL的过程。URL Rewrite最常见的应用是URL伪静态化,是将动态页面显示为静态页面方式的一种技术。比如 http://www.123.com…...
![](https://img-blog.csdnimg.cn/direct/62d17b42f87b4b0c93a351d6d70bd46b.png)
react用ECharts实现组织架构图
找到ECharts中路径图。 然后开始爆改。 <div id{org- name} style{{ width: 100%, height: 650, display: flex, justifyContent: center }}></div> // data的数据格式 interface ChartData {name: string;value: number;children: ChartData[]; } const treeDep…...
![](https://img-blog.csdnimg.cn/direct/12337d5e7cad430f817476c1548e44ab.png)
坚持刷题|合并有序链表
文章目录 题目思考代码实现迭代递归 扩展实现k个有序链表合并方法一方法二 PriorityQueue基本操作Java示例注意事项 Hello,大家好,我是阿月。坚持刷题,老年痴呆追不上我,消失了一段时间,我又回来刷题啦,今天…...
![](https://img-blog.csdnimg.cn/direct/de893b40d7ae4686a6225f361c7c90fb.png)
SPI协议——对外部SPI Flash操作
目录 1. W25Q32JVSSIQ背景知识 1.1 64个可擦除块 1.2 1024个扇区(每个块有16个扇区) 1.3 页 1. W25Q32JVSSIQ背景知识 W25Q32JV阵列被组织成16,384个可编程页,每页有256字节。一次最多可以编程256个字节。页面可分为16组(4KB扇区清除&…...
![](https://img-blog.csdnimg.cn/direct/57eda91db89a4e1dad6aec7947cb7292.png)
kotlin类型检测与类型转换
一、is与!is操作符 1、使用 is 操作符或其否定形式 !is 在运行时检测对象是否符合给定类型。 fun main() {var a "1"if(a is String) {println("a是字符串类型:${a.length}")}// 或val b a is Stringprintln(b) } 二、"不安全的"转换操作符…...
![](https://www.ngui.cc/images/no-images.jpg)
【JDBC】Oracle数据库连接问题记录
Failed to load driver class oracle.jdbc.driver.OracleDriver in either of HikariConfig class oracle驱动包未正确加载,可以先尝试使用下面方式加载检查类是否存在,如果不存在需要手动下载odbc包 try {Class.forName("oracle.jdbc.driver.Ora…...
![](https://www.ngui.cc/images/no-images.jpg)
leetcode45 跳跃游戏II
题目 给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。 每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说,如果你在 nums[i] 处,你可以跳转到任意 nums[i j] 处: 0 < j < nums[i] i j < n 返回到达 nums[n - 1]…...
![](https://www.ngui.cc/images/no-images.jpg)
【数学】什么是方法矩估计?和最大似然估计是什么关系?
背景 方法矩估计(Method of Moments Estimation)和最大似然估计(Maximum Likelihood Estimation, MLE)是两种常用的参数估计方法。方法矩估计基于样本矩与总体矩的关系,通过样本数据计算样本矩来估计总体参数。最大似…...
![](https://img-blog.csdnimg.cn/direct/75f0aa9650494a42b0ba4557dc826cde.png#pic_center)
C++初学者指南第一步---10.内存(基础)
C初学者指南第一步—10.内存(基础) 文章目录 C初学者指南第一步---10.内存(基础)1.内存模型1.1 纸上谈兵:C的抽象内存模型1.2 实践:内存的实际处理 2. 自动存储3.动态存储:std::vector3.1 动态内…...
![](https://www.ngui.cc/images/no-images.jpg)
扩散模型详细推导过程——编码与解码
符号表 符号含义 x ( i ) z 0 ( i ) \boldsymbol{x}^{(i)}\boldsymbol{z}_0^{(i)} x(i)z0(i)第 i i i个训练数据,其为长度为 d d d的向量 z t ( i ) \boldsymbol{z}_t^{(i)} zt(i)第 i i i个训练数据在第 t t t时刻的加噪版本 ϵ t ( i ) \boldsymbol{\epsilo…...
![](https://www.ngui.cc/images/no-images.jpg)
js如何实现开屏弹窗
开屏弹窗是什么,其实就是第一次登录后进入页面给你的一种公告提示,此后再回到当前这个页面时弹窗是不会再出现的。也就是说这个弹窗只会出现一次。 <!DOCTYPE html> <html><head><meta charset"utf-8"><title>…...
![](https://www.ngui.cc/images/no-images.jpg)
C#——文件读取Directory类详情
文件读取Directory类 Durectory提供了目录以及子目录进行创建移动和列举操作方法 Directory和Directorylnfo类(主要操作文件目录属性列如文件是否隐藏的 或者只读等这些属性) Directory对目录进行复制、移动、重命名、创建和删除等操作DirectoryInfo用于对目录属性执行操作 …...
![](https://img-blog.csdnimg.cn/direct/a52743d573f745e4befd5744316879ce.png)
Ruby on Rails Post项目设置网站初始界面
在构建了Ruby的Web服务器后,第三步就可以去掉框架的官方页面,设置自己的网页初始页了。 Linux系统安装Ruby语言-CSDN博客 、在Ubuntu中创建Ruby on Rails项目并搭建数据库-CSDN博客、 Ruby语言建立Web服务器-CSDN博客 了解Ruby onRails项目中的主要文件…...
![](https://img-blog.csdnimg.cn/direct/8d85482617ed49518789a2da231ce984.png)
03-QTWebEngine中使用qtvirtualkeyboard
qt提供了 virtualKeyboard 虚拟键盘模块,只需要在在main函数中最开始加入这样一句就可以了 qputenv("QT_IM_MODULE", QByteArray("qtvirtualkeyboard")); 但是在使用的时候遇到了一些问题: 1、中文输入的时候没有输入提示 Qvirt…...
![](https://www.ngui.cc/images/no-images.jpg)
leetcode3无重复字符的最长字串(重点讲滑动窗口)
本文主要讲解无重复字符的最长字串的要点与细节,根据步骤一步步走更方便理解 c与java代码如下,末尾 具体要点: 1. 区分一下子串和子序列 子串:要求元素在母串中是连续地出现 子序列:不要求连续 2. 题目中有两个核心…...
![](https://img-blog.csdnimg.cn/direct/aa0b52391be54afdbc5b39f989f334d9.png)
Gobject tutorial 八
The GObject base class Object memory management Gobject的内存管理相关的API很复杂,但其目标是提供一个基于引用计数的灵活的内存管理模式。 下面我们来介绍一下,与管理引用计数相关的函数。 Reference Count 函数g_object_ref和g_object_unref的…...
![](https://img-blog.csdnimg.cn/img_convert/77a5bbfb274858f9c9e39e71406e104f.png#pic_center)
DDMA信号处理以及数据处理的流程---cfar检测
Hello,大家好,我是Xiaojie,好久不见,欢迎大家能够和Xiaojie一起学习毫米波雷达知识,Xiaojie准备连载一个系列的文章—DDMA信号处理以及数据处理的流程,本系列文章将从目标生成、信号仿真、测距、测速、cfar检测、测角、目标聚类、目标跟踪这几个模块逐步介绍,这个系列的…...
【机器学习】从理论到实践:决策树算法在机器学习中的应用与实现
📝个人主页:哈__ 期待您的关注 目录 📕引言 ⛓决策树的基本原理 1. 决策树的结构 2. 信息增益 熵的计算公式 信息增益的计算公式 3. 基尼指数 4. 决策树的构建 🤖决策树的代码实现 1. 数据准备 2. 决策树模型训练 3.…...
![](https://www.ngui.cc/images/no-images.jpg)
Zookeeper 集群节点故障剔除、切换、恢复原理
Zookeeper 集群节点故障剔除、切换、恢复原理 zookeeper 集群节点故障时,如何剔除节点,如果为领导节点如何处理,如何进行故障恢 复的,实现原理? 在 Zookeeper 集群中,当节点故障时,集群需要自动剔除故障节点并进行故障恢复,确保集群的高 可用性和一致性。具体来说,…...
![](https://www.ngui.cc/images/no-images.jpg)
解决帝国cms栏目管理拼音乱码的问题
帝国CMS7.5版本utf-8版网站后台增加栏目生成乱码的问题怎么解决 1、需要改一个函数,并且增加一个处理文件,方法如下: 修改e/class/connect.php文件,找到ReturnPinyinFun函数,如未修改文件在4533-4547行,将…...
![](https://img-blog.csdnimg.cn/direct/c8edbd5596224407bcbf4d35d2f85846.png)
Git快速入门
一 快速使用 1.1 初始化 什么是版本库呢?版本库又名仓库,可以简单理解成一个目录,这个目录里面的所有文件都可以被Git管理起来,每个文件的修改、删除,Git都能跟踪,以便任何时刻都可以追踪历史࿰…...
![](https://www.ngui.cc/images/no-images.jpg)
【18.0】JavaScript---事件案例
【18.0】JavaScript—事件案例 【一】开关灯事件 【介绍】设置一个按钮,按下按钮触发事件,来回切换圆形图片的颜色 【分析】 图片设置:设置成圆形的图片背景颜色:设置红绿两个颜色,来回切换按钮设置:点击…...
![](https://img-blog.csdnimg.cn/direct/ff8640497e7d40b690caa19981a6c05e.png)
推荐系统三十六式学习笔记:原理篇.矩阵分解12|如果关注排序效果,那么这个模型可以帮到你
目录 矩阵分解的不足贝叶斯个性化排序AUC构造样本目标函数训练方法 总结 矩阵分解在推荐系统中的地位非常崇高。它既有协同过滤的血统,又有机器学习的基因,可以说是非常优秀了;但即便如此,传统的矩阵分解无论是在处理显式反馈&…...
![](https://www.ngui.cc/images/no-images.jpg)
Kafka之ISR机制的理解
文章目录 Kafka的基本概念什么是ISRISR的维护机制ISR的作用ISR相关配置参数同步过程示例代码总结 Kafka中的ISR(In-Sync Replicas同步副本)机制是确保数据高可用性和一致性的核心组件。 Kafka的基本概念 在Kafka中,数据被组织成主题…...
![](https://img-blog.csdnimg.cn/direct/25152a61a65a490c97aa516750ac97c8.png)
如何设计一个点赞系统
首先我们定义出一个点赞系统需要对外提供哪些接口: 1.用户对特定的消息进行点赞; 2.用户查看自己发布的某条消息点赞数量以及被哪些人赞过; 3.用户查看自己给哪些消息点赞过; 这里假设每条消息都有一个message_id, 每一个用户都…...
![](https://www.ngui.cc/images/no-images.jpg)
对象存储测试工具-s3cmd
一、环境安装 官网:https://s3tools.org/s3cmd 下载安装包:https://s3tools.org/download GitHub:https://github.com/s3tools/s3cmd/releases 本文安装包:https://github.com/s3tools/s3cmd/releases/download/v2.0.2/s3cmd-2.0…...
![](https://www.ngui.cc/images/no-images.jpg)
OpenCV--图像色彩空间及转换
图像色彩空间及转换 python代码和笔记 python代码和笔记 import cv2 色彩空间,基础:RGB或BGR OpenCV中: 一、HSV(HSB):用的最多, Hue:色相-色彩(0-360),红色:0,绿色&…...
![](http://images.macx.cn/forum/201109/14/232817hubyzuk5vel8eesk.gif)
深圳网站快速备案/百度网站
From: http://www.macx.cn/thread-2018736-1-1.html 有些童鞋们 使用 MBP或 MBA 连接到外部显示器时,都可能想要禁用其内部屏幕。 这一般能透过两种方法,但因为 Mac OS X Lion 10.7 上的内部屏幕更稳定,或许会希望继续使用。 要禁用OS X Lio…...
![](/images/no-images.jpg)
麒麟区住房和城乡建设局网站/站长工具是什么意思
两个线程并发执行以下代码,假设a是全局变量,初始为1,那么以下输出______是可能的?1234void foo(){aa1;printf("%d ",a);}3 2 2 3 3 3 2 2 正确答案: A B C D 你的答案: B C D (错误) 编译出来(一般来讲&am…...
![](/images/no-images.jpg)
广东江门市新会区疫情最新消息/重庆seo务
题目传送门 题目大意: 给一个长度为 LLL 的圆环,有两只青蛙,给出两者的起点和速度,问何时相遇。 题解 显然是个追击问题,先列出柿子: xtn≡ytm(modL)xtn\equiv ytm \pmod L xtn≡ytm(modL) 其中 ttt 即…...
![](/images/no-images.jpg)
小皮搭建本地网站/seo搜索优化 指数
vue.js react是仅仅几年前,我们通过小心谨慎地将每个标签可爱地放置在文件中来构建我们的网站吗? 手工制作网站的时代已经过去。 大多数现代网站都是精心制作的程序,它们会不断地对多个数据源执行ping操作,然后搅乱嵌套在其他标签…...
![](https://images2015.cnblogs.com/blog/727148/201611/727148-20161101112154596-1914544370.png)
政府网站域名注册/镇江百度关键词优化
左百度,添加到同右钉邮那么多: 1、\ueditor\lang\zh-cn\zh-cn.js 文件中找到:fontfamily节点,替换如下: fontfamily:{songti:宋体,heiti:黑体,kaiti:楷体,lishu:隶书,yahei:微软雅黑,arial:arial,arialBlack:arial bl…...
![](http://i2.51cto.com/images/blog/201809/20/3c55a189c80dfea90688580376849559.jpg?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=)
青岛大学春季高考有网站建设吗/2345导航网址
支持正则表达式...