欧美一区二区三区老妇人-欧美做爰猛烈大尺度电-99久久夜色精品国产亚洲a-亚洲福利视频一区二区

Spring依賴注入的方式有哪些及原理是什么

本文小編為大家詳細(xì)介紹“Spring依賴注入的方式有哪些及原理是什么”,內(nèi)容詳細(xì),步驟清晰,細(xì)節(jié)處理妥當(dāng),希望這篇“Spring依賴注入的方式有哪些及原理是什么”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學(xué)習(xí)新知識吧。

我們提供的服務(wù)有:成都網(wǎng)站制作、做網(wǎng)站、外貿(mào)營銷網(wǎng)站建設(shè)、微信公眾號開發(fā)、網(wǎng)站優(yōu)化、網(wǎng)站認(rèn)證、海港ssl等。為1000+企事業(yè)單位解決了網(wǎng)站和推廣的問題。提供周到的售前咨詢和貼心的售后服務(wù),是有科學(xué)管理、有技術(shù)的海港網(wǎng)站制作公司

一、三種依賴注入方式

在Spring中提供了三種實現(xiàn)依賴注入的方式:字段注入、構(gòu)造器注入、Setter方法注入

首先我們先創(chuàng)建一個Service層的接口以及對應(yīng)的實現(xiàn)類,基于以下實現(xiàn)類來實現(xiàn)依賴注入的方式:

public interface UserService {

    public void UserInfo();
}

public class UserServiceImpl implements UserService{
    @Override
    public void UserInfo() {
        System.out.println("UserInfo to do ...");
    }
}

字段注入

Spring中通過@Autowired注解,可以完成注入。

public class ClientService {

    @Autowired
    private UserService userService;

    public void UserInfo(){
        userService.UserInfo();
    }
}

字段注入是三種注入方式最簡單、最常用的一種方式,但是也是最需要避免使用的一種方式。那為什么要避免使用呢?接下來進行分析一下。

ClientService 類中,我們定義了一個私有化的變量userService來注入該接口的實例,但是這個實例只能在ClientService 類中訪問到,脫離容器環(huán)境無法訪問到。

ClientService clientService = new ClientService();
        clientService.UserInfo();

Spring依賴注入的方式有哪些及原理是什么

如上圖執(zhí)行結(jié)果拋出NullPointerException空指針異常,原因很簡單無法在ClientService 類的外部實例化UserService 對象。采用字段注入的話,類與容器的耦合度較高,無法脫離容器使用目標(biāo)對象。這就得出了避免使用字段注入的第一個原因:對象的外部可見性較差

避免使用字段注入第二個原因:可能導(dǎo)致潛在的循環(huán)依賴。循環(huán)依賴指的是兩個類之間互相進行注入。代碼如下

public class ClassA {
    
    @Autowired
    private ClassB classB;
    
}

public class ClassB {

    @Autowired
    private ClassA classA;
    
}

如上代碼顯然,ClassA和ClassB發(fā)生循環(huán)依賴。在Spring啟動的時候不會發(fā)生錯誤,但是在使用具體的某個類時會報錯。

構(gòu)造器注入

構(gòu)造器注入就是使用類的構(gòu)造函數(shù)來完成對象的注入。

public class ClientService {
    
    private UserService userService;

    @Autowired
    public ClientService(UserService userService) {
        this.userService = userService;
    }

    public void UserInfo(){
        userService.UserInfo();
    }
}

通過構(gòu)造器注入可以解決對象的外部可見性的問題,因為userService是通過ClientService 構(gòu)造函數(shù)進行注入的。基于構(gòu)造器注入,回顧一下之前循環(huán)依賴的問題。代碼如下

public class ClassA {
    
    private ClassB classB;
    
    @Autowired
    public ClassA(ClassB classB) {
        this.classB = classB;
    }
}

public class ClassB {

    private ClassA classA;

    @Autowired
    public ClassB(ClassA classA) {
        this.classA = classA;
    }
}

在Spring項目啟動的時候,會拋出循環(huán)依賴異常,可以提醒開發(fā)者避免使用循環(huán)依賴。但是構(gòu)造器注入也是有問題的,當(dāng)構(gòu)造函數(shù)中存在較多的依賴對象時,大量的構(gòu)造函數(shù)參數(shù)回訪代碼出現(xiàn)冗余。接下來就引入Setter方法注入

Setter注入

Setter方法注入代碼如下

public class ClientService {

    private UserService userService;

    @Autowired
    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    public void UserInfo(){
        userService.UserInfo();
    }
}

Setter注入相比于構(gòu)造器注入可讀性更強,可以將多個實例對象通過多個Setter方法逐一進行注入。回顧之前的循環(huán)依賴問題。代碼如下

public class ClassA {

    private ClassB classB;

    @Autowired
    public void setClassB(ClassB classB) {
        this.classB = classB;
    }
}


public class ClassB {

    private ClassA classA;

    @Autowired
    public void setClassA(ClassA classA) {
        this.classA = classA;
    }
}

在ClassA 和ClassB 作用域都為單例bean的前提下,代碼正常執(zhí)行。

總結(jié):Setter適合可選對象的注入;構(gòu)造方法適合強制對象的注入;字段注入避免使用。

二、依賴注入原理

前面介紹完依賴注入的三種實現(xiàn)方式,接下來結(jié)合Spring源碼深入的了解下依賴注入的原理,通過Bean 注冊和Bean 實例化兩個模塊進行闡述。

Bean 注冊

在Spring中我們往往通過一個應(yīng)用的上下文(ApplicationContext)對象來操作各種Bean。

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);

xxxApplicationContext接口在Spring中就代表一個Spring IOC 容器,Spring中存在大量的ApplicationContext接口的實現(xiàn)類。如果基于注解的配置方式,就使用AnnotationConfigApplicationContext 來初始化上下文容器對象。接下來進入AnnotationConfigApplicationContext的源碼,查看其構(gòu)造函數(shù)如下:

/**
   * Create a new AnnotationConfigApplicationContext, deriving bean definitions
   * from the given component classes and automatically refreshing the context.
   * @param componentClasses one or more component classes — for example,
   * {@link Configuration @Configuration} classes
   */
  public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
    this();
    // 根據(jù)注解配置類注冊Bean
    register(componentClasses);
    // 刷新容器
    refresh();
  }

  /**
   * Create a new AnnotationConfigApplicationContext, scanning for components
   * in the given packages, registering bean definitions for those components,
   * and automatically refreshing the context.
   * @param basePackages the packages to scan for component classes
   */
  public AnnotationConfigApplicationContext(String... basePackages) {
    this();
    // 根據(jù)包路徑掃描Bean
    scan(basePackages);
    // 刷新容器
    refresh();
  }

通過以上兩個構(gòu)造函數(shù)可以看出,一個是根據(jù)注解配置類注冊Bean,另一個通過包路徑掃描Bean。點擊進入register方法:

//---------------------------------------------------------------------
    // 注解ConfigRegistry的實現(xiàn)
    // Implementation of AnnotationConfigRegistry
    //---------------------------------------------------------------------

    /**
     * Register one or more component classes to be processed.
     * <p>Note that {@link #refresh()} must be called in order for the context
     * to fully process the new classes.
     * @param componentClasses one or more component classes — for example,
     * {@link Configuration @Configuration} classes
     * @see #scan(String...)
     * @see #refresh()
     */
    @Override
    public void register(Class<?>... componentClasses) {
        Assert.notEmpty(componentClasses, "At least one component class must be specified");
        this.reader.register(componentClasses);
    }

通過this.reader.register(componentClasses);可以看出,調(diào)用當(dāng)前對象reader里面的register方法,而reader實際上是AnnotatedBeanDefinitionReader工具類來完成Bean的注冊。繼續(xù)點進register方法:

/**
   * Register one or more component classes to be processed.
   * <p>Calls to {@code register} are idempotent; adding the same
   * component class more than once has no additional effect.
   * @param componentClasses one or more component classes,
   * e.g. {@link Configuration @Configuration} classes
   */
  public void register(Class<?>... componentClasses) {
    for (Class<?> componentClass : componentClasses) {
      registerBean(componentClass);
    }
  }
  
  /**
   * Register a bean from the given bean class, deriving its metadata from
   * class-declared annotations.
   * @param beanClass the class of the bean
   */
  public void registerBean(Class<?> beanClass) {
    doRegisterBean(beanClass, null, null, null, null);
  }

AnnotatedBeanDefinitionReader會遍歷所有的componentClasses組件類,通過registerBean方法中的doRegisterBean方法完成Bean的注冊。進入doRegisterBean

/**
   * Register a bean from the given bean class, deriving its metadata from
   * class-declared annotations.
   * @param beanClass the class of the bean
   * @param name an explicit name for the bean
   * @param supplier a callback for creating an instance of the bean
   * (may be {@code null})
   * @param qualifiers specific qualifier annotations to consider, if any,
   * in addition to qualifiers at the bean class level
   * @param customizers one or more callbacks for customizing the factory's
   * {@link BeanDefinition}, e.g. setting a lazy-init or primary flag
   * @since 5.0
   */
  private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name,
      @Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier,
      @Nullable BeanDefinitionCustomizer[] customizers) {
      
    // 將注解配置類信息轉(zhuǎn)換成一種 BeanDefinition
    AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
    if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
      return;
    }

    abd.setInstanceSupplier(supplier);  

    // 獲取bean的作用域元數(shù)據(jù)
    ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
    
    // 將bean的作用域?qū)懟?nbsp;BeanDefinition
    abd.setScope(scopeMetadata.getScopeName());
    
    // 生成 beanName
    String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
    
    // 解析AnnotatedGenericBeanDefinition 中的 @lazy 和 @Primary注解
    AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
    
     // 處理@Qualifier 注解
    if (qualifiers != null) {
      for (Class<? extends Annotation> qualifier : qualifiers) {
        if (Primary.class == qualifier) {
          // 如果設(shè)置了@Primary注解,設(shè)置當(dāng)前bean為首選bean
          abd.setPrimary(true);
        }
        else if (Lazy.class == qualifier) {
          // 如果設(shè)置了@lazy注解,則設(shè)置當(dāng)前bean為延遲加載模式
          abd.setLazyInit(true);
        }
        else {
          abd.addQualifier(new AutowireCandidateQualifier(qualifier));
        }
      }
    }
    if (customizers != null) {
      for (BeanDefinitionCustomizer customizer : customizers) {
        customizer.customize(abd);
      }
    }

    BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
    definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
    // 注冊 bean對象
    BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
  }

總的來看:

① 首先需要構(gòu)造描述bean實例化信息的BeanDefinition對象,需要將注解配置類信息轉(zhuǎn)化為AnnotatedGenericBeanDefinition 類型,此處的AnnotatedGenericBeanDefinition 就是一種BeanDefinition類型,包含了Bean的構(gòu)造函數(shù)參數(shù),屬性值以及添加的注解信息。
② 設(shè)置BeanDefinition屬性,完成對@Scope、@Lazy、@Primary等注解的處理
③ 最后通過registerBeanDefinition()方法完成Bean的注冊。

Bean 實例化

現(xiàn)在Spring IOC容器對Bean的創(chuàng)建過程并沒有完成,目前只是將Bean的定義加載到了容器中,但是可能容器本身已經(jīng)存在這些Bean的定義,所以需要使用refresh()方法刷新容器,回到最開始進入AnnotationConfigApplicationContext的源碼,查看其構(gòu)造函數(shù)如下:

/**
   * Create a new AnnotationConfigApplicationContext, deriving bean definitions
   * from the given component classes and automatically refreshing the context.
   * @param componentClasses one or more component classes — for example,
   * {@link Configuration @Configuration} classes
   */
  public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
    this();
    // 根據(jù)注解配置類注冊Bean
    register(componentClasses);
    // 刷新容器
    refresh();
  }

接下來分析refresh方法,點擊進入:

@Override
  public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
      ...

      // 提取配置信息,注冊到BeanFactory中
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      ...

      try {
        ......

        // 初始化所有的單例 bean
        finishBeanFactoryInitialization(beanFactory);

        // Last step: publish corresponding event.
        finishRefresh();
      }

      catch (BeansException ex) {
        ......
      }

      finally {
        ......
      }
    }
  }

可以看出obtainFreshBeanFactory完成對Bean的注冊返回一個BeanFactory。而finishBeanFactoryInitialization方法真正完成Bean實例化的入口。真正完成實例化的方法為DefaultListableBeanFactory類中的preInstantiateSingletons方法,進入此方法:

@Override
  public void preInstantiateSingletons() throws BeansException {

    List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

    // 觸發(fā)所有非懶加載的單例Bean的初始化操作
    for (String beanName : beanNames) {
      RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
      if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
        if (isFactoryBean(beanName)) {
          ......
        }
        else {
        // 獲取Bean
          getBean(beanName);
        }
      }
    }

    ......
  }

進入到getBean()方法:

//---------------------------------------------------------------------
  // Implementation of BeanFactory interface
  //---------------------------------------------------------------------

  @Override
  public Object getBean(String name) throws BeansException {
    return doGetBean(name, null, null, false);
  }

Bean的初始化過程就在這個方法中。在當(dāng)前的抽象類AbstractBeanFactory中有一個抽象方法createBean如下:

protected abstract Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
      throws BeanCreationException;

在Spring中實現(xiàn)這個抽象方法的唯一BeanFactory是AbstractAutowireCapableBeanFactory,真正完成Bean創(chuàng)建是在doCreateBean:

/**
  * 此類的中心方法:創(chuàng)建一個bean實例,
   * Central method of this class: creates a bean instance,
   * populates the bean instance, applies post-processors, etc.
   * @see #doCreateBean
   */
  @Override
  protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
      throws BeanCreationException {

    ......

    try {
    // 真正創(chuàng)建Bean
      Object beanInstance = doCreateBean(beanName, mbdToUse, args);
      if (logger.isTraceEnabled()) {
        logger.trace("Finished creating instance of bean '" + beanName + "'");
      }
      return beanInstance;
    }
    catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
      // A previously detected exception with proper bean creation context already,
      // or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
      throw ex;
    }
    catch (Throwable ex) {
      throw new BeanCreationException(
          mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
    }
  }

最后進入到doCreateBean如下:

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
      throws BeanCreationException {
    ......
    // 初始化一個bean
    if (instanceWrapper == null) {
      instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
    ......

    
    Object exposedObject = bean;
    try {
      // 初始化Bean實例
      populateBean(beanName, mbd, instanceWrapper);
      // 執(zhí)行初始化bean實例回調(diào)
      exposedObject = initializeBean(beanName, exposedObject, mbd);
    }
    catch (Throwable ex) {
      if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
        throw (BeanCreationException) ex;
      }
      else {
        throw new BeanCreationException(
            mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
      }
    }

    ......

    // 將bean注冊為一次性。
    try {
      registerDisposableBeanIfNecessary(beanName, bean, mbd);
    }
    catch (BeanDefinitionValidationException ex) {
      throw new BeanCreationException(
          mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
    }

    return exposedObject;
  }

總的來看:

① createBeanInstance方法用于根據(jù)配置生成具體的Bean,最終通過反射方法實現(xiàn),執(zhí)行完后Bean已經(jīng)被創(chuàng)建,但是不完整,沒有屬性的注入。
② populateBean方法用于實現(xiàn)屬性的自動注入,包含byName、byType、@Autowired、@Value屬性的設(shè)置,執(zhí)行完之后Bean就是完整的。
③ initializeBean方法是一種擴展性的機制,用于Bean初始化完成后的一些定制化操作。

讀到這里,這篇“Spring依賴注入的方式有哪些及原理是什么”文章已經(jīng)介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領(lǐng)會,如果想了解更多相關(guān)內(nèi)容的文章,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。

新聞名稱:Spring依賴注入的方式有哪些及原理是什么
URL分享:http://www.chinadenli.net/article22/igphjc.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站建設(shè)網(wǎng)站導(dǎo)航品牌網(wǎng)站設(shè)計網(wǎng)站營銷外貿(mào)網(wǎng)站建設(shè)微信小程序

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

成都定制網(wǎng)站網(wǎng)頁設(shè)計