当前位置: 首页 > 科技观察

我用过@Autowired,但是你知道它是如何实现的吗?

时间:2023-03-15 19:45:53 科技观察

前言在使用Spring开发时,主要有两种配置方式,一种是xml,一种是javaconfig方式。在使用javaconfig的过程中,我们不可避免的要和各种注解打交道。其中,我们使用最多的注解应该是@Autowired注解了。这个注解的目的是注入一个定义好的bean。那么,除了我们常用的属性注入方式之外,我们还有哪些方式可以使用这个注解呢?在代码层面是如何实现的?如何使用@Autowired注解?将@Autowired注解应用于构造函数,如下例所示:@ComponentpublicclassBeanConfig{@AutowiredprivateBeanConfigbeanConfig;@AutowiredpublicBeanConfig(BeanConfigbeanConfig){this.beanConfig=beanConfig;}}直接应用于字段是我们使用的大部分方法,但是从代码级别使用构造函数注入会更好。因为构造函数注入的方式,可以保证注入的依赖是不可变的,保证需要的依赖不为空。此外,当返回到客户端(组件)代码时,构造函数注入的依赖项始终保证处于完全初始化状态。另外还有几个不太常用的方法,看下面代码:@AutowiredprivateListbeanConfigList;@AutowiredprivateSetbeanConfigSet;@AutowiredprivateMapbeanConfigMap;虽然我们经常使用这个注解,但是我们真的了解它的作用吗?首先,从它的范围来看,其实这个注解属于容器配置的Spring注解,还有一些属于容器配置的注解:@Required、@Primary、@Qualifier等。其次,我们可以直接看字面意思,autowire,这个词的意思就是自动装配的意思。自动装配是什么意思?这个词的本义是指在一些行业中用机器代替人来自动完成一些需要组装的工作。在Spring的世界里,自动组装是指将Spring容器中的bean自动组装成我们需要这个bean的类。所以这个注解的作用就是自动把Spring容器中的bean和我们需要使用这个bean的类组装起来。接下来,让我们看看这个注解在幕后是如何工作的。如何实现@Autowired注解?Java注解实现的核心技术是反射。让我们通过一些示例并自己实现注释来了解它是如何工作的。我们拿到target之后,就可以用反射给他实现一个逻辑。这种逻辑在这些方法本身的逻辑之外。这让我们想起proxy、aop等知识。我们相当于对这些方法进行了逻辑上的增强。其实注解实现的主要逻辑大概就是这个思路。总结一下大致的步骤如下:利用反射机制获取类的Class对象。通过该类对象可以获取它的每一个方法,或者字段等。Method、Field等类提供了类似getAnnotation的方法来获取某个字段的所有注解。拿到注解后,我们可以判断该注解是否是我们要实现的注解,如果是,则实现注解逻辑。我们来实现这个逻辑,代码如下:publicvoidpostProcessProperties()throwsException{ClassbeanConfigClass=BeanConfig.class;BeanConfig实例=beanConfigClass.newInstance();Field[]fields=beanConfigClass.getDeclaredFields();for(Fieldfield:fields){//getAnnotation,判断是否有AutowiredAutowiredautowired=field.getDeclaredAnnotation(Autowired.class);if(autowired!=null){StringfileName=field.getName();类declaringClass=field.getDeclaringClass();对象bean=newObject();field.setAccessible(true);field.set(bean,实例);从上面的实现逻辑不难发现,借助Java反射,我们可以直接获取类中的一个All方法,然后获取方法上的注解。当然,我们也可以获得字段的注解。在反射的帮助下,我们几乎可以得到属于一个类的任何东西。这样,我们就简单的自己做了一个实现。知道了上面的知识,我们就不难想到,上面的注解虽然简单,但是@Autowired和他最大的区别应该是注解的实现逻辑。其他如使用反射获取注解等步骤应该相同。接下来我们看看@Autowired在Spring中是如何实现的?Spring源码分析下面看看@Autowired在Spring源码中是如何定义注解的,如下:导入java.lang.annotation.Documented;导入java.lang.annotation.ElementType;导入java.lang.annotation.Retention;导入java.lang.annotation.RetentionPolicy;导入java.lang.annotation.Target;@Target({ElementType.CONSTRUCTOR,ElementType.METHOD,ElementType.PARAMETER,ElementType.FIELD,ElementType.ANNOTATION_TYPE})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic@interfaceAutowired{booleanrequired()defaulttrue;}阅读代码,可以看出@Autowired注解可以应用于五类构造方法、常用方法、参数、字段、注解。保留策略在运行时。接下来我们看一下Spring对该注解的逻辑实现。在Spring源代码中,@Autowired注解位于包org.springframework.beans.factory.annotation中。经过分析不难发现,Spring对自动装配注解的实现逻辑位于类:AutowiredAnnotationBeanPostProcessor。核心处理代码如下://待处理的目标类ClasstargetClass=clazz;做{finalLinkedListcurrElements=newLinkedList<>();//通过反射获取本类的所有字段,并遍历每个字段//通过方法findAutowiredAnnotation遍历每个字段使用的注解//如果修改为autowired,则返回autowired相关属性ReflectionUtils.doWithLocalFields(targetClass,field->{AnnotationAttributesann=findAutowiredAnnotation(field);//检查静态方法是否使用了自动装配注解if(ann!=null){if(Modifier.isStatic(field.getModifiers())){if(logger.isWarnEnabled()){logger.warn("静态字段不支持自动装配注解:"+field);}return;}//判断是否指定了requiredbooleanrequired=determineRequiredStatus(ann);currElements.add(newAutowiredFieldElement(field,required));}});//同上逻辑,但是方法是通过反射来处理ReflectionUtils。doWithLocalMethods(targetClass,method->{MethodbridgedMethod=BridgeMethodResolver.findBridgedMethod(method);如果(!BridgeMethodResolver.isVisibilityBridgeMethodPair(method,bridgedMethod)){return;}AnnotationAttributesann=findAutowiredAnnotation(!&=bridgedMethodAnnotation(!.equals(ClassUtils.getMostSpecificMethod(method,clazz))){if(Modifier.isStatic(method.getModifiers())){if(logger.isWarnEnabled()){logger.warn(“静态方法不支持自动装配注释:”+方法);}返回rn;}if(method.getParameterCount()==0){if(logger.isWarnEnabled()){logger.warn("Autowired注解只能用在带有参数的方法上:"+method);}}booleanrequired=determineRequiredStatus(ann);PropertyDescriptorpd=BeanUtils.findPropertyForMethod(bridgedMethod,clazz);currElements.add(新的AutowiredMethodElement(方法,必需的,pd));}});//@Autowired修改的注解可能不止一个//所以都加入到currElements容器中处理elements.addAll(0,currElements);targetClass=targetClass.getSuperclass();}while(targetClass!=null&&targetClass!=Object.class);returnnewInjectionMetadata(clazz,elements);}最后,该方法返回包含所有自动装配注解的InjectionMetadata集合这个类由两部分组成:publicInjectionMetadata(ClasstargetClass,Collectionelements){this.targetClass=targetClass;this.injectedElements=elements;}一个是我们要处理的目标类,一个是上面elements获取集合的方法。有了目标类和所有需要注入的元素,我们就可以实现自动装配的依赖注入逻辑了。实现方法如下。@OverridepublicPropertyValuespostProcessPropertyValues(PropertyValuespvs,PropertyDescriptor[]pds,Objectbean,StringbeanName)throwsBeanCreationException{InjectionMetadatametadata=findAutowiringMetadata(beanName,bean.getClass(),pvs);尝试{metadata.inject(bean,beanName,pvs);}catch(BeanCreationExceptionex){抛出ex;}catch(Throwableex){thrownewBeanCreationException(beanName,"自动装配依赖注入失败",ex);}returnpvs;}它调用的inject方法定义在InjectionMetadata中。publicvoidinject(Objecttarget,@NullableStringbeanName,@NullablePropertyValuespvs)throwsThrowable{CollectioncheckedElements=this.checkedElements;集合elementsToIterate=(checkedElements!=null?checkedElements:this.injectedElements);if(!elementsToIterate.isEmpty()){for(InjectedElementelement:elementsToIterate){if(logger.isTraceEnabled()){logger.trace("正在处理bean的注入元素'"+beanName+"':"+element);}element.inject(target,beanName,pvs);}}}/***这个或{@link#getResourceToInject}需要被覆盖。*/protectedvoidinject(Objecttarget,@NullableStringrequestingBeanName,@NullablePropertyValuespvs)throwsThrowable{if(this.isField){Fieldfield=(Field)this.member;ReflectionUtils.makeAccessible(字段);field.set(target,getResourceToInject(target,requestingBeanName));}否则{如果(checkPropertySkipping(pvs)){返回;}try{Methodmethod=(Method)this.member;ReflectionUtils.makeAccessible(方法);method.invoke(target,getResourceToInject(target,requestingBeanName));}catch(InvocationTargetExceptionex){抛出ex.getTargetException();}}}上面代码中,方法参数getResourceToInject为要注入的名称,bean方法的作用是根据名称获取bean。以上就是对@Autowire注解实现逻辑的完整解析。下面是spring容器实现@Autowired自动注入的时序图。总结本文讲解@Autowired,Spring中最常用的注解之一。通常,我们可能会使用属性注入,但我们建议您慢慢改变习惯,使用构造函数注入。同时也解释了这个注解背后的实现原理,希望对大家有所帮助。