Spring beanの実用化とIOC依存注入の詳細


前言
私達は知っています。IOCはSpringの核心です。オブジェクトのライフサイクルとオブジェクトの関係をコントロールします。
例えば、私たちはどうやって相手を探しますか?よくある情況は、道で至るところにどのMMを見に行きますか?綺麗でスタイルも良くて、私達の好みに合います。彼女たちの電話番号を聞いて、関連を作って彼女たちと知り合って、ここでN歩を省略して、最後に恋愛結婚をします。
IOCはここで結婚相談所のように、結婚適齢男女の資料がたくさんあります。必要があれば、直接にどのような彼女が必要なのか教えてください。これは私達にMMを提供してくれます。直接恋愛結婚をして完璧です。
ここでは、Springはどのようにこれらのオブジェクトを生成して管理しているかを見てみましょう。
1、方法の入り口
org.springframe ewark.beans.factory.support.DefaultListable BeanFactory.preInstantiateSingletons()方法は今日の主役で、すべてそれから始まります。

 public void preInstantiateSingletons() throws BeansException {
    //beanDefinitionNames              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       ,     Bean IOC    
        getBean(beanName);
      }
    }
  }
2、Beanの実用化
入口法getBenでは,まずdoCreateBean法を呼び出した。第一歩は、反射によってビームを具体化することである。

  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   ,           ,    ctor.newInstance(args);
      instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
  }
3、Annotationのサポート
Beanの実装が完了すると、一部のバックプロセッサのコードが入力されます。コードから見て、フィルタリングはMergedBeanDefinitionPostProcessorインターフェースの種類を実現し、そのpostProcess MergedBenDefinition()方法を呼び出します。誰がMergedBenDefinitionPostProcessorインターフェースを実現しましたか?私たちは重点的に三つを見ます。
  • AutowiredAnnotationBenPostProcessor
  • Common AnnotationBeanPostProcessor
  • RequiredAnnotationBenPostProcessor
  • 覚えていますか?Springソース分析(一)Springの初期化とXMLの章の中で、Springはannotationn configラベルに対する支持と言って、いくつかの特殊なBeanを登録して、ちょうど上の3つを含みます。これらはこっそり何をしていますか?
    メソッド名から、同じことをして、メタデータをロードします。方法の内部はまた同じことをした。
    
    ReflectionUtils.doWithLocalFields(targetClass, new ReflectionUtils.FieldCallback() 
    ReflectionUtils.doWithLocalMethods(targetClass, new ReflectionUtils.MethodCallback()
    方法のパラメータを見ると、taget ClassはBeanのクラスのオブジェクトです。次に、そのフィールドと方法を取得し、対応する注釈が含まれているかどうかを判断し、最後に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)) {
            //   AutowiredFieldElement  ,    
          }
        }
        for (int i = 0; i < methods.length; i++) {
          Method method = methods[i];
          if (method.isAnnotationPresent(Autowired.class)) {
            //   AutowiredMethodElement  ,    
          }
        }
        return new InjectionMetadata(clazz, elements);
      }
    
    
    InjectionMetadataオブジェクトは2つの重要な属性があります。ターゲットClass、injectedelementsは、注解式の依存注入時にはそれらに重点を置いています。
    
      public InjectionMetadata(Class<?> targetClass, Collection<InjectedElement> elements) {
        //targetClass Bean Class  
        this.targetClass = targetClass; 
        //injectedElements   InjectedElement     
        this.injectedElements = elements;
      }
      //member     ,      
      //pd JDK        ,           
      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、依存注入
    前にはdoCreateBen()法Beanの実用化が完成しました。次は注入に依存します。
    ビーンの依存注入は2つの方法があります。一つはプロファイル、もう一つは注釈式です。
    4.1、注解式の注入過程
    上記第3小節では、SpringはBeanをフィルタリングしました。例として@Autowired、@Resourceなどの注釈が含まれているFieldとMethodは、Classオブジェクト、内省オブジェクト、メンバーを含むInjection Metadataオブジェクトに戻りました。やはり@Autowiredを例にとって、今回はAutowiredAnnotationBeanPostProcess.postProcess Process PropertyValuesに呼び出されました。
    まず、InjectionMetadataのオブジェクトを入手して、内部のInjectedelementの集合が空かどうか、つまりBeanのフィールドと方法に@Autowiredが含まれているかどうかを判断します。その後、InjectedElement.inject()を呼び出します。InjectElementには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) {
        //     ,    
        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="    "></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の章の4.2小節において、beanタグの解析は、BeanのClassオブジェクトを反射した後に、そのproperty属性を設定します。つまり、パースPropertyElements()メソッドを呼び出しました。BeanDefinitionオブジェクトにMutable PropertyValues属性があります。
    
    MutablePropertyValues:
     //propertyValueList     property   
     List<PropertyValue> propertyValueList:
      PropertyValue:
       name   //        name  ==id
       value   //        value ==1001 
      PropertyValue:
       name   //        name  ==name
       value   //        value ==     
    上の図はBeanDefinitionオブジェクトの中のMutable PropertyValues属性の構造です。propertyの名前と値をもらった以上、注入は簡単です。内省対象のPropertyDescriptorからwriteMethodのオブジェクトを入手し、アクセス可能、invokeを設定すればいいです。PropertyDescriptorには二つのオブジェクトreadMethodRef、writeMethodRefがありますが、これに対応するのがget set方法です。
    
    public void setValue(final Object object, Object valueToApply) throws Exception {
      //pd      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、initializaBen
    Beanの実用化とIOC依存注入の後、Springは拡張を残しており、Beanに対していくつかの初期化作業を行うことができます。
    5.1、Aware
    Awareは空いているインターフェースで、何もありません。でも、多くのxxAwareはそれから受け継いでいます。下からソースを見にきます。必要があれば、私達のBeanは下のインターフェースを実現して私達が欲しいものを手に入れることができます。
    
      //     IOC         
        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); 
      }
    }
    
    //出力結果
    BenNameAware:awareTest 1
    BeanClass Loader Aware:WebappClass Loader
      context:/springmvc_ドゥbbo.プロデュース
      delegate:false
      repositories:
        /WEB-INF/clases/
    -------->パーティクラスリーダー:
    java.netURLClassLoader@2626b418
    BenFactory Aware:org.springframe ebook.beans.factory.support.DefaultListableBeanFactory@5b4686b4:defining beans…未完
    5.2、初期化
    Beanの初期化方法は3つあります。先着順は@PostCorstruct、afterPropertiesset、init-methodです。
    5.2.1@PostConstruct
    この注釈は深く隠しています。これはCommon AnnotationBeanPostProcessorの父類InitDestroyAnnotationBeanPostProcessorに呼び出されました。この注釈の初期化方法はバンドパラメータをサポートしていないので、直接に異常を投げます。
    
    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
         InitializingBean  。        ,              。
      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、登録する
    register Dispopsable BenIfNecessary()はBeanのキャッシュ登録作業を完了し、BeanをMapに登録する。
    以上が本文の全部です。皆さんの勉強に役に立つように、私たちを応援してください。