Spring全家桶源码解析

编程入门 行业动态 更新时间:2024-10-13 06:20:49

Spring<a href=https://www.elefans.com/category/jswz/34/1770131.html style=全家桶源码解析"/>

Spring全家桶源码解析

文章目录

  • 前言
  • 一、@Resource 作用:
  • 二、@Resource 源码实现:
    • 2.1 @Resource 注入点获取:
    • 2.2 @Resource 对注入点依赖注入:
  • 三、 总结


前言

Spring 中不仅可以使用Spring 包中的@Autowired 还可以使用java 层面提供的@Resource 进行依赖注入。


一、@Resource 作用:

@Resource注解是Java EE提供的一种用于进行依赖注入(Dependency Injection)的注解。它可以标注在字段、setter方法或构造函数上,用于指定需要注入的依赖对象。

@Resource注解的作用可以总结如下:

注入依赖对象:当使用@Resource注解标注在一个字段、setter方法或构造函数上时,它告诉Spring容器需要注入一个对应的依赖对象。

指定依赖对象名:可以通过name属性来指定依赖对象的名字。如果没有指定name属性,则默认使用字段名、方法名或构造函数参数名称作为依赖对象的名字。

解决依赖对象冲突:当有多个符合类型的依赖对象可用时,可以使用name属性来指定具体要注入的依赖对象名。

支持按照类型匹配和名称匹配:当没有指定name属性时,@Resource注解会首先按照字段类型或方法参数类型进行匹配,如果找到多个符合条件的依赖对象,会再根据字段名或方法名进行匹配。

需要注意的是,@Resource注解通常与Spring容器中的ApplicationContext一起使用。容器可以根据注解信息自动解析依赖关系,并将对应的依赖对象注入到被注解标注的字段、setter方法或构造函数中。

二、@Resource 源码实现:

2.1 @Resource 注入点获取:

同@Autowired 相同在spring 创建bean的过程中,通过applyMergedBeanDefinitionPostProcessors bean 定义的后置处理器来解析@Resource 注解并将解析的后属性和方法进行包装放入到集合中;

applyMergedBeanDefinitionPostProcessors 调用 CommonAnnotationBeanPostProcessor 中 postProcessMergedBeanDefinition方法 来负责 @Resource 注入点的工作;

public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName);// 通过 findResourceMetadata 寻找注入点InjectionMetadata metadata = this.findResourceMetadata(beanName, beanType, (PropertyValues)null);metadata.checkConfigMembers(beanDefinition);
}

findResourceMetadata 注入点寻找:

private InjectionMetadata findResourceMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {String cacheKey = StringUtils.hasLength(beanName) ? beanName : clazz.getName();InjectionMetadata metadata = (InjectionMetadata)this.injectionMetadataCache.get(cacheKey);if (InjectionMetadata.needsRefresh(metadata, clazz)) {synchronized(this.injectionMetadataCache) {metadata = (InjectionMetadata)this.injectionMetadataCache.get(cacheKey);if (InjectionMetadata.needsRefresh(metadata, clazz)) {if (metadata != null) {metadata.clear(pvs);}// 获取注入点metadata = this.buildResourceMetadata(clazz);this.injectionMetadataCache.put(cacheKey, metadata);}}}return metadata;
}

buildResourceMetadata 注入点寻找:

private InjectionMetadata buildResourceMetadata(Class<?> clazz) {if (!AnnotationUtils.isCandidateClass(clazz, resourceAnnotationTypes)) {return InjectionMetadata.EMPTY;} else {List<InjectedElement> elements = new ArrayList();Class targetClass = clazz;do {List<InjectedElement> currElements = new ArrayList();// 属性注入ReflectionUtils.doWithLocalFields(targetClass, (field) -> {if (webServiceRefClass != null && field.isAnnotationPresent(webServiceRefClass)) {if (Modifier.isStatic(field.getModifiers())) {throw new IllegalStateException("@WebServiceRef annotation is not supported on static fields");}currElements.add(new CommonAnnotationBeanPostProcessor.WebServiceRefElement(field, field, (PropertyDescriptor)null));} else if (ejbClass != null && field.isAnnotationPresent(ejbClass)) {if (Modifier.isStatic(field.getModifiers())) {throw new IllegalStateException("@EJB annotation is not supported on static fields");}currElements.add(new CommonAnnotationBeanPostProcessor.EjbRefElement(field, field, (PropertyDescriptor)null));} else if (field.isAnnotationPresent(Resource.class)) {// 存在@Resource 注解if (Modifier.isStatic(field.getModifiers())) {// 如果是static 属性则直接报错@ Autired 不会报错throw new IllegalStateException("@Resource annotation is not supported on static fields");}if (!this.ignoredResourceTypes.contains(field.getType().getName())) {// 不是忽略的,包装为ResourceElemen 的集合中进行返回currElements.add(new CommonAnnotationBeanPostProcessor.ResourceElement(field, field, (PropertyDescriptor)null));}}});// 方法注入处理ReflectionUtils.doWithLocalMethods(targetClass, (method) -> {// 桥接方法则获取真正执行的方法Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);if (BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {PropertyDescriptor pdx;if (webServiceRefClass != null && bridgedMethod.isAnnotationPresent(webServiceRefClass)) {if (Modifier.isStatic(method.getModifiers())) {throw new IllegalStateException("@WebServiceRef annotation is not supported on static methods");}if (method.getParameterCount() != 1) {throw new IllegalStateException("@WebServiceRef annotation requires a single-arg method: " + method);}pdx = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);currElements.add(new CommonAnnotationBeanPostProcessor.WebServiceRefElement(method, bridgedMethod, pdx));} else if (ejbClass != null && bridgedMethod.isAnnotationPresent(ejbClass)) {if (Modifier.isStatic(method.getModifiers())) {throw new IllegalStateException("@EJB annotation is not supported on static methods");}if (method.getParameterCount() != 1) {throw new IllegalStateException("@EJB annotation requires a single-arg method: " + method);}pdx = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);currElements.add(new CommonAnnotationBeanPostProcessor.EjbRefElement(method, bridgedMethod, pdx));} else if (bridgedMethod.isAnnotationPresent(Resource.class)) {// 方法上有@Resource 注解if (Modifier.isStatic(method.getModifiers())) {// 不能修饰在 static 方法上throw new IllegalStateException("@Resource annotation is not supported on static methods");}// 如果是方法,则只能传入一个参数// @Resource注解要求方法只有一个参数是因为它的设计初衷是用于进行简单的依赖注入,一次只注入一个依赖项。Class<?>[] paramTypes = method.getParameterTypes();if (paramTypes.length != 1) {// 多个入参报错throw new IllegalStateException("@Resource annotation requires a single-arg method: " + method);}if (!this.ignoredResourceTypes.contains(paramTypes[0].getName())) {PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);currElements.add(new CommonAnnotationBeanPostProcessor.ResourceElement(method, bridgedMethod, pd));}}}}});elements.addAll(0, currElements);targetClass = targetClass.getSuperclass();} while(targetClass != null && targetClass != Object.class);return InjectionMetadata.forElements(elements, clazz);}
}

ResourceElement 对注入点的包装:

public ResourceElement(Member member, AnnotatedElement ae, @Nullable PropertyDescriptor pd) {super(member, pd);Resource resource = (Resource)ae.getAnnotation(Resource.class);String resourceName = resource.name();Class<?> resourceType = resource.type();this.isDefaultName = !StringUtils.hasLength(resourceName);//  Resource 中是否定义name 属性if (this.isDefaultName) {// 没有定义 则获取 属性的名称resourceName = this.member.getName();// 如果是set 方法则截取set 以后的值,并将第一个字母小写if (this.member instanceof Method && resourceName.startsWith("set") && resourceName.length() > 3) {resourceName = Introspector.decapitalize(resourceName.substring(3));}} else if (CommonAnnotationBeanPostProcessor.this.embeddedValueResolver != null) {// 如果定义了则直接获取resourceName = CommonAnnotationBeanPostProcessor.this.embeddedValueResolver.resolveStringValue(resourceName);}// 自己定义了type 类型,则进行检查if (Object.class != resourceType) {// 指定的type 和属性的type /入参的参数 类型不一致则报错this.checkResourceType(resourceType);} else {// 没有定义则,获取属性对应的resourceType = this.getResourceType();}this.name = resourceName != null ? resourceName : "";this.lookupType = resourceType;String lookupValue = resource.lookup();this.mappedName = StringUtils.hasLength(lookupValue) ? lookupValue : resource.mappedName();Lazy lazy = (Lazy)ae.getAnnotation(Lazy.class);this.lazyLookup = lazy != null && lazy.value();
}

至此注入点的扫描和获取 完成,下一步则bean实例化之后,对属性注入时,通过遍历注入点来完成 属性注入;

2.2 @Resource 对注入点依赖注入:

@Resource 对注入点依赖注入,遍历所有的注入点,按照属性/方法 执行不同的解析逻辑,都进入到 autowireResource 方法中,完成bean 的获取,最终通过反射的方式,将bean 进行依赖注入;

在bean 的 populateBean 方法中 通过postProcessProperties 方法进行依赖注入,CommonAnnotationBeanPostProcessor 中的postProcessProperties 方法为 @Resource 依赖注入的入口;

public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {InjectionMetadata metadata = this.findResourceMetadata(beanName, bean.getClass(), pvs);try {// 依赖注入metadata.inject(bean, beanName, pvs);return pvs;} catch (Throwable var6) {throw new BeanCreationException(beanName, "Injection of resource dependencies failed", var6);}
}

inject set方法和 属性 依赖注入:

protected void inject(Object target, @Nullable String requestingBeanName, @Nullable PropertyValues pvs) throws Throwable {if (this.isField) {//属性注入Field field = (Field)this.member;// 属性是私有的设置强制访问ReflectionUtils.makeAccessible(field);// 反射方法为属性赋值field.set(target, this.getResourceToInject(target, requestingBeanName));} else {if (this.checkPropertySkipping(pvs)) {return;}try {// 获取方法Method method = (Method)this.member;// 方法 强制访问ReflectionUtils.makeAccessible(method);// 反射方法为入参赋值method.invoke(target, this.getResourceToInject(target, requestingBeanName));} catch (InvocationTargetException var5) {throw var5.getTargetException();}}}

ResourceElement .getResourceToInject 依赖注入:

protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) {// 是否有lazy 注解,有则生成代理对象,没有则直接获取真正的bean 对象return this.lazyLookup ? CommonAnnotationBeanPostProcessor.this.buildLazyResourceProxy(this, requestingBeanName) : CommonAnnotationBeanPostProcessor.this.getResource(this, requestingBeanName);
}

getResource 不是懒加载的bean :

protected Object getResource(CommonAnnotationBeanPostProcessor.LookupElement element, @Nullable String requestingBeanName) throws NoSuchBeanDefinitionException {String jndiName = null;if (StringUtils.hasLength(element.mappedName)) {jndiName = element.mappedName;} else if (this.alwaysUseJndiLookup) {jndiName = element.name;}if (jndiName != null) {if (this.jndiFactory == null) {throw new NoSuchBeanDefinitionException(element.lookupType, "No JNDI factory configured - specify the 'jndiFactory' property");} else {return this.jndiFactory.getBean(jndiName, element.lookupType);}} else if (this.resourceFactory == null) {throw new NoSuchBeanDefinitionException(element.lookupType, "No resource factory configured - specify the 'resourceFactory' property");} else {//从beanFactory 中获取beanreturn this.autowireResource(this.resourceFactory, element, requestingBeanName);}
}

autowireResource 获取bean:
获取bean的流程: 是否指定了name属性,如果定义直接根据name 去寻找bean ,如果没有定义name 名字,则先按照属性的名称去获取bean ,如果获取不到 则在按照属性的类型去获取bean(@ Resource 先按照 名称去获取了bean 的体现就在这里);

protected Object autowireResource(BeanFactory factory, CommonAnnotationBeanPostProcessor.LookupElement element, @Nullable String requestingBeanName) throws NoSuchBeanDefinitionException {String name = element.name;Object resource;Object autowiredBeanNames;if (factory instanceof AutowireCapableBeanFactory) {AutowireCapableBeanFactory beanFactory = (AutowireCapableBeanFactory)factory;// 属性的描述器DependencyDescriptor descriptor = element.getDependencyDescriptor();// 根据bean 的名字先进行获取if (this.fallbackToDefaultTypeMatch && element.isDefaultName && !factory.containsBean(name)) {// 如果没有指定 bean 的name 属性,并且beanFactory 也没有beanautowiredBeanNames = new LinkedHashSet();// 根据属性类型去寻找,这里resolveDependency  // 判断是否有@Value 注解 如果有就解析并且生成对象返回,没有@Value 继续走下一步// 看当前的属性,或者方法的入参,是有什么接收的,如果map,或者list 或者array 根据类型找到bean 并进行返回;// 如果是普通的注入 则进行优先级或者bean 名称的筛选,如果筛选后,没有得到bean则报错,筛选后得到bean 则进行返回resource = beanFactory.resolveDependency(descriptor, requestingBeanName, (Set)autowiredBeanNames, (TypeConverter)null);if (resource == null) {throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object");}} else {// beanFactory 有对应的bean 则直接获取,resource = beanFactory.resolveBeanByName(name, descriptor);autowiredBeanNames = Collections.singleton(name);}} else {resource = factory.getBean(name, element.lookupType);autowiredBeanNames = Collections.singleton(name);}if (factory instanceof ConfigurableBeanFactory) {ConfigurableBeanFactory beanFactory = (ConfigurableBeanFactory)factory;Iterator var11 = ((Set)autowiredBeanNames).iterator();while(var11.hasNext()) {String autowiredBeanName = (String)var11.next();if (requestingBeanName != null && beanFactory.containsBean(autowiredBeanName)) {beanFactory.registerDependentBean(autowiredBeanName, requestingBeanName);}}}return resource;
}

这里的resolveDependency 逻辑和@Autowired 相同,都是按照bean 的类型去获取:

  • 如果获取到一个则直接返回;
  • 如果获取到多个并且使用Map/List/Array 接收则直接返回,如果使用普通对象接收,在通过name 进行筛选,如果筛选后只有一个则正常返回,如果没有或者有多个则报错处理;
  • 如果没有胡获取到则报错处理;

三、 总结

@Resource 获取bean 的情况总结:

  • 没有指定name 和type :

  • 没有指定名称,指定type;先按照名称去找,如果找到则进行装配(如果和实际需要的类型不一致则会报错),如果找不到则按照类型取获取;

  • 指定name,不指定type:直接按照名字去获取,找到一个自动装配(如果和实际需要的类型不一致则会报错,找不到则报错

  • 同时指定name 和type:

更多推荐

Spring全家桶源码解析

本文发布于:2023-11-14 22:11:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1588637.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:全家   源码   Spring

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!