前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【小家Spring】Spring标准处理组件大合集(ParameterNameDiscoverer、AutowireCandidateResolver、ResolvableType。。。)

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

作者头像
YourBatman
发布2019-09-03 16:12:39
1.9K0
发布2019-09-03 16:12:39
举报
文章被收录于专栏:BAT的乌托邦BAT的乌托邦
前言

本篇博文更像是一个工具文章,在阅读Spring源码的时候,经常会遇见一些处理器、提供器之类的组件,有的时候不深入去理解它的含义,确实还读不下去了。

为了方便自己流畅的阅读下去,特开本文记录一些关键的,使用的Spring提供的处理组件,尽量的解释清楚它们的作用甚至原理,以便我们能更自由的阅读,甚至运为己用~

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

DefaultParameterNameDiscoverer:它其实就是个聚合的作用:
代码语言:javascript
复制
// 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
代码语言:javascript
复制
// 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提供了方法弥补了这个缺陷

代码语言:javascript
复制
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吧:

代码语言:javascript
复制
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));
        }
    }

}

输出为:

代码语言:javascript
复制
方法:main 参数为:[args]
方法:test 参数为:[]
方法:testArguments 参数为:[test, myInteger, booleanTest]

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

代码语言:javascript
复制
private static final ParameterNameDiscoverer parameterNameDiscoverer = new StandardReflectionParameterNameDiscoverer();

运行看看效果:也能除数一样的效果

代码语言:javascript
复制
方法:main 参数为:[args]
方法:test 参数为:[]
方法:testArguments 参数为:[test, myInteger, booleanTest]

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

1、比如是JDK8以上版本

2、必须编译的时候有带上参数:javac -parameters

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

若你把它去掉,你的结果会如下:(建议各位小伙伴都试试)

代码语言:javascript
复制
// 去掉这个参数后,获取到的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是否满足作为自动绑定的备选项

代码语言:javascript
复制
// 本接口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后才推出来的)

代码语言:javascript
复制
	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注解的情况

代码语言:javascript
复制
	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,只能使用其提供的静态方法进行类型获取

代码语言:javascript
复制
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 获取指定的实例的泛型信息

提供一个案例参考:

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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019年03月25日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • ParameterNameDiscoverer:获取方法参数名称的工具
    • DefaultParameterNameDiscoverer:它其实就是个聚合的作用:
      • PrioritizedParameterNameDiscoverer
        • StandardReflectionParameterNameDiscoverer:
          • LocalVariableTableParameterNameDiscoverer:
          • AutowireCandidateResolver:
          • Aware 容器感知技术
          • ResolvableType:可解决的数据类型(更好的处理泛型)
            • 获取对象
              • 总结
                相关产品与服务
                容器服务
                腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档