只有一个无参工厂方法
如果只有一个构造函数的话,看是否有参数,没有的话直接就设置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
个同名的,其实也就是重载的,有不同修饰符的,有不同参数个数的,没排序前:
排序后:
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
也很关键,用来做优化的,如果有最小参数个数,那么一些参数个数少于最小个数的就不需要去判断了,直接跳过,否则就算获取到了也不匹配,反而浪费资源了。如果minNrOfArgs
为0
,那就说明没有参数个数限制,那后面就需要一个个去判断哪个差异最小,最符合了。
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);
}
}
}
后面一篇用一个简单的例子来分析下这个过程。
好了,今天就到这里了,希望对学习理解有帮助,大神看见勿喷,仅为自己的学习理解,能力有限,请多包涵。