Spring 5.x 源码之旅-20getBean详解六

作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO

联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬

学习必须往深处挖,挖的越深,基础越扎实!

阶段1、深入多线程

阶段2、深入多线程设计模式

阶段3、深入juc源码解析


阶段4、深入jdk其余源码解析


阶段5、深入jvm源码解析

码哥源码部分

码哥讲源码-原理源码篇【2024年最新大厂关于线程池使用的场景题】

码哥讲源码【炸雷啦!炸雷啦!黄光头他终于跑路啦!】

码哥讲源码-【jvm课程前置知识及c/c++调试环境搭建】

​​​​​​码哥讲源码-原理源码篇【揭秘join方法的唤醒本质上决定于jvm的底层析构函数】

码哥源码-原理源码篇【Doug Lea为什么要将成员变量赋值给局部变量后再操作?】

码哥讲源码【你水不是你的错,但是你胡说八道就是你不对了!】

码哥讲源码【谁再说Spring不支持多线程事务,你给我抽他!】

终结B站没人能讲清楚红黑树的历史,不服等你来踢馆!

打脸系列【020-3小时讲解MESI协议和volatile之间的关系,那些将x86下的验证结果当作最终结果的水货们请闭嘴】

 

创建参数持有器

我就用两个工厂方法来做例子,其实他们差异是一样的,会报错,没关系,我们主要分析过程原理:


首先会用参数名字探测器去获取参数的名字,其实内部是用ASM读取字节码来操作的,主要是LocalVariableTableParameterNameDiscoverer这个类,二进制流读取class文件,然后分析,比较复杂,有兴趣的可以去看。我们直接就核心方法分析吧。

createArgumentArray

留下了一般的情况,不包括用xml方式设置参数值或者手动设置了ConstructorArgumentValues,就是最普通的形式。根据参数的数量,开始遍历参数,这里就会涉及参数的索引paramIndex 啦,xml里会用到,首先将工厂方法(构造方法)和索引封装成一个通用的MethodParameter类型处理,然后进行自动装配,解析出参数对象,里面很复杂,一会儿说,最后要注册依赖和被依赖的bean,其实就是做缓存,下次可以直接用。

    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 {
    		//类型转换器
    		TypeConverter customConverter = this.beanFactory.getCustomTypeConverter();
    		TypeConverter converter = (customConverter != null ? customConverter : bw);
    
    		ArgumentsHolder args = new ArgumentsHolder(paramTypes.length);//参数持有器
    		Set<ConstructorArgumentValues.ValueHolder> usedValueHolders = new HashSet<>(paramTypes.length);//构造器参数值集合
    		Set<String> autowiredBeanNames = new LinkedHashSet<>(4);//装配的bean名字
    		//带参数的话
    		for (int paramIndex = 0; paramIndex < paramTypes.length; paramIndex++) {
    			Class<?> paramType = paramTypes[paramIndex];
    			String paramName = (paramNames != null ? paramNames[paramIndex] : "");//获取参数名字
    			// Try to find matching constructor argument value, either indexed or generic.
    			ConstructorArgumentValues.ValueHolder valueHolder = null;
    			if (resolvedValues != null) {
    				...
    			}
    			if (valueHolder != null) {
    				...
    			}
    			else {
    				//获取统一的方法参数类型
    				MethodParameter methodParam = MethodParameter.forExecutable(executable, paramIndex);
    
    				if (!autowiring) {
    					throw new UnsatisfiedDependencyException(
    					...
    				}
    				try {//解析自动装配参数,找到会进行实例化
    					Object autowiredArgument = resolveAutowiredArgument(
    							methodParam, beanName, autowiredBeanNames, converter, fallback);
    					args.rawArguments[paramIndex] = autowiredArgument;
    					args.arguments[paramIndex] = autowiredArgument;
    					args.preparedArguments[paramIndex] = autowiredArgumentMarker;
    					args.resolveNecessary = true;
    				}
    				catch (BeansException ex) {
    					throw new UnsatisfiedDependencyException(
    							mbd.getResourceDescription(), beanName, new InjectionPoint(methodParam), ex);
    				}
    			}
    		}
    		//注册依赖的bean
    		for (String autowiredBeanName : autowiredBeanNames) {
    			this.beanFactory.registerDependentBean(autowiredBeanName, beanName);
    			if (logger.isDebugEnabled()) {
    				logger.debug("Autowiring by type from bean name '" + beanName +
    						"' via " + (executable instanceof Constructor ? "constructor" : "factory method") +
    						" to bean named '" + autowiredBeanName + "'");
    			}
    		}
    
    		return args;
    	}

MethodParameter的forExecutable封装方法参数

这里就是将工厂方法和构造方法一起处理成通用形式,方便后面统一处理。

    	public static MethodParameter forExecutable(Executable executable, int parameterIndex) {
    		if (executable instanceof Method) {
    			return new MethodParameter((Method) executable, parameterIndex);
    		}
    		else if (executable instanceof Constructor) {
    			return new MethodParameter((Constructor<?>) executable, parameterIndex);
    		}
    		else {
    			throw new IllegalArgumentException("Not a Method/Constructor: " + executable);
    		}
    	}
MethodParameter的构造方法

其实就是把方法和索引设置进去,还有一个嵌套层,比如list里嵌套list这种,暂时不用管,我们还是按一般的来,嵌套就是层就是1,如果不是1,后面会去查找内部的嵌套类型。

    	public MethodParameter(Method method, int parameterIndex, int nestingLevel) {
    		Assert.notNull(method, "Method must not be null");
    		this.executable = method;
    		this.parameterIndex = validateIndex(method, parameterIndex);
    		this.nestingLevel = nestingLevel;
    	}

resolveAutowiredArgument解析自动装配参数

首先判断是否是注入点类型InjectionPoint ,这个类型就是描述一个方法或者构造器的参数或者一个属性,因为这些都是可以自动注入的地方。如果是的话,就直接获取注入点返回,否则就让beanFactory来解析依赖,在这之前,先把MethodParameter 封装成DependencyDescriptor,也就是注入点。

    	protected Object resolveAutowiredArgument(MethodParameter param, String beanName,
    			@Nullable Set<String> autowiredBeanNames, TypeConverter typeConverter, boolean fallback) {
    
    		Class<?> paramType = param.getParameterType();
    		if (InjectionPoint.class.isAssignableFrom(paramType)) {//是否是注入点类型,比如DependencyDescriptor
    			InjectionPoint injectionPoint = currentInjectionPoint.get();
    			if (injectionPoint == null) {
    				throw new IllegalStateException("No current InjectionPoint available for " + param);
    			}
    			return injectionPoint;
    		}
    		try {//beanFactory解析依赖
    			return this.beanFactory.resolveDependency(
    					new DependencyDescriptor(param, true), beanName, autowiredBeanNames, typeConverter);
    		}
    		catch (NoUniqueBeanDefinitionException ex) {
    			throw ex;
    		}
    		catch (NoSuchBeanDefinitionException ex) {
    			if (fallback) {
    				...
    			}
    			throw ex;
    		}
    	}
DefaultListableBeanFactory的resolveDependency

判断依赖是什么类型,根据不同类型来处理,当然我们肯定是最后的那种情况,我们自定义的类型,看看是否是懒加载的,如果不是就立即调用doResolveDependency来解析。

    @Override
    	@Nullable
    	public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
    			@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
    		//设置参数名字探索器
    		descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
    		if (Optional.class == descriptor.getDependencyType()) {//Optional类型
    			return createOptionalDependency(descriptor, requestingBeanName);
    		}//是对象工厂类型或者对象提供者类型
    		else if (ObjectFactory.class == descriptor.getDependencyType() ||
    				ObjectProvider.class == descriptor.getDependencyType()) {
    			return new DependencyObjectProvider(descriptor, requestingBeanName);
    		}//java扩展的注入类
    		else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
    			return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);
    		}
    		else {//我们自己只能的类型,先看是否是懒加载,是的话就直接返回,否则要去解析依赖
    			Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
    					descriptor, requestingBeanName);
    			if (result == null) {
    				result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
    			}
    			return result;
    		}
    	}
MethodParameter的getDependencyType获取依赖类型

这里如果有属性存在,而且嵌套类型大于1,就会找出真实类型,比如List<String>这种,String的嵌套就是2,真实类型就是String。当然我们不是属性注入,所以直接调用MethodParametergetNestedParameterType

    public Class<?> getDependencyType() {
    		if (this.field != null) {
    			if (this.nestingLevel > 1) {
    				Type type = this.field.getGenericType();
    				for (int i = 2; i <= this.nestingLevel; i++) {
    					if (type instanceof ParameterizedType) {
    						Type[] args = ((ParameterizedType) type).getActualTypeArguments();
    						type = args[args.length - 1];
    					}
    				}
    				if (type instanceof Class) {
    					return (Class<?>) type;
    				}
    				else if (type instanceof ParameterizedType) {
    					Type arg = ((ParameterizedType) type).getRawType();
    					if (arg instanceof Class) {
    						return (Class<?>) arg;
    					}
    				}
    				return Object.class;
    			}
    			else {
    				return this.field.getType();
    			}
    		}
    		else {
    			return obtainMethodParameter().getNestedParameterType();
    		}
    	}
MethodParameter的getNestedParameterType

发现了大量重复代码,其实大部分是跟属性的处理逻辑一样的,如果只有1层,就直接调用getParameterType

    public Class<?> getNestedParameterType() {
    		if (this.nestingLevel > 1) {
    			Type type = getGenericParameterType();
    			for (int i = 2; i <= this.nestingLevel; i++) {
    				if (type instanceof ParameterizedType) {
    					Type[] args = ((ParameterizedType) type).getActualTypeArguments();
    					Integer index = getTypeIndexForLevel(i);
    					type = args[index != null ? index : args.length - 1];
    				}
    				// TODO: Object.class if unresolvable
    			}
    			if (type instanceof Class) {
    				return (Class<?>) type;
    			}
    			else if (type instanceof ParameterizedType) {
    				Type arg = ((ParameterizedType) type).getRawType();
    				if (arg instanceof Class) {
    					return (Class<?>) arg;
    				}
    			}
    			return Object.class;
    		}
    		else {
    			return getParameterType();
    		}
    	}
DependencyDescriptor的getNestedParameterType

存在类型就直接返回了,否则就要进行其他处理获得参数类型。

    public Class<?> getParameterType() {
    		Class<?> paramType = this.parameterType;
    		if (paramType != null) {
    			return paramType;
    		}
    		if (getContainingClass() != getDeclaringClass()) {
    			paramType = ResolvableType.forMethodParameter(this, null, 1).resolve();
    		}
    		if (paramType == null) {
    			paramType = computeParameterType();
    		}
    		this.parameterType = paramType;
    		return paramType;
    	}

好了,今天就到这里了,希望对学习理解有帮助。

  • 20
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值