【走近Spring】Spring标准处理组件大合集(ParameterNameDiscoverer、AutowireCandidateResolver、ResolvableType...)

在阅读Spring源码的时候,经常会遇见一些处理器、提供器之类的组件,有的时候不深入去理解它的含义,确实还读不下去了。

为了方便自己流畅的阅读下去,本文介绍Spring提供的处理组件,尽量的解释清楚它们的作用甚至原理,以便我们能更自由的阅读。

ParameterNameDiscoverer:获取方法参数名称的工具

DefaultParameterNameDiscoverer:它其实就是个聚合的作用

// Spring4.0后出现的类(伴随着StandardReflectionParameterNameDiscoverer一起出现的)
public class DefaultParameterNameDiscoverer extends PrioritizedParameterNameDiscoverer {

	private static final boolean kotlinPresent =
			ClassUtils.isPresent("kotlin.Unit", DefaultParameterNameDiscoverer.class.getClassLoader());

	public DefaultParameterNameDiscoverer() {
		// 这里非常非常需要注意的一点是:用于存储的是一个LinkedList(见父类:PrioritizedParameterNameDiscoverer)
		// LinkedList是先进先出的。所以for循环遍历的时候,会最先执行Kotlin、Standard、Local... 按照这个优先级
		if (kotlinPresent) {
			addDiscoverer(new KotlinReflectionParameterNameDiscoverer());
		}
		addDiscoverer(new StandardReflectionParameterNameDiscoverer());
		addDiscoverer(new LocalVariableTableParameterNameDiscoverer());
	}

}

这里面就是给注册进去了实际意义上干事情的三个ParameterNameDiscoverer。然后它自己的方法getParameterNames和getParameterNames都由注册进来的去完成~~~~~~~方法都在它的父类:

PrioritizedParameterNameDiscoverer

// Spring2.0就出现了。它的一个原则很简单:这么多发现器,按照顺序执行,随获取到了就算谁的
public class PrioritizedParameterNameDiscoverer implements ParameterNameDiscoverer {

	private final List<ParameterNameDiscoverer> parameterNameDiscoverers = new LinkedList<>();

	public void addDiscoverer(ParameterNameDiscoverer pnd) {
		this.parameterNameDiscoverers.add(pnd);
	}


	@Override
	@Nullable
	public String[] getParameterNames(Method method) {
		for (ParameterNameDiscoverer pnd : this.parameterNameDiscoverers) {
			String[] result = pnd.getParameterNames(method);
			if (result != null) {
				return result;
			}
		}
		return null;
	}

	@Override
	@Nullable
	public String[] getParameterNames(Constructor<?> ctor) {
		for (ParameterNameDiscoverer pnd : this.parameterNameDiscoverers) {
			String[] result = pnd.getParameterNames(ctor);
			if (result != null) {
				return result;
			}
		}
		return null;
	}

}

那么接下来说说:StandardReflectionParameterNameDiscoverer和LocalVariableTableParameterNameDiscoverer

StandardReflectionParameterNameDiscoverer

Spring4.0提供 ,但是也得jdk8及以上版本使用。

Java.lang.reflect 包中提供了很多方法,获取所有的方法,获取所有的参数类型等,但是却没有一个方法能够帮助我们获取方法的参数名列表。JDK提供了方法弥补了这个缺陷。

public class StandardReflectionParameterNameDiscoverer implements ParameterNameDiscoverer {

	@Override
	@Nullable
	public String[] getParameterNames(Method method) {
		return getParameterNames(method.getParameters());
	}

	@Override
	@Nullable
	public String[] getParameterNames(Constructor<?> ctor) {
		return getParameterNames(ctor.getParameters());
	}

	// 因为Parameter这个类是JDK8以上才提供的
	@Nullable
	private String[] getParameterNames(Parameter[] parameters) {
		String[] parameterNames = new String[parameters.length];
		for (int i = 0; i < parameters.length; i++) {
			Parameter param = parameters[i];
			if (!param.isNamePresent()) {
				return null;
			}
			parameterNames[i] = param.getName();
		}
		return parameterNames;
	}

}

LocalVariableTableParameterNameDiscoverer

Spring2.0就有了,对JDK版本没啥要求,完全Spring自己实现的获取字段名称,逻辑复杂些,效率稍微低一点。

原理是:通过ASM提供的通过字节码获取方法的参数名称,Spring给我们集成了这个类,我们只需要简单的使用即可。

这个处理在我们Controller方法入参自动映射时,我们发现我们并不需要写@RequestParam("aaa")这样的注解,也能给自动映射上值,其实底层就运用了它去把这个key分析出来,然后去request里面拿的。

下面来看一个例子:

public class Main {

    private static final ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();

    public void testArguments(String test, Integer myInteger, boolean booleanTest) {
    }

    public void test() {
    }

    public static void main(String[] args) {
        Method[] methods = Main.class.getDeclaredMethods();
        for (Method method : methods) {
            String[] parameterNames = parameterNameDiscoverer.getParameterNames(method);
            System.out.println("方法:" + method.getName() + " 参数为:" + Arrays.asList(parameterNames));
        }
    }

}

输出为:

方法:main 参数为:[args]
方法:test 参数为:[]
方法:testArguments 参数为:[test, myInteger, booleanTest]

接下来,我们什么都不改,只改一句话:

private static final ParameterNameDiscoverer parameterNameDiscoverer = new StandardReflectionParameterNameDiscoverer();

运行看看效果:也能得出一样的效果

方法:main 参数为:[args]
方法:test 参数为:[]
方法:testArguments 参数为:[test, myInteger, booleanTest]

这里面需要特别注意了,StandardReflectionParameterNameDiscoverer的使用条件有两个

  • JDK8以上版本
  • 必须编译的时候有带上参数:javac -parameters

那么有小伙伴就疑问了,为何我上面编译没加参数也好使呢?这里面小伙伴务必注意了:如果你使用的idea的IDE,它里面默认编辑就带有此参数的:(Compiler-Java Compiler)

在这里插入图片描述

若你把它去掉,你的结果会如下:

// 去掉这个参数后,获取到的parameterNames 就是null了
// 再次提示一下:若还没效果,请手动重新编译对应文件
String[] parameterNames = parameterNameDiscoverer.getParameterNames(method);

为了获取最好的兼容性,推荐使用DefaultParameterNameDiscoverer,而不是直接使用某个具体的Discoverer

AutowireCandidateResolver

策略接口,对特定的依赖,这个接口决定一个特定的bean definition是否满足作为自动绑定的备选项。

// 本接口Spring2.5就提供了
public interface AutowireCandidateResolver {
	default boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
		return bdHolder.getBeanDefinition().isAutowireCandidate();
	}
	// Spring5.0后提供
	default boolean isRequired(DependencyDescriptor descriptor) {
		return descriptor.isRequired();
	}
	// 3.0后提供
	@Nullable
	default Object getSuggestedValue(DependencyDescriptor descriptor) {
		return null;
	}
	// 4.0后提供
	@Nullable
	default Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, @Nullable String beanName) {
		return null;
	}
}

继承关系如下:

在这里插入图片描述

  • SimpleAutowireCandidateResolver没有任何实现,就是一个Adapter
  • GenericTypeAwareAutowireCandidateResolver:基础实现,有核心逻辑方法:checkGenericTypeMatch(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor),

作用为:根据给定的候选bean定义,将给定的依赖类型与其泛型类型信息匹配。(这就是泛型依赖注入的核心匹配逻辑,所以这其实也是Spring4.0后才推出来的)

protected boolean checkGenericTypeMatch(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
	ResolvableType dependencyType = descriptor.getResolvableType();
	if (dependencyType.getType() instanceof Class) {
		// No generic type -> we know it's a Class type-match, so no need to check again.
		return true;
	}

	ResolvableType targetType = null;
	boolean cacheType = false;
	RootBeanDefinition rbd = null;
	if (bdHolder.getBeanDefinition() instanceof RootBeanDefinition) {
		rbd = (RootBeanDefinition) bdHolder.getBeanDefinition();
	}
	if (rbd != null) {
		targetType = rbd.targetType;
		if (targetType == null) {
			cacheType = true;
			// First, check factory method return type, if applicable
			targetType = getReturnTypeForFactoryMethod(rbd, descriptor);
			if (targetType == null) {
				RootBeanDefinition dbd = getResolvedDecoratedDefinition(rbd);
				if (dbd != null) {
					targetType = dbd.targetType;
					if (targetType == null) {
						targetType = getReturnTypeForFactoryMethod(dbd, descriptor);
					}
				}
			}
		}
	}

	if (targetType == null) {
		// Regular case: straight bean instance, with BeanFactory available.
		if (this.beanFactory != null) {
			Class<?> beanType = this.beanFactory.getType(bdHolder.getBeanName());
			if (beanType != null) {
				targetType = ResolvableType.forClass(ClassUtils.getUserClass(beanType));
			}
		}
		// Fallback: no BeanFactory set, or no type resolvable through it
		// -> best-effort match against the target class if applicable.
		if (targetType == null && rbd != null && rbd.hasBeanClass() && rbd.getFactoryMethodName() == null) {
			Class<?> beanClass = rbd.getBeanClass();
			if (!FactoryBean.class.isAssignableFrom(beanClass)) {
				targetType = ResolvableType.forClass(ClassUtils.getUserClass(beanClass));
			}
		}
	}

	if (targetType == null) {
		return true;
	}
	if (cacheType) {
		rbd.targetType = targetType;
	}
	if (descriptor.fallbackMatchAllowed() && targetType.hasUnresolvableGenerics()) {
		return true;
	}
	// Full check for complex generic type match...
	return dependencyType.isAssignableFrom(targetType);
}

@Nullable
protected RootBeanDefinition getResolvedDecoratedDefinition(RootBeanDefinition rbd) {
	BeanDefinitionHolder decDef = rbd.getDecoratedDefinition();
	if (decDef != null && this.beanFactory instanceof ConfigurableListableBeanFactory) {
		ConfigurableListableBeanFactory clbf = (ConfigurableListableBeanFactory) this.beanFactory;
		if (clbf.containsBeanDefinition(decDef.getBeanName())) {
			BeanDefinition dbd = clbf.getMergedBeanDefinition(decDef.getBeanName());
			if (dbd instanceof RootBeanDefinition) {
				return (RootBeanDefinition) dbd;
			}
		}
	}
	return null;
}

@Nullable
protected ResolvableType getReturnTypeForFactoryMethod(RootBeanDefinition rbd, DependencyDescriptor descriptor) {
	// Should typically be set for any kind of factory method, since the BeanFactory
	// pre-resolves them before reaching out to the AutowireCandidateResolver...
	ResolvableType returnType = rbd.factoryMethodReturnType;
	if (returnType == null) {
		Method factoryMethod = rbd.getResolvedFactoryMethod();
		if (factoryMethod != null) {
			returnType = ResolvableType.forMethodReturnType(factoryMethod);
		}
	}
	if (returnType != null) {
		Class<?> resolvedClass = returnType.resolve();
		if (resolvedClass != null && descriptor.getDependencyType().isAssignableFrom(resolvedClass)) {
			// Only use factory method metadata if the return type is actually expressive enough
			// for our dependency. Otherwise, the returned instance type may have matched instead
			// in case of a singleton instance having been registered with the container already.
			return returnType;
		}
	}
	return null;
}
  • QualifierAnnotationAutowireCandidateResolver:对@Qualifier的解析,功能是将qualifier注解要自动绑定的field或者参数和bean definition qualifier相匹配。同时也支持通过@value注解来绑定表达式的值。另外还支持JSR-330的javax.inject.Qualifier注解
  • ContextAnnotationAutowireCandidateResolver:对@Lazy的解析

@Required:依赖检查;@Autowired:自动装配(可完全代替基于xml的装配)
@Value(value = “SpEL表达式”) 或者 @Value(value = “#{message}”)
@Qualifier:限定描述符,用于细粒度选择候选者
@Inject:等价于默认的@Autowired,只是没有required属性(支持@Primary)

  • QualifierAnnotationAutowireCandidateResolver#getSuggestedValue:处理标注了@Value注解的情况
private Class<? extends Annotation> valueAnnotationType = Value.class;

// 拿到value注解的这个属性(若标注有@Value注解的话)
@Override
@Nullable
public Object getSuggestedValue(DependencyDescriptor descriptor) {
	Object value = findValue(descriptor.getAnnotations());
	if (value == null) {
		// 看看方法有没有标注
		MethodParameter methodParam = descriptor.getMethodParameter();
		if (methodParam != null) {
			value = findValue(methodParam.getMethodAnnotations());
		}
	}
	return value;
}

/**
 * Determine a suggested value from any of the given candidate annotations.
 */
@Nullable
protected Object findValue(Annotation[] annotationsToSearch) {
	// 寻找到@Value注解的value属性值
	AnnotationAttributes attr = AnnotatedElementUtils.getMergedAnnotationAttributes(
			AnnotatedElementUtils.forAnnotations(annotationsToSearch), this.valueAnnotationType);
	if (attr != null) {
		return extractValue(attr);
	}
	return null;
}

/**
 * Extract the value attribute from the given annotation.
 * @since 4.3
 */
protected Object extractValue(AnnotationAttributes attr) {
	Object value = attr.get(AnnotationUtils.VALUE);
	if (value == null) {
		throw new IllegalStateException("Value annotation must have a value attribute");
	}
	return value;
}

Aware 容器感知技术

Spring的依赖注入的最大亮点:就是你所有的Bean对Spring容器的存在是没有意识的(我们的Bean并不需要实现它的任何接口)。即你可以将你的容器替换成别的容器,例如Goggle Guice,这时Bean之间的耦合度很低。

但是在实际的项目中,我们不可避免的要用到Spring容器本身的功能资源,这时候Bean必须要意识到Spring容器的存在,才能调用Spring所提供的资源,这就是所谓的Spring Aware。其实Spring Aware本来就是Spring设计用来框架内部使用的,若使用了Spring Aware,你的Bean将会和Spring框架耦合。

Spring提供Aware接口能让Bean感知Spring容器的存在,即让Bean可以使用Spring容器所提供的资源,下面是它提供给我们的感知器,我们一一简单介绍即可

在这里插入图片描述

  • ApplicationContextAware:获取容器上下文
  • BeanClassLoaderAware:获取加载当前Bean的类加载器
  • BeanNameAware:获取当前Bean的名称
  • LoadTimeWeaverAware:可以接收一个指向载入时(编译时)时织入实例的引用,实现编译时代理,属于比较高端的。可参见AspectJWeavingEnabler
  • BootstrapContextAware:拿到资源适配器BootstrapContext上下文,如JCA,CCI
  • ServletConfigAware:获取到ServletConfig
  • ImportAware:获取到AnnotationMetadata等信息。这个挺重要的,比如AbstractCachingConfiguration、AbstractTransactionManagementConfiguration都通过实现这个接口来获取到了注解的属性们。比如@EnableAsync、EnableCaching等注解上的属性值。参考:Spring的@Import注解与ImportAware接口
  • EmbeddedValueResolverAware:能让我们拿到StringValueResolver这个处理器,这样我们就可以很好的处理配置文件的值了。我们可以做个性化处理(比如我们自己要书写一个属性获取的工具类之类的)
  • EnvironmentAware:拿到环境Environment
  • BeanFactoryAware:获取Bean Factory
  • NotificationPublisherAware:和JMX有关
  • ResourceLoaderAware:获取资源加载器ResourceLoader可以获得外部资源文件 比如它的:ResourceLoader#getResource方法
  • MessageSourceAware:获取国际化文本信息
  • ServletContextAware:获取ServletContext
  • ApplicationEventPublisher:拿到事件发布器

备注:在高版本的Spring容器中,这些基础组件基本上(大多数)都能直接Autowired了,比如ApplicationContext、BeanFactory等等,有时候使用起来更加的方便

ResolvableType:可解决的数据类型(更好的处理泛型)

Spring为我们提供的ResolvableType:它为java语言中的所有类型提供了相同的数据结构,其内部封装了一个java.lang.reflect.Type类型的对象。

ResolvableType为所有的java类型提供了统一的数据结构以及API ,换句话说,一个ResolvableType对象就对应着一种java类型。 下面我们看看具体的使用。

获取对象

ResolvableType所有构造函数都是私有的。我们不能直接new,只能使用其提供的静态方法进行类型获取。

public static ResolvableType forClass(@Nullable Class<?> clazz);
public static ResolvableType forRawClass(@Nullable Class<?> clazz);
public static ResolvableType forClass(Class<?> baseType, Class<?> implementationClass);
public static ResolvableType forMethodReturnType(Method method); //获取指定方法的返回值的类型
...见下面截图,非常非常多

在这里插入图片描述

部分方法:

  • getSuperType():获取直接父类型
  • getInterfaces():获取接口类型们
  • getGenerics():获取类型携带的泛型类型
  • resolve():Type对象到Class对象的转换(使用得非常多)
  • isInstance、isAssignableFrom
  • ResolvableType.forInstance 获取指定的实例的泛型信息

示例:

public static void main(String[] args) {
	ResolvableType type = ResolvableType.forClass(ArrayList.class);
	System.out.println(type); //java.util.ArrayList<?>
	System.out.println(type.getType()); //class java.util.ArrayList
	System.out.println(type.getComponentType()); //?
	System.out.println(type.getInterfaces()); //[Lorg.springframework.core.ResolvableType;@4b4523f8
	System.out.println(type.getSuperType()); //java.util.AbstractList<?>
	System.out.println(type.getRawClass()); //class java.util.ArrayList
	System.out.println(type.getSource()); //class java.util.ArrayList

	System.out.println(type.getGenerics()); //[Lorg.springframework.core.ResolvableType;@1f28c152

	System.out.println(type.resolve()); //class java.util.ArrayList
}

Spring还提供了一个专门处理泛型的工具类:GenericTypeResolver,非常的强大。

比如常用使用场景:this.genericType = (Class<T>) GenericTypeResolver.resolveTypeArgument(getClass(), GenericDaoSupport.class);

还有:GenericTypeResolver.resolveParameterType…方法

 

参考链接:

【小家Spring】Spring标准处理组件大合集(ParameterNameDiscoverer、AutowireCandidateResolver、ResolvableType。。。)

 

posted @ 2021-12-11 19:05  残城碎梦  阅读(595)  评论(0编辑  收藏  举报