【小家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。然后它自己的方法getParameterNamesgetParameterNames都由注册进来的们去完成~~~~~~~方法都在它的父类:

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;
	}

}

那么接下来说说:StandardReflectionParameterNameDiscovererLocalVariableTableParameterNameDiscoverer

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里面拿的~
下面看一个Demo吧:

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的使用条件有两个
1、比如是JDK8以上版本
2、必须编译的时候有带上参数:javac -parameters

那么有小伙伴就疑问了,为何我上面编译没加参数也好使呢?这里面小伙伴务必注意了:如果你使用的idea的IDE,它里面默认编辑就带有此参数的:(Compiler-Java Compiler)
在这里插入图片描述
若你把它去掉,你的结果会如下:(建议各位小伙伴都试试)

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

好了,讲解完这个,讲视线继续移步到上面的自动注入吧。

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

AutowireCandidateResolver:

Strategy interface for determining whether a specific bean definition qualifies as an autowire candidate for a specific dependency.

策略接口,对特定的依赖,这个接口决定一个特定的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等信息。这个挺重要的,比如AbstractCachingConfigurationAbstractTransactionManagementConfiguration都通过实现这个接口来获取到了注解的属性们。比如@EnableAsyncEnableCaching等注解上的属性值 参考: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:可解决的数据类型(更好的处理泛型)

之前有有讲过JDK对泛型抽象的文章:【小家Java】你真的了解Java泛型参数吗?细说java.lang.reflect.Type(ParameterizedType、TypeVariable、WildcardType…)

今儿说说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是面向对象编程的典范工程,对封装、适配、抽象做得非常到位,希望这些东西我们也能学以致用~


关注A哥

AuthorA哥(YourBatman)
个人站点www.yourbatman.cn
E-mailyourbatman@qq.com
微 信fsx641385712
活跃平台
公众号BAT的乌托邦(ID:BAT-utopia)
知识星球BAT的乌托邦
每日文章推荐每日文章推荐

BAT的乌托邦

YourBatman CSDN认证博客专家 博客专家 专栏创作者 BAT的乌托邦
也许当我老了,也一样写代码。不为别的,只为爱好
公众号:BAT的乌托邦
亦可在这里和我交流:https://www.yourbatman.cn
©️2020 CSDN 皮肤主题: 代码科技 设计师:Amelia_0503 返回首页
实付 59.90元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值