Spring依存注入の実現原理--java反射とASMフレームワーク


依存注入はspringの特性であり、構成面からプログラム結合、依存問題を解決し、springは構造関数依存注入、Setter方法依存注入、自動組立依存注入、@autowired注釈依存注入など多くの実現方式を提供している.
では、依存注入はどのように実現されますか?最初の反応はjava反射歌です.たとえば、コンストラクション関数注入では、Beanクラスのコンストラクション関数、パラメータ個数、パラメータタイプを反射で読み取ることができます.したがって、xmlプロファイルでパラメータタイプまたはパラメータ順序を指定すれば、反射によってBeanのインスタンスを簡単に作成できます.しかし、実際にはxmlプロファイルでname=パラメータ名を指定しても依存注入を実現できることが分かった.これはjava反射が実現しにくい少なくともjdk 1.8以下では実現できない.jdk 1.8以下では反射によって具体的なパラメータ名が得られないためだ.ケースを見てください.
Student.java
package com.marcus.spring.beans;

public class Student {
	private Integer age;
	private String name;
	private String gender;
	
	public Student() {
	}
	public Student(String name, String gender, Integer age) {
		this.name = name;
		this.age = age;
		this.gender = gender;
	}
	public void setAge(Integer age) {
		this.age = age;
	}
	public Integer getAge() {
		return age;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getName() {
		return name;
	}
	public String getGender() {
		return gender;
	}
	public String toString() {
		return String.format("{name: %s, gender: %s, age: %d}", this.name, this.gender, this.age);
	}
}

xmlプロファイルioc-di-asm.xml

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
   
   
   <bean id="student1" class="com.marcus.spring.beans.Student">
   	<constructor-arg  value="student1" />
   	<constructor-arg  value="male" />
   	<constructor-arg  value="11" />
   bean>   
   <bean id="student2" class="com.marcus.spring.beans.Student">
   	<constructor-arg  type="java.lang.Integer" value="22" />
   	<constructor-arg  value="student2" />
   	<constructor-arg  value="male" />
   bean>
   <bean id="student3" class="com.marcus.spring.beans.Student">
   	<constructor-arg  type="java.lang.Integer" value="33" />
   	<constructor-arg  name="gender" value="female" />
   	<constructor-arg  value="student3" />	
   bean>
beans>

テストクラスDIAsmApp.java
public class DIAsmApp {
	public static void main(String[] args) {
		AbstractApplicationContext context = new ClassPathXmlApplicationContext("ioc-di-asm.xml");
		
		Student student1 = context.getBean("student1", Student.class);
		System.out.println("student1: " + student1.toString());

		Student student2 = context.getBean("student2", Student.class);
		System.out.println("student2: " + student2.toString());
		
		Student student3 = context.getBean("student3", Student.class);
		System.out.println("student3: " + student3.toString());	
		
		context.close();
	}
}

出力結果
student1: {name: student1, gender: male, age: 11}
student2: {name: student2, gender: male, age: 22}
student3: {name: student3, gender: female, age: 33}

ケース・ノート
1、student 1、反射で実現可能
xmlには3つのconstructor-argが構成されており、type、index、nameなどの他の装飾はありません.この場合、java反射によりインスタンスを正常に作成できます.すなわち、3つのパラメータの構造関数、Student(String name、String gender、Integer age)を探し、constructor-argの出現順に構造関数パラメータに値を割り当て、インスタンスを作成します.
2、student 2、反射で実現可能
xmlは3つのconstructor-argを構成しています.1番目のパラメータはIntegerタイプとしてマークされ、残りの2つはvalue値のみで、他の識別はありません.この場合、java反射によってインスタンスを作成することができます.
  • は3つのパラメータの構造関数、Student(String name、String gender、Integer age)を見つけた.
  • 1 1番目のconstructor-arg type=「java.lang.Integer」を読み込み、コンストラクション関数でIntegerタイプのパラメータを見つけ、3番目のパラメータであることを発見するとarg 2=22
  • を設定する
  • は2番目のconstructor-argを読み出し、構造関数はarg 0であり、arg 1は設定されず、前後順に値を付与し、arg 0=student 2
  • 3番目のconstructor-argを読み込み、arg 1は設定されていません.arg 1=male


  • 3、student 3、java反射は実現できない
    xmlには3つのconstructor-argが配置されています.1番目のパラメータはIntegerタイプと表記され、2番目のパラメータはnameが設定されています.3番目のパラメータには特別な識別はありません.java反射で実現できると仮定すると、実現手順は次のとおりです.
  • は3つのパラメータの構造関数、Student(String name、String gender、Integer age)を見つけた.
  • 1 1番目のconstructor-arg type=「java.lang.Integer」を読み込み、コンストラクション関数でIntegerタイプのパラメータを見つけ、3番目のパラメータであることを発見するとarg 2=22
  • を設定する
  • は2番目のconstructor-argを読み出し、構造関数はarg 0であり、arg 1は設定されず、前後順に値を付与し、arg 0=female
  • 3 3番目のconstructor-argを読み込みます.arg 1は設定されていません.arg 1=student 3
  • です.
    私たちはstudent beanが{name=female,gender=student 3,age=33}であることを得て、名前が性別になって、性別が名前になって、私たちが望んだ結果ではありませんて、java反射は失敗しました!
    ではspring依存注入はなぜ正常に動作するのでしょうか.なぜならspringはasmフレームワークを使用してjavaクラスのバイトコードを読み取り、コンストラクション関数のパラメータ名を読み取ることができるため、前述のstudent 3構成のようにspringは2番目のconstructor-arg name=「gender」対応コンストラクション関数のarg 1を読み取ることができるため、正常に動作することができる.ASMフレームワークコードのデモ:
    AsmDemo.java
    package com.marcus.spring.aop;
    
    import java.lang.reflect.Constructor;
    
    import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
    
    import com.marcus.spring.beans.Student;
    
    public class AsmDemo {
    	public static void main(String[] args) throws ClassNotFoundException {
    		LocalVariableTableParameterNameDiscoverer localVariableTableParameterNameDiscoverer 
    			= new LocalVariableTableParameterNameDiscoverer();
    		Class<?> forName = Class.forName(Student.class.getName());
    		Constructor<?>[] constructors = forName.getConstructors();
    		for (Constructor<?> constructor : constructors) {
    			String argNames = "";
    			String[] parameterNames = localVariableTableParameterNameDiscoverer
    					.getParameterNames(constructor);
    			for (String parameterName : parameterNames) {
    				argNames += argNames.length() < 1 ? parameterName : "," + parameterName;
    			}
    			System.out.println(argNames.length()<1 ? constructor.getName() + "()" 
    					: constructor.getName() + "(" + argNames + ")");
    		}
    		//output:		
    		//com.marcus.spring.beans.Student()
    		//com.marcus.spring.beans.Student(name,gender,age)
    	}
    }
    

    ASMフレームワークでは、com.marcus.spring.beans.Studio(name,gender,age)コンストラクション関数パラメータ名を読み取り、student 3 bean注入の実装手順を続行しました.
    student 3 xmlファイルには、3つのconstructor-argが配置されています.1番目のパラメータはIntegerタイプと表記され、2番目のパラメータはnameが設定されています.3番目のパラメータには特別な識別はありません.java反射+ASMフレームワークによって、実現手順は次のとおりです.
  • は3つのパラメータの構造関数、Student(String name、String gender、Integer age)を見つけた.
  • 1 1番目のconstructor-arg type=「java.lang.Integer」を読み込み、コンストラクション関数でIntegerタイプのパラメータを見つけ、3番目のパラメータであることを発見するとarg 2=22
  • を設定する
  • 2 2番目のconstructor-arg name="gender"を読み出し、コンストラクション関数でgenderという名前のパラメータを見つけ、2番目のパラメータであることを発見するとarg 1=female
  • を設定する.
  • 3 3番目のconstructor-argを読み込みます.arg 0は設定されていません.arg 0=student 3
  • です.
    student beanは{name=student 3,gender=female,age=33}で成功した.
    小結
    SpringフレームワークはASMフレームワークを広く使用しており,springのjarパッケージ構成からspringに対するASMの重要性を見ることができる.3.2.5.RELEASEバージョンを例にとると、ASM部分はspring-core-3.2.5.RELEASE.jarパッケージ、springの最も核心的なjarパッケージの中にあります!AOP,cglibなどはいずれもASMがJavaクラスを読み取り,操作する必要がある.