【spring】依赖注入之@Autowired依赖注入

@Autowired依赖注入

  • 本文源码基于spring-framework-5.3.10。
  • 源码位置:org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(PropertyValues, Object, String)
  • 他在Bean的生命周期的实例化后之后,初始化前(或者说各种Aware回调之前)之前调用。

@Autowired依赖注入的大致流程

  • 寻找注入点:被@Autowired注解的方法和字段
  • 遍历每个注入点
  • 根据注入点类型去寻找bean:属性的类型、方法参数的类型
  • 找到一个Bean进行注入。没有找到Bean,如果是必填的,抛异常。找到多个Bean,去取出唯一的Bean
  • 判断是不是isAutowireCandidate
  • 判断是不是数组、Map、列表..
  • 这时候如果还有多个:取@Primary标注了的Bean
  • 这时候如果还有多个:取优先级最高的Bean
  • 这时候如果还有多个:根据名字(属性就是属性的名字,方法是参数的名字)筛选出唯一一个

@Autowired依赖注入源码分析

public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
	// 寻找注入点(所有被@Autowired注解了的Field或Method)
	InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
	try {
		// 依赖注入。Field与Method的实现方式不同
		metadata.inject(bean, beanName, pvs);
	}
	catch (BeanCreationException ex) {
		throw ex;
	}
	catch (Throwable ex) {
		throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
	}
	// 返回全部的属性值对象
	return pvs;
}

/**
 * 依赖注入:这个方法主要作用是循环每个注入点进行注入。区分字段和方法进行注入
 */
public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
	// 得到全部的注入点
	Collection<InjectedElement> checkedElements = this.checkedElements;
	Collection<InjectedElement> elementsToIterate =
			(checkedElements != null ? checkedElements : this.injectedElements);
	if (!elementsToIterate.isEmpty()) {
		// 遍历每个注入点进行依赖注入
		for (InjectedElement element : elementsToIterate) {
			element.inject(target, beanName, pvs);
		}
	}
}

@Autowired针对字段的注入方式

protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {

	// 得到当前注入点的字段
	Field field = (Field) this.member;
	// 定义一个具体注入的值
	Object value;
	if (this.cached) {
		// 对于原型Bean,第一次创建的时候,也找注入点,然后进行注入,此时cached为false,注入完了之后cached为true
		// 第二次创建的时候,先找注入点(此时会拿到缓存好的注入点),也就是AutowiredFieldElement对象,此时cache为true,也就进到此处了
		// 注入点内并没有缓存被注入的具体Bean对象,而是beanName,这样就能保证注入到不同的原型Bean对象
		try {
			value = resolvedCachedArgument(beanName, this.cachedFieldValue);
		}
		catch (NoSuchBeanDefinitionException ex) {
			// Unexpected removal of target bean for cached argument -> re-resolve
			value = resolveFieldValue(field, bean, beanName);
		}
	}
	else {
		// 根据filed从BeanFactory中获取的匹配的Bean对象
		value = resolveFieldValue(field, bean, beanName);
	}

	// 反射给filed赋值
	if (value != null) {
		// 主要设置私有私有可以访问,标记在反射的时候跳过校验!
		ReflectionUtils.makeAccessible(field);
		// 给字段设置值!
		field.set(bean, value);
	}
}

/**
 * 根据filed从BeanFactory中获取的匹配的Bean对象
 */
private Object resolveFieldValue(Field field, Object bean, @Nullable String beanName) {

	// 根据当前字段以及@autowired(required = true)构建一个属性描述器
	DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
	// 设置属性描述器对应的class为当前Bean的class
	desc.setContainingClass(bean.getClass());

	// 得到需要注入的BeanName几个
	Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
	Assert.state(beanFactory != null, "No BeanFactory available");
	// 得到当前Bean工厂的类型转化器
	TypeConverter typeConverter = beanFactory.getTypeConverter();
	// 属性实际注入的值
	Object value;
	try {
		// 获取要注入的值
		value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
	}
	catch (BeansException ex) {
		throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
	}
	synchronized (this) {
		// 缓存不存在的时候(走过一遍的时候会有缓存),会走下面的逻辑
		if (!this.cached) {
			// 缓存的属性描述器
			Object cachedFieldValue = null;
			// 要注入的值不为null或者这个属性是必填的!
			if (value != null || this.required) {
				// 属性描述器赋值
				cachedFieldValue = desc;
				// 注册一下beanName依赖了autowiredBeanNames,
				registerDependentBeans(beanName, autowiredBeanNames);
				// 只有一个,找到了唯一的Bean。上面的定义,这里肯定是一个
				if (autowiredBeanNames.size() == 1) {
					// 得到要注入的BeanName
					String autowiredBeanName = autowiredBeanNames.iterator().next();
					// Bean工厂中可以获取当前Bean,并且类型与要注入的类型一致
					if (beanFactory.containsBean(autowiredBeanName) &&
							beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
						// 构造一个ShortcutDependencyDescriptor作为缓存,保存了当前filed所匹配的autowiredBeanName,而不是对应的bean对象(考虑原型bean)
						cachedFieldValue = new ShortcutDependencyDescriptor(
								desc, autowiredBeanName, field.getType());
					}
				}
			}
			// 全局缓存的属性描述器赋值
			this.cachedFieldValue = cachedFieldValue;
			// 下次缓存判断就不会进来了
			this.cached = true;
		}
	}
	// 返回具体注入的值
	return value;
}

@Autowired针对方法的注入方式

protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
	// 如果pvs中已经有当前注入点的值了,则跳过注入。程序员自己设置了值
	if (checkPropertySkipping(pvs)) {
		return;
	}
	// 得到当前要注入的的方法
	Method method = (Method) this.member;
	// 得到当前方法的参数
	Object[] arguments;
	if (this.cached) {
		try {
			// 使用缓存的方式进行注入
			arguments = resolveCachedArguments(beanName);
		}
		catch (NoSuchBeanDefinitionException ex) {
			// Unexpected removal of target bean for cached argument -> re-resolve
			arguments = resolveMethodArguments(method, bean, beanName);
		}
	}
	else {
		// 根据method从BeanFactory中获取的参数匹配的Bean对象
		arguments = resolveMethodArguments(method, bean, beanName);
	}
	// 参数不为空的时候(没有抛异常的时候),进行方法调用
	if (arguments != null) {
		try {
			// 主要设置私有方法可以访问,标记在反射的时候跳过校验!
			ReflectionUtils.makeAccessible(method);
			// 执行具体的方法
			method.invoke(bean, arguments);
		}
		catch (InvocationTargetException ex) {
			throw ex.getTargetException();
		}
	}
}

/**
 * 根据method从BeanFactory中获取的参数匹配的Bean对象
 */
private Object[] resolveMethodArguments(Method method, Object bean, @Nullable String beanName) {
	// 得到方法参数的个数
	int argumentCount = method.getParameterCount();
	// 定义一个参数的数组
	Object[] arguments = new Object[argumentCount];
	// 定义一个依赖描述器
	DependencyDescriptor[] descriptors = new DependencyDescriptor[argumentCount];
	// 定义要注入的每个Bean的集合
	Set<String> autowiredBeans = new LinkedHashSet<>(argumentCount);
	Assert.state(beanFactory != null, "No BeanFactory available");
	// 得到当前Bean工厂的类型转化器
	TypeConverter typeConverter = beanFactory.getTypeConverter();

	// 遍历每个方法参数,找到匹配的bean对象
	for (int i = 0; i < arguments.length; i++) {
		// 构建具体的参数对象
		MethodParameter methodParam = new MethodParameter(method, i);

		// 构建具体的依赖描述器对象
		DependencyDescriptor currDesc = new DependencyDescriptor(methodParam, this.required);
		// 设置属性描述器对应的class为当前Bean的class
		currDesc.setContainingClass(bean.getClass());
		// 依赖描述器集合数据设置
		descriptors[i] = currDesc;
		try {
			// 获取要注入的值
			Object arg = beanFactory.resolveDependency(currDesc, beanName, autowiredBeans, typeConverter);
			// 有任意一个参数没找到,并且当前的方法不是必须的要注入的。直接放弃继续找参数
			if (arg == null && !this.required) {
				arguments = null;
				break;
			}
			// 参数赋值
			arguments[i] = arg;
		}
		catch (BeansException ex) {
			throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(methodParam), ex);
		}
	}
	synchronized (this) {
		// 缓存不存在的时候(走过一遍的时候会有缓存),会走下面的逻辑
		if (!this.cached) {
			// 存在参数(得到了参数值)
			if (arguments != null) {
				// 得到参数值的数组
				DependencyDescriptor[] cachedMethodArguments = Arrays.copyOf(descriptors, arguments.length);
				// 注册一下beanName依赖了autowiredBeanNames。
				registerDependentBeans(beanName, autowiredBeans);
				// 得到的参数和需要的参数数量相同的时候
				if (autowiredBeans.size() == argumentCount) {
					// 得到要注入参数的迭代器
					Iterator<String> it = autowiredBeans.iterator();
					// 得到当前方法各个参数的类型
					Class<?>[] paramTypes = method.getParameterTypes();
					// 遍历每一个参数的类型
					for (int i = 0; i < paramTypes.length; i++) {
						// 得到类型对应的Bean名称
						String autowiredBeanName = it.next();
						// 能在Bean工厂找到当前参数的Bean,并且类型能匹配上,参数值赋值
						if (beanFactory.containsBean(autowiredBeanName) &&
								beanFactory.isTypeMatch(autowiredBeanName, paramTypes[i])) {
							cachedMethodArguments[i] = new ShortcutDependencyDescriptor(
									descriptors[i], autowiredBeanName, paramTypes[i]);
						}
					}
				}
				// 设置全局的参数对应的BeanName
				this.cachedMethodArguments = cachedMethodArguments;
			}
			else {
				// 参数值不全的时候,或者没有参数的时候,这里设置为null
				this.cachedMethodArguments = null;
			}
			// 下次缓存判断就不会进来了
			this.cached = true;
		}
	}
	// 返回具体注入需要的参数值的集合
	return arguments;
}

获取要注入的值外部方法:resolveDependency源码分析

/**
 * DependencyDescriptor descriptor:依赖描述器,可能是字段,方法
 * requestingBeanName:请求的BeanName
 * autowiredBeanNames:需要注入的BeanName集合
 * typeConverter:类型转换器
 */
public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
		@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
	// 用来获取方法入参名字的
	descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());

	// 所需要的类型是Optional,包装成Optional对象,核心调用的也是doResolveDependency
	if (Optional.class == descriptor.getDependencyType()) {
		return createOptionalDependency(descriptor, requestingBeanName);
	}
	// 所需要的的类型是ObjectFactory,或ObjectProvider
	else if (ObjectFactory.class == descriptor.getDependencyType() ||
			ObjectProvider.class == descriptor.getDependencyType()) {
		return new DependencyObjectProvider(descriptor, requestingBeanName);
	}
	else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
		return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);
	}
	else {
		// 在属性或set方法上使用了@Lazy注解,那么则构造一个代理对象并返回,真正使用该代理对象时才进行类型筛选Bean
		Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
				descriptor, requestingBeanName);

		// 没有@Lazy注解的时候
		if (result == null) {
			// descriptor表示某个属性或某个set方法
			// requestingBeanName表示正在进行依赖注入的Bean
			result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
		}
		return result;
	}
}

获取要注入的值内部方法:doResolveDependency源码分析

/**
 * 最核心的方法,如何寻找Bean
 * descriptor:表示某个属性或某个set方法
 * beanName:表示正在进行依赖注入的Bean
 * autowiredBeanNames:需要注入的BeanName列表
 * typeConverter:类型转换器
 */
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
		@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {

	// 设置当前的descriptor(存储了方法字段参数等信息)为当前注入点
	InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
	try {
		// 如果当前descriptor之前做过依赖注入了,则可以直接取shortcut了,相当于缓存
		Object shortcut = descriptor.resolveShortcut(this);
		if (shortcut != null) {
			return shortcut;
		}

		// 找到要注入的类型,可能是字段的类型,也可能是方法参数的类型
		Class<?> type = descriptor.getDependencyType();
		// 获取@Value所指定的值
		Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
		// 字段上或者方法参数存在@Value注解
		if (value != null) {
			// 占位符或者spring表达式的解析
			if (value instanceof String) {
				// 占位符填充(${})
				String strVal = resolveEmbeddedValue((String) value);
				BeanDefinition bd = (beanName != null && containsBean(beanName) ?
						getMergedBeanDefinition(beanName) : null);
				// 解析Spring表达式(#{})
				value = evaluateBeanDefinitionString(strVal, bd);
			}
			// 将value转化为descriptor所对应的类型
			TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
			try {
				// 需要转码后的值
				return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor());
			}
			catch (UnsupportedOperationException ex) {
				// A custom TypeConverter which does not support TypeDescriptor resolution...
				// 不是string转Bean,变为使用自定义的转化器
				return (descriptor.getField() != null ?
						converter.convertIfNecessary(value, type, descriptor.getField()) :
						converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
			}
		}

		// 如果descriptor所对应的类型是数组、Map这些,就将descriptor对应的类型所匹配的所有bean方法,不用进一步做筛选了
		Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
		if (multipleBeans != null) {
			return multipleBeans;
		}

		// 找到所有Bean,key是beanName, value有可能是bean对象,有可能是beanClass(接口的方式注入,优先级较低,类没有加载)
		Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
		if (matchingBeans.isEmpty()) {
			// required为true,抛异常
			if (isRequired(descriptor)) {
				raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
			}
			return null;
		}

		// 定义要注入的Bean名称
		String autowiredBeanName;
		// 定义要注入的Bean值
		Object instanceCandidate;

		if (matchingBeans.size() > 1) {
			// 根据类型找到了多个Bean,进一步筛选出某一个, @Primary-->优先级最高--->name
			// 这里实现的是ByName
			autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
			// 注入的Bean的名称为null
			if (autowiredBeanName == null) {
				// 他是必填的
				if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
					// 抛异常
					return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);
				}
				else {
					// In case of an optional Collection/Map, silently ignore a non-unique case:
					// possibly it was meant to be an empty collection of multiple regular beans
					// (before 4.3 in particular when we didn't even look for collection beans).
					// 非必填返回null
					return null;
				}
			}
			// 给当前要注入的具体指赋值
			instanceCandidate = matchingBeans.get(autowiredBeanName);
		}
		else {
			// We have exactly one match.
			// 只找到了一个Bean
			Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
			// 给当前要注入的BeanName赋值
			autowiredBeanName = entry.getKey();
			// 给当前要注入的具体指赋值
			instanceCandidate = entry.getValue();
		}

		// 记录匹配过的beanName
		if (autowiredBeanNames != null) {
			autowiredBeanNames.add(autowiredBeanName);
		}
		// 有可能筛选出来的是某个bean的类型,此处就进行实例化,调用getBean
		if (instanceCandidate instanceof Class) {
			instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
		}
		// 空的抛异常
		Object result = instanceCandidate;
		if (result instanceof NullBean) {
			if (isRequired(descriptor)) {
				raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
			}
			result = null;
		}
		// 类型不匹配抛异常
		if (!ClassUtils.isAssignableValue(type, result)) {
			throw new BeanNotOfRequiredTypeException(autowiredBeanName, type, instanceCandidate.getClass());
		}
		// 返回注入的值
		return result;
	}
	finally {
		// 
		ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
	}
}

针对数组、Map的特殊处理:resolveMultipleBeans源码分析

private Object resolveMultipleBeans(DependencyDescriptor descriptor, @Nullable String beanName,
		@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) {

	// 得到当前要注入的类型
	Class<?> type = descriptor.getDependencyType();

	// 属于Stream相关的
	if (descriptor instanceof StreamDependencyDescriptor) {
		// 找到type所匹配的所有bean
		Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
		if (autowiredBeanNames != null) {
			autowiredBeanNames.addAll(matchingBeans.keySet());
		}

		// 构造成一个stream
		Stream<Object> stream = matchingBeans.keySet().stream()
				.map(name -> descriptor.resolveCandidate(name, type, this))
				.filter(bean -> !(bean instanceof NullBean));

		// 排序
		if (((StreamDependencyDescriptor) descriptor).isOrdered()) {
			stream = stream.sorted(adaptOrderComparator(matchingBeans));
		}
		return stream;
	}
	// 属于数组相关的
	else if (type.isArray()) {
		// 得到数组元素的类型
		Class<?> componentType = type.getComponentType();
		ResolvableType resolvableType = descriptor.getResolvableType();
		Class<?> resolvedArrayType = resolvableType.resolve(type);
		if (resolvedArrayType != type) {
			componentType = resolvableType.getComponentType().resolve();
		}
		if (componentType == null) {
			return null;
		}
		// 根据数组元素类型找到所匹配的所有Bean
		Map<String, Object> matchingBeans = findAutowireCandidates(beanName, componentType,
				new MultiElementDescriptor(descriptor));
		// 没有获取到bean,返回Null
		if (matchingBeans.isEmpty()) {
			return null;
		}
		// 把需要注入的BeanName传入的数组中
		if (autowiredBeanNames != null) {
			autowiredBeanNames.addAll(matchingBeans.keySet());
		}
		// 进行类型转化
		TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
		// Map的Values转为数组
		Object result = converter.convertIfNecessary(matchingBeans.values(), resolvedArrayType);
		// 排序
		if (result instanceof Object[]) {
			Comparator<Object> comparator = adaptDependencyComparator(matchingBeans);
			if (comparator != null) {
				Arrays.sort((Object[]) result, comparator);
			}
		}
		return result;
	}
	// Collection处理
	else if (Collection.class.isAssignableFrom(type) && type.isInterface()) {
		// 找到这个列表的具体类型
		Class<?> elementType = descriptor.getResolvableType().asCollection().resolveGeneric();
		// 类似是null,直接返回
		if (elementType == null) {
			return null;
		}
		// 找到type所匹配的所有bean
		Map<String, Object> matchingBeans = findAutowireCandidates(beanName, elementType,
				new MultiElementDescriptor(descriptor));
		// 没有找到Bean,直接返回
		if (matchingBeans.isEmpty()) {
			return null;
		}
		// 把需要注入的BeanName传入的数组中
		if (autowiredBeanNames != null) {
			autowiredBeanNames.addAll(matchingBeans.keySet());
		}
		// 类型转换
		TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
		// 把Map的Values拿出来
		Object result = converter.convertIfNecessary(matchingBeans.values(), type);
		// 遍历拿到的Values
		if (result instanceof List) {
			// 排序
			if (((List<?>) result).size() > 1) {
				Comparator<Object> comparator = adaptDependencyComparator(matchingBeans);
				if (comparator != null) {
					((List<?>) result).sort(comparator);
				}
			}
		}
		// 返回链表的结果
		return result;
	}
	// Map类型处理
	else if (Map.class == type) {
		// 找到Map的Key的类型
		ResolvableType mapType = descriptor.getResolvableType().asMap();
		Class<?> keyType = mapType.resolveGeneric(0);
		// 如果Map的key不是String
		if (String.class != keyType) {
			return null;
		}
		Class<?> valueType = mapType.resolveGeneric(1);
		// Map的Balue不能是null
		if (valueType == null) {
			return null;
		}
		// 找到type所匹配的所有bean
		Map<String, Object> matchingBeans = findAutowireCandidates(beanName, valueType,
				new MultiElementDescriptor(descriptor));
		// 找到的Bean为空,直接返回null
		if (matchingBeans.isEmpty()) {
			return null;
		}
		// 把需要注入的BeanName传入的数组中
		if (autowiredBeanNames != null) {
			autowiredBeanNames.addAll(matchingBeans.keySet());
		}
		// 返回找到的所有Bean
		return matchingBeans;
	}
	else {
		// 不是上面的几种类型,直接返回null
		return null;
	}
}

@Autowire自己的ByName源码分析

protected String determineAutowireCandidate(Map<String, Object> candidates, DependencyDescriptor descriptor) {
	Class<?> requiredType = descriptor.getDependencyType();
	// candidates表示根据类型所找到的多个Bean,判断这些Bean中是否有一个是@Primary的,存在多个@Primary注解,抛异常!
	String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);
	if (primaryCandidate != null) {
		return primaryCandidate;
	}

	// 取优先级最高的Bean。这里的优先级指的是@Priority注解配置的优先级,数字越小,优先级越高!
	String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType);
	if (priorityCandidate != null) {
		return priorityCandidate;
	}

	// Fallback
	// 匹配descriptor的名字,要么是字段的名字,要么是set方法入参的名字
	for (Map.Entry<String, Object> entry : candidates.entrySet()) {
		String candidateName = entry.getKey();
		Object beanInstance = entry.getValue();

		// resolvableDependencies记录了某个类型对应某个Bean,启动Spring时会进行设置,比如BeanFactory.class对应BeanFactory实例
		// 注意:如果是Spring自己的byType,descriptor.getDependencyName()将返回空,只有是@Autowired才会方法属性名或方法参数名
		if ((beanInstance != null && this.resolvableDependencies.containsValue(beanInstance)) ||
				matchesBeanName(candidateName, descriptor.getDependencyName())) {
			return candidateName;
		}
	}
	return null;
}

获取方法入参名称:DefaultParameterNameDiscoverer源码分析

public DefaultParameterNameDiscoverer() {
	// TODO Remove this conditional inclusion when upgrading to Kotlin 1.5, see https://youtrack.jetbrains.com/issue/KT-44594
	if (KotlinDetector.isKotlinReflectPresent() && !NativeDetector.inNativeImage()) {
		addDiscoverer(new KotlinReflectionParameterNameDiscoverer());
	}
	// 反射(1.8+):getParameterNames
	addDiscoverer(new StandardReflectionParameterNameDiscoverer());
	// ASM分析.class文件
	addDiscoverer(new LocalVariableTableParameterNameDiscoverer());
}

结束语

  • 获取更多本文的前置知识文章,以及新的有价值的文章,让我们一起成为架构师!
  • 关注公众号,可以让你对MySQL、并发编程、spring源码有深入的了解!
  • 关注公众号,后续持续高效的学习JVM!
  • 这个公众号,无广告!!!每日更新!!!
    作者公众号.jpg
posted @ 2022-02-22 23:47  程序java圈  阅读(185)  评论(0编辑  收藏  举报