欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解Spring注解@Autowired的實現(xiàn)原理和使用方法

 更新時間:2023年07月19日 11:59:27   作者:JAVA旭陽  
在使用Spring開發(fā)的時候,配置的方式主要有兩種,一種是xml的方式,另外一種是 java config的方式,在使用的過程中,我們使用最多的注解應(yīng)該就是@Autowired注解了,所以本文就給大家講講@Autowired注解是如何使用和實現(xiàn)的,需要的朋友可以參考下

如何使用@Autowired注解?

@Autowired注解應(yīng)用于構(gòu)造函數(shù),如以下示例所示:

@Component
public class BeanConfig{
    @Autowired
    private BeanConfig beanConfig;
    @Autowired
    public BeanConfig(BeanConfig beanConfig) {
        this.beanConfig = beanConfig;
    }
}

直接應(yīng)用于字段是我們使用最多的方式,但是從代碼層面使用構(gòu)造函數(shù)注入會更好。因為構(gòu)造器注入的方式,能夠保證注入的依賴不可變,并確保需要的依賴不為空。此外,構(gòu)造器注入的依賴總是能夠在返回客戶端(組件)代碼的時候保證完全初始化的狀態(tài)。

此外,還有以下幾種不太常用的方法,見下面的代碼:

 @Autowired
 private List<BeanConfig> beanConfigList;
 @Autowired
 private Set<BeanConfig> beanConfigSet;
 @Autowired
 private Map<String, BeanConfig> beanConfigMap;

雖然我們經(jīng)常使用這個注解,但是我們真的了解它的作用嗎?

首先從它的作用域來看,其實這個注解是屬于容器配置的Spring注解,其他屬于容器配置注解:@Required, @Primary, @Qualifier等。

其次,我們可以直接看字面意思,autowire,這個詞的意思就是自動裝配的意思。

自動裝配是什么意思?這個詞的本意是指在一些行業(yè)中用機(jī)器代替人自動完成一些需要裝配的任務(wù)。在Spring的世界里,自動組裝是指使用我們需要這個beanclass自動組裝Spring容器中的bean

所以這個注解作用的就是自動將Spring容器中的bean和我們需要這個bean一起使用的類組裝起來。

接下來,讓我們看看這個注解背后工作的原理。

如何實現(xiàn)@Autowired 注解?

Java注解實現(xiàn)的核心技術(shù)是反射。讓我們通過一些例子和自己實現(xiàn)一個注解來了解它的工作原理。

我們拿到target之后就可以用反射給他實現(xiàn)一個邏輯,這種邏輯在這些方法本身的邏輯之外,這讓我們想起proxy、aop等知識,我們相當(dāng)于為這些方法做了一個邏輯增強(qiáng)。

其實注解的實現(xiàn)主要邏輯大概就是這個思路??偨Y(jié)一下一般步驟如下:

  • 使用反射機(jī)制獲取類的Class對象。
  • 通過這個類對象,可以得到它的每一個方法方法,或者字段等。
  • Method、Field等類提供了類似getAnnotation的方法來獲取某個字段的所有注解。
  • 拿到注解后,我們可以判斷該注解是否是我們要實現(xiàn)的注解,如果是,則實現(xiàn)注解邏輯。

下面我們來實現(xiàn)這個邏輯,代碼如下:

public void postProcessProperties() throws Exception {
    Class<BeanConfig> beanConfigClass = BeanConfig.class;
	BeanConfig instance = beanConfigClass.newInstance();
	Field[] fields = beanConfigClass.getDeclaredFields();
	for (Field field : fields) {
    	// getAnnotation,判斷是否有Autowired 
    	Autowired autowired = field.getDeclaredAnnotation(Autowired.class);
    	if (autowired != null) {
        	String fileName = field.getName();
        	Class<?> declaringClass = field.getDeclaringClass();
        	Object bean = new Object();
        	field.setAccessible(true);
        	field.set(bean, instance);
    	}
	}
}

從上面的實現(xiàn)邏輯不難發(fā)現(xiàn),借助Java反射,我們可以直接獲取一個類中的所有方法,然后獲取方法上的注解。當(dāng)然,我們也可以獲取字段上的注解。在反射的幫助下,我們幾乎可以得到屬于一個類的任何東西。這樣,我們自己簡單做了一個實現(xiàn)。

知道了上面的知識,我們就不難想到,上面的注解雖然簡單,但是@Autowired和他最大的區(qū)別應(yīng)該只是注解的實現(xiàn)邏輯,其他的如使用反射獲取注解等步驟應(yīng)該是相同的。

接下來我們看在Spring中,@Autowired是如何實現(xiàn)的呢?

Spring中源碼解析

我們來看@Autowired在Spring源碼中是如何定義注解的,如下:

package org.springframework.beans.factory.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
    boolean required() default true;
}

閱讀代碼可以看出,@Autowired注解可以應(yīng)用于五類構(gòu)造方法,普通方法、參數(shù)、字段、注解,其保留策略是在運行時。

接下來我們看一Spring對這個注解的邏輯實現(xiàn)。

Spring源碼中,@Autowired注解位于包中org.springframework.beans.factory.annotation。經(jīng)過分析不難發(fā)現(xiàn),Spring對自動裝配注解的實現(xiàn)邏輯位于類:AutowiredAnnotationBeanPostProcessor。

核心處理代碼如下:

private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
    LinkedList<InjectionMetadata.InjectedElement> elements = new LinkedList<>();
     // 需要處理的目標(biāo)類
    Class<?> targetClass = clazz; 
    do {
        final LinkedList<InjectionMetadata.InjectedElement> currElements = new LinkedList<>();
         // 通過反射獲取本類的所有字段,并遍歷每個字段
            // 通過方法findAutowiredAnnotation遍歷每個字段使用的注解
            // 如果用autowired修飾,返回autowired相關(guān)屬性
        ReflectionUtils.doWithLocalFields(targetClass, field -> {
            AnnotationAttributes ann = findAutowiredAnnotation(field);
             // 檢查靜態(tài)方法上是否使用了自動裝配注解
            if (ann != null) {
                if (Modifier.isStatic(field.getModifiers())) {
                    if (logger.isWarnEnabled()) {
                        logger.warn("Autowired annotation is not supported on static fields: " + field);
                    }
                    return;
                }
                  // 判斷是否指定了required 
                boolean required = determineRequiredStatus(ann);
                currElements.add(new AutowiredFieldElement(field, required));
            }
        });
        //和上面的邏輯一樣,但是方法是通過反射來處理
        ReflectionUtils.doWithLocalMethods(targetClass, method -> {
            Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
            if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
                return;
            }
            AnnotationAttributes ann = findAutowiredAnnotation(bridgedMethod);
            if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
                if (Modifier.isStatic(method.getModifiers())) {
                    if (logger.isWarnEnabled()) {
                        logger.warn("Autowired annotation is not supported on static methods: " + method);
                    }
                    return;
                }
                if (method.getParameterCount() == 0) {
                    if (logger.isWarnEnabled()) {
                        logger.warn("Autowired annotation should only be used on methods with parameters: " +
                                    method);
                    }
                }
                boolean required = determineRequiredStatus(ann);
                PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
                currElements.add(new AutowiredMethodElement(method, required, pd));
            }
        });
         // @Autowired 修飾的注解可能不止一個
         // 所以都加入到currElements容器中一起處理
        elements.addAll(0, currElements);
        targetClass = targetClass.getSuperclass();
    }
        while (targetClass != null && targetClass != Object.class);
    return new InjectionMetadata(clazz, elements);
}

最后,此方法返回一個InjectionMetadata包含所有autowire注解的集合。

這個類由兩部分組成:

public InjectionMetadata(Class<?> targetClass, Collection<InjectedElement> elements) {
  this.targetClass = targetClass;
  this.injectedElements = elements;
}

一個是我們要處理的目標(biāo)類,一個是elements上面方法得到的集合。

有了目標(biāo)類和所有需要注入的元素,我們就可以實現(xiàn)自動裝配的依賴注入邏輯。實現(xiàn)方法如下。

@Override
public PropertyValues postProcessPropertyValues(
  PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {
  InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
  try {
    metadata.inject(bean, beanName, pvs);
  }
  catch (BeanCreationException ex) {
    throw ex;
  }
  catch (Throwable ex) {
    throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
  }
  return pvs;
}

它調(diào)用的inject方法就是定義在InjectionMetadata。

public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
  Collection<InjectedElement> checkedElements = this.checkedElements;
  Collection<InjectedElement> elementsToIterate =
    (checkedElements != null ? checkedElements : this.injectedElements);
  if (!elementsToIterate.isEmpty()) {
   for (InjectedElement element : elementsToIterate) {
    if (logger.isTraceEnabled()) {
     logger.trace("Processing injected element of bean '" + beanName + "': " + element);
    }
    element.inject(target, beanName, pvs);
   }
  }
 }
/**
 * Either this or {@link #getResourceToInject} needs to be overridden.
 */
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, getResourceToInject(target, requestingBeanName));
 }
 else {
  if (checkPropertySkipping(pvs)) {
   return;
  }
  try {
   Method method = (Method) this.member;
   ReflectionUtils.makeAccessible(method);
   method.invoke(target, getResourceToInject(target, requestingBeanName));
  }
  catch (InvocationTargetException ex) {
   throw ex.getTargetException();
  }
 }
}

上面代碼中,方法的參數(shù)getResourceToInject是要注入的名稱,bean這個方法的作用是根據(jù)名稱獲取bean。

以上就是@Autowire注解實現(xiàn)邏輯的完整解析。

下面是spring容器實現(xiàn)@Autowired自動注入的時序圖。

總結(jié)

本文講解了Spring中最常用的注解之一@Autowired, 平時我們可能都是使用屬性注入的,但是后續(xù)建議大家慢慢改變習(xí)慣,使用構(gòu)造器注入。同時也講解了這個注解背后的實現(xiàn)原理,希望對大家有幫助。

以上就是詳解Spring注解@Autowired的實現(xiàn)原理和使用方法的詳細(xì)內(nèi)容,更多關(guān)于Spring注解@Autowired的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • rabbitmq學(xué)習(xí)系列教程之消息應(yīng)答(autoAck)、隊列持久化(durable)及消息持久化

    rabbitmq學(xué)習(xí)系列教程之消息應(yīng)答(autoAck)、隊列持久化(durable)及消息持久化

    這篇文章主要介紹了rabbitmq學(xué)習(xí)系列教程之消息應(yīng)答(autoAck)、隊列持久化(durable)及消息持久化,本文通過示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-03-03
  • SpringBoot啟動后執(zhí)行方法的五種實現(xiàn)方式

    SpringBoot啟動后執(zhí)行方法的五種實現(xiàn)方式

    本文介紹了SpringBoot中五種在項目啟動后執(zhí)行方法的方式,包括實現(xiàn)CommandLineRunner和ApplicationRunner接口、實現(xiàn)ApplicationListener接口、使用@PostConstruct注解以及實現(xiàn)InitializingBean接口,每種方式都有其特點和適用場景
    2025-02-02
  • springboot整合Shiro

    springboot整合Shiro

    這篇文章主要介紹了SpringBoot整合Shiro一些方式,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-07-07
  • 模仿J2EE的session機(jī)制的App后端會話信息管理實例

    模仿J2EE的session機(jī)制的App后端會話信息管理實例

    下面小編就為大家分享一篇模仿J2EE的session機(jī)制的App后端會話信息管理實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2017-11-11
  • java maven項目如何讀取配置文件信息

    java maven項目如何讀取配置文件信息

    這篇文章主要介紹了java maven項目如何讀取配置文件信息,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • Java中final關(guān)鍵字詳解及實例

    Java中final關(guān)鍵字詳解及實例

    這篇文章主要介紹了Java中final關(guān)鍵字詳解及實例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • IntelliJ IDEA使用SVN分支的簡單介紹

    IntelliJ IDEA使用SVN分支的簡單介紹

    今天小編就為大家分享一篇關(guān)于IntelliJ IDEA使用SVN分支的簡單介紹,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2018-10-10
  • java并發(fā)編程專題(十一)----(JUC原子類)數(shù)組類型詳解

    java并發(fā)編程專題(十一)----(JUC原子類)數(shù)組類型詳解

    這篇文章主要介紹了JAVA JUC原子類 數(shù)組類型詳解的相關(guān)資料,文中示例代碼非常詳細(xì),幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下
    2020-07-07
  • 并發(fā)編程ConcurrentLinkedQueue示例詳解

    并發(fā)編程ConcurrentLinkedQueue示例詳解

    這篇文章主要為大家介紹了并發(fā)編程ConcurrentLinkedQueue使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • Java讀取文件及基于正則表達(dá)式的獲取電話號碼功能詳解

    Java讀取文件及基于正則表達(dá)式的獲取電話號碼功能詳解

    這篇文章主要介紹了Java讀取文件及基于正則表達(dá)式的獲取電話號碼功能,結(jié)合實例形式詳細(xì)分析了正則匹配操作的相關(guān)語法及電話號碼匹配的原理與實現(xiàn)技巧,需要的朋友可以參考下
    2017-09-09

最新評論