真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

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

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

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

一、三種依賴注入方式

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

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

public interface UserService {

    public void UserInfo();
}

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

字段注入

Spring中通過(guò)@Autowired注解,可以完成注入。

public class ClientService {

    @Autowired
    private UserService userService;

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

字段注入是三種注入方式最簡(jiǎn)單、最常用的一種方式,但是也是最需要避免使用的一種方式。那為什么要避免使用呢?接下來(lái)進(jìn)行分析一下。

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

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

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

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

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

public class ClassA {
    
    @Autowired
    private ClassB classB;
    
}

public class ClassB {

    @Autowired
    private ClassA classA;
    
}

如上代碼顯然,ClassA和ClassB發(fā)生循環(huán)依賴。在Spring啟動(dòng)的時(shí)候不會(huì)發(fā)生錯(cuò)誤,但是在使用具體的某個(gè)類(lèi)時(shí)會(huì)報(bào)錯(cuò)。

構(gòu)造器注入

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

public class ClientService {
    
    private UserService userService;

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

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

通過(guò)構(gòu)造器注入可以解決對(duì)象的外部可見(jiàn)性的問(wèn)題,因?yàn)?code>userService是通過(guò)ClientService 構(gòu)造函數(shù)進(jìn)行注入的。基于構(gòu)造器注入,回顧一下之前循環(huán)依賴的問(wè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項(xiàng)目啟動(dòng)的時(shí)候,會(huì)拋出循環(huán)依賴異常,可以提醒開(kāi)發(fā)者避免使用循環(huán)依賴。但是構(gòu)造器注入也是有問(wèn)題的,當(dāng)構(gòu)造函數(shù)中存在較多的依賴對(duì)象時(shí),大量的構(gòu)造函數(shù)參數(shù)回訪代碼出現(xiàn)冗余。接下來(lái)就引入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)造器注入可讀性更強(qiáng),可以將多個(gè)實(shí)例對(duì)象通過(guò)多個(gè)Setter方法逐一進(jìn)行注入?;仡欀暗难h(huán)依賴問(wè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適合可選對(duì)象的注入;構(gòu)造方法適合強(qiáng)制對(duì)象的注入;字段注入避免使用。

二、依賴注入原理

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

Bean 注冊(cè)

在Spring中我們往往通過(guò)一個(gè)應(yīng)用的上下文(ApplicationContext)對(duì)象來(lái)操作各種Bean。

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);

xxxApplicationContext接口在Spring中就代表一個(gè)Spring IOC 容器,Spring中存在大量的ApplicationContext接口的實(shí)現(xiàn)類(lèi)。如果基于注解的配置方式,就使用AnnotationConfigApplicationContext 來(lái)初始化上下文容器對(duì)象。接下來(lái)進(jìn)入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ù)注解配置類(lèi)注冊(cè)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();
  }

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

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

    /**
     * Register one or more component classes to be processed.
     * 

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);     }

通過(guò)this.reader.register(componentClasses);可以看出,調(diào)用當(dāng)前對(duì)象reader里面的register方法,而reader實(shí)際上是AnnotatedBeanDefinitionReader工具類(lèi)來(lái)完成Bean的注冊(cè)。繼續(xù)點(diǎn)進(jìn)register方法:

/**
   * Register one or more component classes to be processed.
   * 

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會(huì)遍歷所有的componentClasses組件類(lèi),通過(guò)registerBean方法中的doRegisterBean方法完成Bean的注冊(cè)。進(jìn)入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  void doRegisterBean(Class beanClass, @Nullable String name,
      @Nullable Class[] qualifiers, @Nullable Supplier supplier,
      @Nullable BeanDefinitionCustomizer[] customizers) {
      
    // 將注解配置類(lèi)信息轉(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 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);
    // 注冊(cè) bean對(duì)象
    BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
  }

總的來(lái)看:

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

Bean 實(shí)例化

現(xiàn)在Spring IOC容器對(duì)Bean的創(chuàng)建過(guò)程并沒(méi)有完成,目前只是將Bean的定義加載到了容器中,但是可能容器本身已經(jīng)存在這些Bean的定義,所以需要使用refresh()方法刷新容器,回到最開(kāi)始進(jìn)入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ù)注解配置類(lèi)注冊(cè)Bean
    register(componentClasses);
    // 刷新容器
    refresh();
  }

接下來(lái)分析refresh方法,點(diǎn)擊進(jìn)入:

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

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

      ...

      try {
        ......

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

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

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

      finally {
        ......
      }
    }
  }

可以看出obtainFreshBeanFactory完成對(duì)Bean的注冊(cè)返回一個(gè)BeanFactory。而finishBeanFactoryInitialization方法真正完成Bean實(shí)例化的入口。真正完成實(shí)例化的方法為DefaultListableBeanFactory類(lèi)中的preInstantiateSingletons方法,進(jìn)入此方法:

@Override
  public void preInstantiateSingletons() throws BeansException {

    List 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);
        }
      }
    }

    ......
  }

進(jìn)入到getBean()方法:

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

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

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

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

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

/**
  * 此類(lèi)的中心方法:創(chuàng)建一個(gè)bean實(shí)例,
   * 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);
    }
  }

最后進(jìn)入到doCreateBean如下:

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

    
    Object exposedObject = bean;
    try {
      // 初始化Bean實(shí)例
      populateBean(beanName, mbd, instanceWrapper);
      // 執(zhí)行初始化bean實(shí)例回調(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注冊(cè)為一次性。
    try {
      registerDisposableBeanIfNecessary(beanName, bean, mbd);
    }
    catch (BeanDefinitionValidationException ex) {
      throw new BeanCreationException(
          mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
    }

    return exposedObject;
  }

總的來(lái)看:

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

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


新聞標(biāo)題:Spring依賴注入的方式有哪些及原理是什么
網(wǎng)頁(yè)地址:http://weahome.cn/article/igphjc.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部