2023-09-16  阅读(103)
原文作者:王伟王胖胖 原文地址: https://blog.csdn.net/wangwei19871103/article/details/105081731

只有一个无参工厂方法

如果只有一个构造函数的话,看是否有参数,没有的话直接就设置bean定义的属性,然后实例化设置进包装对象。

    //如果只获取到一个方法,且传入的参数为空,且没有设置构造方法参数值
    			if (candidateList.size() == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) {
    				Method uniqueCandidate = candidateList.get(0);//获取出来
    				if (uniqueCandidate.getParameterCount() == 0) {//没参数的话
    					mbd.factoryMethodToIntrospect = uniqueCandidate;//设置工厂方法
    					synchronized (mbd.constructorArgumentLock) {
    						mbd.resolvedConstructorOrFactoryMethod = uniqueCandidate;//设置解析出来的方法
    						mbd.constructorArgumentsResolved = true;//参数也已经解析了
    						mbd.resolvedConstructorArguments = EMPTY_ARGS;//方法参数为空
    					}
    					bw.setBeanInstance(instantiate(beanName, mbd, factoryBean, uniqueCandidate, EMPTY_ARGS));//创建实例并设置到持有器里
    					return bw;
    				}
    			}

instantiate工厂方法实例化

这个就是最简单的无参工厂方法实例化。

    private Object instantiate(String beanName, RootBeanDefinition mbd,
    			@Nullable Object factoryBean, Method factoryMethod, Object[] args) {
    
    		try {
    			if (System.getSecurityManager() != null) {
    				return AccessController.doPrivileged((PrivilegedAction<Object>) () ->
    						this.beanFactory.getInstantiationStrategy().instantiate(
    								mbd, beanName, this.beanFactory, factoryBean, factoryMethod, args),
    						this.beanFactory.getAccessControlContext());
    			}
    			else {//获取实例化策略进行实例化
    				return this.beanFactory.getInstantiationStrategy().instantiate(
    						mbd, beanName, this.beanFactory, factoryBean, factoryMethod, args);
    			}
    		}
    		catch (Throwable ex) {
    			throw new BeanCreationException(mbd.getResourceDescription(), beanName,
    					"Bean instantiation via factory method failed", ex);
    		}
    	}

SimpleInstantiationStrategy的instantiate

其实默认的实例化策略是CglibSubclassingInstantiationStrategy,就是可以用CGLIB做动态代理,但是仅限于方法注入的形式,所以这里是无参工厂方法还是调用父类SimpleInstantiationStrategy的实现。其实就是调用工厂实例的工厂方法,传入参数,只是参数是个空数组EMPTY_ARGS,返回对象。

    @Override
    	public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
    			@Nullable Object factoryBean, final Method factoryMethod, Object... args) {
    
    		try {
    			if (System.getSecurityManager() != null) {
    				AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
    					ReflectionUtils.makeAccessible(factoryMethod);
    					return null;
    				});
    			}
    			else {//设置factoryMethod可访问
    				ReflectionUtils.makeAccessible(factoryMethod);
    			}
    			//获取前面存在的线程本地的FactoryMethod
    			Method priorInvokedFactoryMethod = currentlyInvokedFactoryMethod.get();
    			try {
    				currentlyInvokedFactoryMethod.set(factoryMethod);//设置新的
    				Object result = factoryMethod.invoke(factoryBean, args);//调用工厂方法
    				if (result == null) {
    					result = new NullBean();
    				}
    				return result;
    			}
    			finally {
    				if (priorInvokedFactoryMethod != null) {//如果线程本地存在,就设置回老的
    					currentlyInvokedFactoryMethod.set(priorInvokedFactoryMethod);
    				}
    				else {//否则就删除,等于没设置
    					currentlyInvokedFactoryMethod.remove();
    				}
    			}
    		}
    		...
    	}

有多个有参工厂方法(重点)

接下去又是很经典的地方了,怎么处理有多个参数的工厂方法呢,到底哪一个才是应该调用呢。

分段1-排序

首先会根据一定的规则进行工厂方法的排序,规则就是 先按修饰符public优先排,然后同修饰符的按参数多的排

    Method[] candidates = candidateList.toArray(new Method[0]);
    AutowireUtils.sortFactoryMethods(candidates);//排序,根据public优先,参数多的优先

比如我有6个同名的,其实也就是重载的,有不同修饰符的,有不同参数个数的,没排序前:

202309162312304601.png
排序后:

202309162312308622.png

AutowireUtils的sortFactoryMethods排序

    	public static void sortFactoryMethods(Method[] factoryMethods) {
    		Arrays.sort(factoryMethods, EXECUTABLE_COMPARATOR);
    	}

EXECUTABLE_COMPARATOR比较器

    	//先比较修饰符,再比较参数个数 从Public到非Public,参数从多到少
    	private static final Comparator<Executable> EXECUTABLE_COMPARATOR = (e1, e2) -> {
    		int result = Boolean.compare(Modifier.isPublic(e2.getModifiers()), Modifier.isPublic(e1.getModifiers()));
    		return result != 0 ? result : Integer.compare(e2.getParameterCount(), e1.getParameterCount());
    	};

分段2-准备工作

因为接下去解析的工厂方法是有参数的,所以要进行一些处理,比如获取构造器参数值(如果有传的话),然后获取自动装配模式,工厂方法一般默认是AUTOWIRE_CONSTRUCTOR,还会定义一个minTypeDiffWeight 差异值,这个就是用在如果有多个工厂方法的时候,看工厂方法的参数和具体装配的bean的类型的差异,取最小的。还定义了有个ambiguousFactoryMethods ,用来存放差异值一样的方法,说明是一样的类型,无法判断要用哪个工厂方法实例化。比如protected UserDao userDao(TestBean2 t2)public UserDao userDao(TestBean t1)两个构造方法类型差异是一样的,所以不知道要用哪个,就会报异常啦。还有minNrOfArgs也很关键,用来做优化的,如果有最小参数个数,那么一些参数个数少于最小个数的就不需要去判断了,直接跳过,否则就算获取到了也不匹配,反而浪费资源了。如果minNrOfArgs0,那就说明没有参数个数限制,那后面就需要一个个去判断哪个差异最小,最符合了。

    			ConstructorArgumentValues resolvedValues = null;//构造器参数值
    			boolean autowiring = (mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR);
    			int minTypeDiffWeight = Integer.MAX_VALUE;//最小的类型差距
    			Set<Method> ambiguousFactoryMethods = null;//模棱两可的工厂方法集合
    
    			int minNrOfArgs;//最小参数个数
    			if (explicitArgs != null) {
    				minNrOfArgs = explicitArgs.length;//如果存在显示参数,就是显示参数的个数
    			}
    			else {
    				if (mbd.hasConstructorArgumentValues()) {//如果存在构造器参数值,就解析出最小参数个数
    					ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
    					resolvedValues = new ConstructorArgumentValues();
    					minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
    				}
    				else {
    					minNrOfArgs = 0;//没有就为0
    				}
    			}

分段3-核心工作

这段最核心的,自动装配就在里面,首先前面已经过滤出candidates工厂方法了,但是我们这不知道要选哪个去实例化,所以得有个选取规则,首先参数个数不能小于最少的,如果有显示的参数存在,那个数不一致的也不要,然后就获取参数名字探测器去进行每一个工厂方法的参数名字探测,然后创建一个参数持有器,这里面就会涉及自定装配,依赖的参数会实例化。最后根据参数持有器的参数和工厂方法的参数类型作比较,保存最小的差异值的那个,把模棱两可的集合设置空。如果有相同差异值的就放入一个集合里,如果集合有数据,说明不知道用哪个工厂方法来实例化,会报异常。大致就这么个过程,但是其中涉及了很多东西,只能慢慢来分析了。

    //遍历每个后选的方法,查看可以获取实例的匹配度
    			for (Method candidate : candidates) {
    				Class<?>[] paramTypes = candidate.getParameterTypes();
    				if (paramTypes.length >= minNrOfArgs) {
    					ArgumentsHolder argsHolder;
    
    					if (explicitArgs != null) {//显示参数存在,如果长度不对就继续下一个,否则就创建参数持有其持有
    
    						if (paramTypes.length != explicitArgs.length) {
    							continue;
    						}
    						argsHolder = new ArgumentsHolder(explicitArgs);
    					}
    					else {
    
    						try {
    							String[] paramNames = null;
    							ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();//获取参数名字探测器
    							if (pnd != null) {//存在的话进行探测
    								paramNames = pnd.getParameterNames(candidate);
    							}
    							argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw,
    									paramTypes, paramNames, candidate, autowiring, candidates.length == 1);
    						}
    						catch (UnsatisfiedDependencyException ex) {
    							...
    							continue;
    						}
    					}
    					//根据参数类型匹配,获取类型的差异值
    					int typeDiffWeight = (mbd.isLenientConstructorResolution() ?
    							argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));
    					// Choose this factory method if it represents the closest match.
    					if (typeDiffWeight < minTypeDiffWeight) {//保存最小的,说明参数类型相近
    						factoryMethodToUse = candidate;
    						argsHolderToUse = argsHolder;
    						argsToUse = argsHolder.arguments;
    						minTypeDiffWeight = typeDiffWeight;
    						ambiguousFactoryMethods = null;//没有模棱两可的方法
    					}//如果出现类型差异相同,参数个数也相同的,而且需要严格判断,参数长度也一样,常数类型也一样,就可能会无法判定要实例化哪个,就会报异常
    
    					else if (factoryMethodToUse != null && typeDiffWeight == minTypeDiffWeight &&
    							!mbd.isLenientConstructorResolution() &&
    							paramTypes.length == factoryMethodToUse.getParameterCount() &&
    							!Arrays.equals(paramTypes, factoryMethodToUse.getParameterTypes())) {
    						if (ambiguousFactoryMethods == null) {
    							ambiguousFactoryMethods = new LinkedHashSet<>();
    							ambiguousFactoryMethods.add(factoryMethodToUse);
    						}
    						ambiguousFactoryMethods.add(candidate);
    					}
    				}
    			}

后面一篇用一个简单的例子来分析下这个过程。

好了,今天就到这里了,希望对学习理解有帮助,大神看见勿喷,仅为自己的学习理解,能力有限,请多包涵。

阅读全文
  • 点赞