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

Spring bean的實例化和IOC依賴注入詳解

 更新時間:2019年03月13日 11:55:34   作者:清幽之地  
這篇文章主要介紹了Spring bean的實例化和IOC依賴注入詳解,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

前言

我們知道,IOC是Spring的核心。它來負責(zé)控制對象的生命周期和對象間的關(guān)系。

舉個例子,我們?nèi)绾蝸碚覍ο蟮哪兀砍R姷那闆r是,在路上要到處去看哪個MM既漂亮身材又好,符合我們的口味。就打聽她們的電話號碼,制造關(guān)聯(lián)想辦法認識她們,然后...這里省略N步,最后談戀愛結(jié)婚。

IOC在這里就像婚介所,里面有很多適婚男女的資料,如果你有需求,直接告訴它你需要個什么樣的女朋友就好了。它會給我們提供一個MM,直接談戀愛結(jié)婚,完美!

下面就來看Spring是如何生成并管理這些對象的呢?

1、方法入口
org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons()方法是今天的主角,一切從它開始。

 public void preInstantiateSingletons() throws BeansException {
    //beanDefinitionNames就是上一節(jié)初始化完成后的所有BeanDefinition的beanName
    List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);
    for (String beanName : beanNames) {
      RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
      if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
        //getBean是主力中的主力,負責(zé)實例化Bean和IOC依賴注入
        getBean(beanName);
      }
    }
  }

2、Bean的實例化

在入口方法getBean中,首先調(diào)用了doCreateBean方法。第一步就是通過反射實例化一個Bean。

  protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
    // Instantiate the bean.
    BeanWrapper instanceWrapper = null;
    if (mbd.isSingleton()) {
      instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
    }
    if (instanceWrapper == null) {
      //createBeanInstance就是實例化Bean的過程,無非就是一些判斷加反射,最后調(diào)用ctor.newInstance(args);
      instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
  }

3、Annotation的支持

在Bean實例化完成之后,會進入一段后置處理器的代碼。從代碼上看,過濾實現(xiàn)了MergedBeanDefinitionPostProcessor接口的類,調(diào)用其postProcessMergedBeanDefinition()方法。都是誰實現(xiàn)了MergedBeanDefinitionPostProcessor接口呢?我們重點看三個

  • AutowiredAnnotationBeanPostProcessor
  • CommonAnnotationBeanPostProcessor
  • RequiredAnnotationBeanPostProcessor

記不記得在Spring源碼分析(一)Spring的初始化和XML這一章節(jié)中,我們說Spring對annotation-config標簽的支持,注冊了一些特殊的Bean,正好就包含上面這三個。下面來看它們偷偷做了什么呢?

從方法名字來看,它們做了相同一件事,加載注解元數(shù)據(jù)。方法內(nèi)部又做了相同的兩件事

ReflectionUtils.doWithLocalFields(targetClass, new ReflectionUtils.FieldCallback() 
ReflectionUtils.doWithLocalMethods(targetClass, new ReflectionUtils.MethodCallback()

看方法的參數(shù),targetClass就是Bean的Class對象。接下來就可以獲取它的字段和方法,判斷是否包含了相應(yīng)的注解,最后轉(zhuǎn)成InjectionMetadata對象,下面以一段偽代碼展示處理過程。

  public static void main(String[] args) throws ClassNotFoundException {
    Class<?> clazz = Class.forName("com.viewscenes.netsupervisor.entity.User");
    Field[] fields = clazz.getFields();
    Method[] methods = clazz.getMethods();

    for (int i = 0; i < fields.length; i++) {
      Field field = fields[i];
      if (field.isAnnotationPresent(Autowired.class)) {
        //轉(zhuǎn)換成AutowiredFieldElement對象,加入容器
      }
    }
    for (int i = 0; i < methods.length; i++) {
      Method method = methods[i];
      if (method.isAnnotationPresent(Autowired.class)) {
        //轉(zhuǎn)換成AutowiredMethodElement對象,加入容器
      }
    }
    return new InjectionMetadata(clazz, elements);
  }

InjectionMetadata對象有兩個重要的屬性:targetClass ,injectedElements,在注解式的依賴注入的時候重點就靠它們。

  public InjectionMetadata(Class<?> targetClass, Collection<InjectedElement> elements) {
    //targetClass是Bean的Class對象
    this.targetClass = targetClass; 
    //injectedElements是一個InjectedElement對象的集合
    this.injectedElements = elements;
  }
  //member是成員本身,字段或者方法
  //pd是JDK中的內(nèi)省機制對象,后面的注入屬性值要用到
  protected InjectedElement(Member member, PropertyDescriptor pd) {
    this.member = member;
    this.isField = (member instanceof Field);
    this.pd = pd;
  }

說了這么多,最后再看下源碼里面是什么樣的,以Autowired 為例。

ReflectionUtils.doWithLocalFields(targetClass, new ReflectionUtils.FieldCallback() {
  @Override
  public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
    AnnotationAttributes ann = findAutowiredAnnotation(field);
    if (ann != null) {
      if (Modifier.isStatic(field.getModifiers())) {
        if (logger.isWarnEnabled()) {
          logger.warn("Autowired annotation is not supported on static fields: " + field);
        }
        return;
      }
      boolean required = determineRequiredStatus(ann);
      currElements.add(new AutowiredFieldElement(field, required));
    }
  }
});

ReflectionUtils.doWithLocalMethods(targetClass, new ReflectionUtils.MethodCallback() {
  @Override
  public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
    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.getParameterTypes().length == 0) {
        if (logger.isWarnEnabled()) {
          logger.warn("Autowired annotation should be used on methods with parameters: " + method);
        }
      }
      boolean required = determineRequiredStatus(ann);
      PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
      currElements.add(new AutowiredMethodElement(method, required, pd));
    }
  }
});

4、依賴注入

前面完成了在doCreateBean()方法Bean的實例化,接下來就是依賴注入。

Bean的依賴注入有兩種方式,一種是配置文件,一種是注解式。

4.1、 注解式的注入過程

在上面第3小節(jié),Spring已經(jīng)過濾了Bean實例上包含@Autowired、@Resource等注解的Field和Method,并返回了包含Class對象、內(nèi)省對象、成員的InjectionMetadata對象。還是以@Autowired為例,這次調(diào)用到AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues()。

首先拿到InjectionMetadata對象,再判斷里面的InjectedElement集合是否為空,也就是說判斷在Bean的字段和方法上是否包含@Autowired。然后調(diào)用InjectedElement.inject()。InjectedElement有兩個子類AutowiredFieldElement、AutowiredMethodElement,很顯然一個是處理Field,一個是處理Method。

4.1.1 AutowiredFieldElement

如果Autowired注解在字段上,它的配置是這樣。

public class User { 
  @Autowired
  Role role;
}
protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
  //以User類中的@Autowired Role role為例,這里的field就是
  //public com.viewscenes.netsupervisor.entity.Role com.viewscenes.netsupervisor.entity.User.role
  Field field = (Field) this.member;
  Object value;
  DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
  desc.setContainingClass(bean.getClass());
  Set<String> autowiredBeanNames = new LinkedHashSet<String>(1);
  TypeConverter typeConverter = beanFactory.getTypeConverter();
  try {
    //這里的beanName因為Bean,所以會重新進入populateBean方法,先完成Role對象的注入
    //value == com.viewscenes.netsupervisor.entity.Role@7228c85c
    value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
  }
  catch (BeansException ex) {
    throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
  }
  if (value != null) {
    //設(shè)置可訪問,直接賦值
    ReflectionUtils.makeAccessible(field);
    field.set(bean, value);
  }
}

4.1.2 AutowiredFieldElement

如果Autowired注解在方法上,就得這樣寫。

public class User {
  @Autowired
  public void setRole(Role role) {}
}

它的inject方法和上面類似,不過最后是method.invoke。感興趣的小伙伴可以去翻翻源碼。

ReflectionUtils.makeAccessible(method);
method.invoke(bean, arguments);

4.2、配置文件的注入過程

先來看一個配置文件,我們在User類中注入了id,name,age和Role的實例。

  <bean id="user" class="com.viewscenes.netsupervisor.entity.User">
    <property name="id" value="1001"></property>
    <property name="name" value="網(wǎng)機動車"></property>
    <property name="age" value="24"></property>
    <property name="role" ref="role"></property>
  </bean>
  <bean id="role" class="com.viewscenes.netsupervisor.entity.Role">
    <property name="id" value="1002"></property>
    <property name="name" value="中心管理員"></property>
  </bean>

Spring源碼分析(一)Spring的初始化和XML這一章節(jié)的4.2 小節(jié),bean標簽的解析,我們看到在反射得到Bean的Class對象后,會設(shè)置它的property屬性,也就是調(diào)用了parsePropertyElements()方法。在BeanDefinition對象里有個MutablePropertyValues屬性。

MutablePropertyValues:
 //propertyValueList就是有幾個property 節(jié)點
 List<PropertyValue> propertyValueList:
  PropertyValue:
   name   //對應(yīng)配置文件中的name  ==id
   value   //對應(yīng)配置文件中的value ==1001 
  PropertyValue:
   name   //對應(yīng)配置文件中的name  ==name
   value   //對應(yīng)配置文件中的value ==網(wǎng)機動車 

上圖就是BeanDefinition對象里面MutablePropertyValues屬性的結(jié)構(gòu)。既然已經(jīng)拿到了property的名稱和值,注入就比較簡單了。從內(nèi)省對象PropertyDescriptor中拿到writeMethod對象,設(shè)置可訪問,invoke即可。PropertyDescriptor有兩個對象readMethodRef、writeMethodRef其實對應(yīng)的就是get set方法。

public void setValue(final Object object, Object valueToApply) throws Exception {
  //pd 是內(nèi)省對象PropertyDescriptor
  final Method writeMethod = this.pd.getWriteMethod());
  writeMethod.setAccessible(true);
  final Object value = valueToApply;
  //以id為例 writeMethod == public void com.viewscenes.netsupervisor.entity.User.setId(java.lang.String)
  writeMethod.invoke(getWrappedInstance(), value);
}

5、initializeBean
在Bean實例化和IOC依賴注入后,Spring留出了擴展,可以讓我們對Bean做一些初始化的工作。

5.1、Aware

Aware是一個空的接口,什么也沒有。不過有很多xxxAware繼承自它,下面來看源碼。如果有需要,我們的Bean可以實現(xiàn)下面的接口拿到我們想要的。

  //在實例化和IOC依賴注入完成后調(diào)用
    private void invokeAwareMethods(final String beanName, final Object bean) {
    if (bean instanceof Aware) {
      //讓我們的Bean可以拿到自身在容器中的beanName
      if (bean instanceof BeanNameAware) {
        ((BeanNameAware) bean).setBeanName(beanName);
      }
      //可以拿到ClassLoader對象
      if (bean instanceof BeanClassLoaderAware) {
        ((BeanClassLoaderAware) bean).setBeanClassLoader(getBeanClassLoader());
      }
      //可以拿到BeanFactory對象
      if (bean instanceof BeanFactoryAware) {
        ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
      }
      if (bean instanceof EnvironmentAware) {
        ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
      }
      if (bean instanceof EmbeddedValueResolverAware) {
        ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
      }
      if (bean instanceof ResourceLoaderAware) {
        ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
      }
      if (bean instanceof ApplicationEventPublisherAware) {
        ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
      }
      if (bean instanceof MessageSourceAware) {
        ((MessageSourceAware) bean).setMessageSource(this.applicationContext);
      }
      if (bean instanceof ApplicationContextAware) {
        ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
      }
      ......未完
    }
  }

做法如下:

public class AwareTest1 implements BeanNameAware,BeanClassLoaderAware,BeanFactoryAware{
  public void setBeanName(String name) {
    System.out.println("BeanNameAware:" + name);
  }
  public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
    System.out.println("BeanFactoryAware:" + beanFactory); 
  }
  public void setBeanClassLoader(ClassLoader classLoader) {
    System.out.println("BeanClassLoaderAware:" + classLoader); 
  }
}

//輸出結(jié)果
BeanNameAware:awareTest1
BeanClassLoaderAware:WebappClassLoader
  context: /springmvc_dubbo_producer
  delegate: false
  repositories:
    /WEB-INF/classes/
----------> Parent Classloader:
java.net.URLClassLoader@2626b418
BeanFactoryAware:org.springframework.beans.factory.support.DefaultListableBeanFactory@5b4686b4: defining beans ...未完

5.2、初始化

Bean的初始化方法有三種方式,按照先后順序是,@PostConstruct、afterPropertiesSet、init-method

5.2.1 @PostConstruct

這個注解隱藏的比較深,它是在CommonAnnotationBeanPostProcessor的父類InitDestroyAnnotationBeanPostProcessor調(diào)用到的。這個注解的初始化方法不支持帶參數(shù),會直接拋異常。

if (method.getParameterTypes().length != 0) {
  throw new IllegalStateException("Lifecycle method annotation requires a no-arg method: " + method);
}
public void invoke(Object target) throws Throwable {
  ReflectionUtils.makeAccessible(this.method);
  this.method.invoke(target, (Object[]) null);
}

5.2.2 afterPropertiesSet
這個要實現(xiàn)InitializingBean接口。這個也不能有參數(shù),因為它接口方法就沒有定義參數(shù)。
  boolean isInitializingBean = (bean instanceof InitializingBean);
  if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
    if (logger.isDebugEnabled()) {
      logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
    }
    ((InitializingBean) bean).afterPropertiesSet();
  }

5.2.3 init-method

ReflectionUtils.makeAccessible(initMethod);
initMethod.invoke(bean);

6、注冊

registerDisposableBeanIfNecessary()完成Bean的緩存注冊工作,把Bean注冊到Map中。

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java中的OkHttpClient 工具類

    Java中的OkHttpClient 工具類

    這篇文章主要介紹了Java中的OkHttpClient工具類,具有很好的參考價值,希望杜大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-06-06
  • Linux+Docker+SpringBoot+IDEA一鍵自動化部署的詳細步驟

    Linux+Docker+SpringBoot+IDEA一鍵自動化部署的詳細步驟

    這篇文章主要介紹了Linux+Docker+SpringBoot+IDEA一鍵自動化部署的詳細步驟,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-07-07
  • 使用Maven配置Spring的方法步驟

    使用Maven配置Spring的方法步驟

    這篇文章主要介紹了使用Maven配置Spring的方法步驟,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2019-04-04
  • Java jvm中Code Cache案例詳解

    Java jvm中Code Cache案例詳解

    這篇文章主要介紹了Java jvm中Code Cache案例詳解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • Maven添加Tomcat插件實現(xiàn)熱部署代碼實例

    Maven添加Tomcat插件實現(xiàn)熱部署代碼實例

    這篇文章主要介紹了Maven添加Tomcat插件實現(xiàn)熱部署代碼實例,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-04-04
  • Java Swing中的文本區(qū)(JTextArea)實現(xiàn)換行保存到文件的幾個方法

    Java Swing中的文本區(qū)(JTextArea)實現(xiàn)換行保存到文件的幾個方法

    這篇文章主要介紹了Java Swing中的文本區(qū)(JTextArea)實現(xiàn)換行保存到文件的幾個方法,本文給出了4種方法,需要的朋友可以參考下
    2014-10-10
  • springboot @RequestBody 接收字符串實例

    springboot @RequestBody 接收字符串實例

    這篇文章主要介紹了springboot @RequestBody 接收字符串實例,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • JAVA實現(xiàn)sm3加密簽名以及防止重復(fù)攻擊

    JAVA實現(xiàn)sm3加密簽名以及防止重復(fù)攻擊

    這篇文章主要給大家介紹了關(guān)于JAVA實現(xiàn)sm3加密簽名以及防止重復(fù)攻擊的相關(guān)資料,SM3是簽名算法,和MD5一樣(對于應(yīng)用層來說),SM4是對稱加密算法,和AES一樣(對于應(yīng)用層來說),需要的朋友可以參考下
    2023-10-10
  • SpringBoot實現(xiàn)監(jiān)控Actuator,關(guān)閉redis監(jiān)測

    SpringBoot實現(xiàn)監(jiān)控Actuator,關(guān)閉redis監(jiān)測

    這篇文章主要介紹了SpringBoot實現(xiàn)監(jiān)控Actuator,關(guān)閉redis監(jiān)測,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • Java代碼的三根頂梁柱:循環(huán)結(jié)構(gòu)

    Java代碼的三根頂梁柱:循環(huán)結(jié)構(gòu)

    這篇文章主要介紹了JAVA 循環(huán)結(jié)構(gòu)的相關(guān)資料,文中講解的非常細致,示例代碼幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下
    2021-08-08

最新評論