Springの中の自動組立とAutowired注釈の使用を徹底的に理解します。


一、自動組立
SpringがBean属性を組み立てる時には、あるBeanの引用を指定属性に組み立てる必要があることが非常に明確になっています。例えば、私たちの応用文脈に一つのorg.mybatis.spring.Sql Session FactoryBenタイプのBenしかないなら、Sql Session FactoryBenに依存する他のどのBenもこのBeanが必要です。ここにはSql Session FactoryBeanだけがあります。
この明確な組み立てシーンに対応するために、Springは自動組立を提供しています。この明示的な組み立てBean特性よりも,Springに自動的に組み立てることができるシーンを認識させないのはなぜですか?
自動組立ビームの依存関係に関わる場合には、Springには様々な処理がある。従って,Springは4つの自動組立戦略を提供する。

public interface AutowireCapableBeanFactory{

 //      
 int AUTOWIRE_NO = 0;

 //       bean  
 int AUTOWIRE_BY_NAME = 1;

 //       bean  
 int AUTOWIRE_BY_TYPE = 2;

 //        
 int AUTOWIRE_CONSTRUCTOR = 3;

 //    ,Spring3.0      
 @Deprecated
 int AUTOWIRE_AUTODETECT = 4;
}
SpringはAutowireCapableBeanFactoryインターフェースでこれらのポリシーを定義している。AUTOWIRE_AUTODETECTは、古い方法としてマークされ、Spring 3.0以降はサポートされていない。
1、byName
ビーンの属性と同じ名前の他のビーンを自動的にビーンの対応する属性に組み立てるという意味です。言いにくいかもしれませんが、例を見てみます。
まず、UserのBeanの中にRole myRoleという属性があります。もう一つのRole Beanを作成します。その名前はmyRoleというなら、Userの中でbyNameを使って自動組立ができます。

public class User{
 private Role myRole;
}
public class Role {
 private String id; 
 private String name;
}
上はビーンの定義です。配置ファイルを見てください。

<bean id="myRole" class="com.viewscenes.netsupervisor.entity.Role">
 <property name="id" value="1001"></property>
 <property name="name" value="   "></property>
</bean>

<bean id="user" class="com.viewscenes.netsupervisor.entity.User" autowire="byName"></bean>

上述したように、属性名とBeanの名称が対応できる限り、userのBeanにおいて、byNameを用いて自動的に組み立てることができる。では、属性名が対応していない場合は?
2、byType
はい、属性名を使わずに対応すれば、オプションで自動組み立てもできます。ビーンの属性と同じタイプの他のビーンを自動的にビーンの対応する属性に組み立てるという意味です。

<bean class="com.viewscenes.netsupervisor.entity.Role">
 <property name="id" value="1001"></property>
 <property name="name" value="   "></property>
</bean>

<bean id="user" class="com.viewscenes.netsupervisor.entity.User" autowire="byType"></bean>

やはり上記の例です。byTypeを使えば、Role BeanのIDは全部省けます。
3、constructor
ビームのコンストラクタと同じタイプの他のビームを自動的にビーコンの対応に組み込むということです。値の注意点は、同じタイプの他のビーンという言葉は、参者を探している時に、やはりビーンのタイプによって決まるということです。
コンストラクタに入るタイプはRoleです。

public class User{
 private Role role;

 public User(Role role) {
 this.role = role;
 }
}

<bean id="user" class="com.viewscenes.netsupervisor.entity.User" autowire="constructor"></bean>
4、autdetect
まずconstructorを使って自動組立を試みます。失敗したらbyTypeを使ってみます。ただし、Spring 3.0以降は@Deprecatedと表記されています。
5、標準自動組立
デフォルトでは、default-autwire属性はnoneとして設定されており、全てのBeanは自動組立を使用しない。ビーン上にautwire属性が配置されている場合を除いて。
すべてのビーンに同じautwire属性を設定する必要があるなら、この操作を簡単にする方法があります。
ルート要素ビームに属性を追加するdefault-autowire="byType"。

<beans default-autowire="byType">
Spring自動組立のメリットは言うまでもない。しかし実際には、Spring XMLのプロファイルでの自動組立は推奨されていません。あるいは、Spring全体の応用の中の全てのBeanの状況を知っていない限り、Beanの増加と関係の複雑さが増すにつれて、状況が悪いかもしれません。
二、Autowired
Spring 2.5から、注釈を用いて自動的にビーンを組み立てる属性をサポートし始めました。これはより細かい粒度の自動組立を可能にし、ある属性を選択的に表示して自動組立を適用することができます。
Springは自動組立へのいくつかの異なる注釈をサポートしています。
  • Springは@Autowiredの注釈を持参します。
  • JSR-330の@Injectコメント。
  • JSR-250@Resourceコメント。
  • 今日はAutowiredの注釈だけに注目します。その解析と注入過程については、著者Springソースシリーズの文章を参照してください。Springソース分析(二)beanの実用化とIOC依存注入
    @Autowiredを使うのは簡単です。注入が必要な属性に注解を加えるだけでいいです。
    
    @Autowired
    UserService userService;
    しかし、使うにはいくつか注意が必要です。
    1、強制性
    デフォルトでは、強制契約の性質があり、表示されている属性は組み立て可能である必要があります。ビーンがAutowiredに表示された属性またはパラメータに組み込まれていない場合、NoSuchBeanDefinitionExceptionの異常情報が表示されます。
    
    public Object doResolveDependency(DependencyDescriptor descriptor, String beanName,
      Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {
     
     //  Bean
     Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
     //     Bean    , isRequired,     。
     if (matchingBeans.isEmpty()) {
     if (descriptor.isRequired()) {
      raiseNoSuchBeanDefinitionException(type, "", descriptor);
     }
     return null;
     }
    }
    上記のソースコードを見て、私たちはこの情報を得ることができます。ビーンの集合は空で大丈夫です。肝心のisrequired条件は成立できません。それでは、属性が確定できないなら、Autowiredを使うことができます。
    
    @Autowired(required=false)
    UserService userService;
    2、組み立て戦略
    Autowiredはどのような戦略で自動組立されていますか?
    この問題については、一概には言えません。タイプによって、あるいは名称によって、簡単に言ってはいけません。しかし、これはデフォルトでは、タイプによって自動的に組み立てられているbyTypeであることが確認できます。
    デフォルトはタイプ別に組み立てられています。
    ポイントfindAutowireCanddidatesという方法です。
    
    protected Map<String, Object> findAutowireCandidates(
     String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {
     
     //         bean  ,         beanName,      
     //   isTypeMatch     
     String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
      this, requiredType, true, descriptor.isEager());
      
     Map<String, Object> result = new LinkedHashMap<String, Object>(candidateNames.length);
     
     //     beanName,       
     for (String candidateName : candidateNames) {
     if (!isSelfReference(beanName, candidateName) && isAutowireCandidate(candidateName, descriptor)) {
      result.put(candidateName, getBean(candidateName));
     }
     }
     return result;
    }
    
    名称に従って組み立てる
    戻ってきたのはリストであることが分かります。タイプ別のマッチングで複数のインスタンスが検索される可能性があります。いったいどの例を組み立てるべきですか?ある文章の中には、注釈を加えて避けることができると書いてあります。例えば@qulifier、@Primaryなど、実は簡単な方法があります。
    例えば、UserServiceインターフェースのタイプによって実装クラスを組み立てる。UserServiceインターフェースには複数の実装クラスがあり、UserServiceImpl、UserServiceImpl 2に分けられています。注入時には、属性名をビーン実現類の名称と定義することができます。
    
    @Autowired
    UserService UserServiceImpl2;
    これでSpringはbyNameで組み立てられます。まず、タイプの複数の例を調べたら、Springはすでに判断しました。
    
    public Object doResolveDependency(DependencyDescriptor descriptor, String beanName,
      Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {
      
     //      Bean  
     Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
     //  Bean    , isRequired       
     if (matchingBeans.isEmpty()) {
     if (descriptor.isRequired()) {
      raiseNoSuchBeanDefinitionException(type, "", descriptor);
     }
     return null;
     }
     //     Bean    1 
     if (matchingBeans.size() > 1) {
     //        ,       。。     
     String primaryBeanName = determineAutowireCandidate(matchingBeans, descriptor);
     if (primaryBeanName == null) {
      throw new NoUniqueBeanDefinitionException(type, matchingBeans.keySet());
     }
     if (autowiredBeanNames != null) {
      autowiredBeanNames.add(primaryBeanName);
     }
     return matchingBeans.get(primaryBeanName);
     } 
    }
    
    複数の例を調べたら、determineAutowireCandidateの方法が鍵となることが分かります。これは適切なビーンリターンを決定します。その一部はビーンの名前でマッチします。
    
    protected String determineAutowireCandidate(Map<String, Object> candidateBeans, 
      DependencyDescriptor descriptor) {
     //     Bean  
     for (Map.Entry<String, Object> entry : candidateBeans.entrySet()) {
     String candidateBeanName = entry.getKey();
     Object beanInstance = entry.getValue();
     //  matchesBeanName     bean                
     if (matchesBeanName(candidateBeanName, descriptor.getDependencyName())) {
      return candidateBeanName;
     }
     }
     return null;
    }
    
    最後に問題に戻りますが、結果としては@AutowiredはデフォルトではbyTypeを使って属性を組み立てています。もしタイプのいくつかのインスタンスにマッチしたら、byNameを通じてBeanを決定します。
    3、主と優先度
    上記はすでに見ましたが、byTypeを通じて複数のインスタンスのBeaBenが見つかるかもしれません。そしてbyNameを通じて適当なBeaビンを確定します。名前も確定できないなら?
    それともdetermineAutowireCandidateという方法がありますか?
    
    protected String determineAutowireCandidate(Map<String, Object> candidateBeans, 
      DependencyDescriptor descriptor) {
     Class<?> requiredType = descriptor.getDependencyType();
     //  @Primary     Bean
     String primaryCandidate = determinePrimaryCandidate(candidateBeans, requiredType);
     if (primaryCandidate != null) {
     return primaryCandidate;
     }
     //  @Priority(value = 0)     Bean value      
     String priorityCandidate = determineHighestPriorityCandidate(candidateBeans, requiredType);
     if (priorityCandidate != null) {
     return priorityCandidate;
     }
     return null;
    }
    
    Primary
    その役割はBeanに@Primaryの注釈が含まれているかどうかを見て、含めると戻ります。もちろんです。複数のBeanを@Primaryに設定してはいけません。そうしないとNoUniqueBeanDefinitionExceptionという異常があります。
    
    protected String determinePrimaryCandidate(Map<String, Object> candidateBeans, Class<?> requiredType) {
     String primaryBeanName = null;
     for (Map.Entry<String, Object> entry : candidateBeans.entrySet()) {
     String candidateBeanName = entry.getKey();
     Object beanInstance = entry.getValue();
     if (isPrimary(candidateBeanName, beanInstance)) {
      if (primaryBeanName != null) {
      boolean candidateLocal = containsBeanDefinition(candidateBeanName);
      boolean primaryLocal = containsBeanDefinition(primaryBeanName);
      if (candidateLocal && primaryLocal) {
       throw new NoUniqueBeanDefinitionException(requiredType, candidateBeans.size(),
        "more than one 'primary' bean found among candidates: " + candidateBeans.keySet());
      }
      else if (candidateLocal) {
       primaryBeanName = candidateBeanName;
      }
      }
      else {
      primaryBeanName = candidateBeanName;
      }
     }
     }
     return primaryBeanName;
    }
    Priority
    Bean上に@Priorityコメントを配置することもできます。intタイプの属性valueがあり、優先度の大きさを設定できます。数字が小さいほど、優先的にマッチされます。同じように、複数のBeanの優先度を同じ大きさの数値に設定してはいけません。そうでないと、NoUniqueBeanDefinitionExceptionは異常なままに出てきます。
    
    protected String determineHighestPriorityCandidate(Map<String, Object> candidateBeans, 
         Class<?> requiredType) {
     String highestPriorityBeanName = null;
     Integer highestPriority = null;
     for (Map.Entry<String, Object> entry : candidateBeans.entrySet()) {
     String candidateBeanName = entry.getKey();
     Object beanInstance = entry.getValue();
     Integer candidatePriority = getPriority(beanInstance);
     if (candidatePriority != null) {
      if (highestPriorityBeanName != null) {
      //         
      if (candidatePriority.equals(highestPriority)) {
       throw new NoUniqueBeanDefinitionException(requiredType, candidateBeans.size(),
       "Multiple beans found with the same priority ('" + highestPriority + "') " +
        "among candidates: " + candidateBeans.keySet());
      }
      else if (candidatePriority < highestPriority) {
       highestPriorityBeanName = candidateBeanName;
       highestPriority = candidatePriority;
      }
      }
      else {
      highestPriorityBeanName = candidateBeanName;
      highestPriority = candidatePriority;
      }
     }
     }
     return highestPriorityBeanName;
    }
    
    最後に、ちょっと注意が必要です。Priorityのカバンはjavax.annotations.Priorityにあります。これを使うなら座標を導入します。
    
    <dependency>
     <groupId>javax.annotation</groupId>
     <artifactId>javax.annotation-api</artifactId>
     <version>1.2</version>
    </dependency>
    
    三、まとめ
    この章ではSpringの中の自動組立のいくつかの戦略を詳しく述べ、またソースコードを通してAutowired注釈の使用方法を分析しました。
    Spring 3.0の後、有効な自動組立戦略はbyType、byName、constructorの3つの方式に分けられます。注AutowiredはデフォルトではbyTypeを使って自動的に組み立てられていますが、もしタイプが複数あるならばbyNameを使ってみてください。byNameを通じても確定できないなら、PrimaryとPriorityの注釈で決定できます。
    以上が本文の全部です。皆さんの勉強に役に立つように、私たちを応援してください。