1. 程式人生 > 程式設計 >使用Java註解模擬spring ioc容器過程解析

使用Java註解模擬spring ioc容器過程解析

使用註解,簡單模擬spring ioc容器。通過註解給物件屬性注入值。

專案結構

使用Java註解模擬spring ioc容器過程解析

annotation 包,用於存放自定義註解

Component 註解表示該類為元件類,並需要宣告名字

package priv.haidnor.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 元件
 */
@Target(value = ElementType.TYPE)
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Component {
	String name();
}

Value 註解用於給類的屬性賦值

package priv.haidnor.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 欄位值
 */
@Target(value = ElementType.FIELD)
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Value {
	String value();
}

ioc 包

package priv.haidnor.ioc;

import priv.haidnor.annotation.Component;
import priv.haidnor.annotation.Value;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

public class ApplicationContext {

	/**
	 * IOC 控制反轉容器,在載入類的時候建立 HashMap,並存入指定物件
	 */
	private static Map<String,Object> ioc;

	static {
		try {
			// 初始化 IOC 容器
			ioc = new HashMap<String,Object>();

			// 反射獲得 Class 物件
			Class<?> clazz = Class.forName("priv.haidnor.pojo.Person");

			// 獲取指定註解
			Component componentClass = clazz.getAnnotation(Component.class);

			// 獲取指定註解的值
			String key = componentClass.name();

			// 通過反射建立物件
			Object object = clazz.newInstance();
			ioc.put(key,object);

			// 獲取 Java Bean 所有的欄位
			Field[] fields = clazz.getDeclaredFields();

			for (Field field : fields) {
				// 欄位型別
				Class<?> type = field.getType();

				// 根據欄位名生成 set 方法
				String filedName = field.getName();
				String methodName = produceSetMethodName(filedName);

				// 獲得 Value 註解
				Value valueAnnotation = field.getAnnotation(Value.class);

				// 獲得註解的值
				String theValue = valueAnnotation.value();

				// 構造 Method 物件
				Method method = clazz.getDeclaredMethod(methodName,field.getType());

				// 將註解引數轉換型別
				Object value = typeConversion(field.getType(),theValue);

				// 執行 set 方法
				method.invoke(object,value);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 型別轉換。將 String 字串轉換為指定資料型別型別的值
	 * @param typeClass 欄位型別
	 * @param value 註解值
	 * @return 字串轉換為指定資料型別型別的值
	 */
	private static Object typeConversion(Class<?> typeClass,String value) {
		if (typeClass == int.class || typeClass == Integer.class) {
			if (value == null) {
				return 0;
			}
			return Integer.valueOf(value);
		} else if (typeClass == short.class) {
			if (value == null) {
				return 0;
			}
			return Short.valueOf(value);
		} else if (typeClass == byte.class) {
			if (value == null) {
				return 0;
			}
			return Short.valueOf(value);
		} else if (typeClass == double.class) {
			if (value == null) {
				return 0;
			}
			return Double.valueOf(value);
		} else if (typeClass == long.class) {
			if (value == null) {
				return 0;
			}
			return Long.valueOf(value);
		} else if (typeClass == String.class) {
			if (value == null) {
				return "";
			}
			return value;
		} else if (typeClass == Boolean.class) {
			if (value == null) {
				return false;
			}
			return Boolean.valueOf(value);
		} else if (typeClass == BigDecimal.class) {
			if (value == null) {
				return new BigDecimal(0);
			}
			return new BigDecimal(value + "");
		} else {
			return typeClass.cast(value);
		}
	}

	/**
	 * 拼接字串,生成 set 方法名
	 * @param filedName 欄位名
	 * @return set方法名,例如傳入"name",則返回"setName"
	 */
	private static String produceSetMethodName(String filedName) {
		return "set" + filedName.substring(0,1).toUpperCase() + filedName.substring(1);
	}

	/**
	 * 從容器中獲得指定物件
	 * @param name 物件名稱
	 * @return IOC 容器中的物件
	 */
	public static Object getBean(String name) {
		return ioc.get(name);
	}
}

pojo 包

package priv.haidnor.pojo;

import priv.haidnor.annotation.Component;
import priv.haidnor.annotation.Value;

@Component(name = "man")
public class Person {

	@Value("張三")
	private String name;

	@Value("男")
	private String gender;

	@Value("中國")
	private String country;

	@Value("23")
	private Integer age;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getGender() {
		return gender;
	}

	public void setGender(String gender) {
		this.gender = gender;
	}

	public String getCountry() {
		return country;
	}

	public void setCountry(String country) {
		this.country = country;
	}

	public Integer getAge() {
		return age;
	}

	public void setAge(Integer age) {
		this.age = age;
	}

	@Override
	public String toString() {
		return "Person{" +
				"name='" + name + '\'' +
				",gender='" + gender + '\'' +
				",country='" + country + '\'' +
				",age=" + age +
				'}';
	}
}

測試類

import priv.haidnor.ioc.ApplicationContext;
import priv.haidnor.pojo.Person;

/**
 * 測試類
 */
public class Demo {
	public static void main(String[] args) {
		Person person = (Person) ApplicationContext.getBean("man");
		System.out.println(person);
	}
}

執行程式後,控制檯輸出物件資訊,可以看到從ioc容器中拿出的物件已經成功被註解賦值

使用Java註解模擬spring ioc容器過程解析

備註

內建註解

@Override:定義在java.lang.Override中,此註釋只適用於修辭方法,表示一個方法宣告打算重寫超類中的另一個方法宣告.

@Deprecated:定義在java.lang.Deprecated中,此註釋可以用於修辭方法,屬性,類,表示不鼓勵程式設計師使用這樣的元素,通常是因為它很危險或者存在更好的選擇.

@SuppressWarnings:定義在java.lang.SuppressWarnings中,用來抑制編譯時的警告資訊.口與前兩個註釋有所不同,你需要新增一個引數才能正確使用,這些引數都是已經定義好了的,我們選擇性的使用就好了.

  • @SuppressWarnings ("all")
  • @SuppressWarnings ("unchecked")
  • @SuppressWarnings (value={"unchecked","deprecation"})
  • 等等……

4個元註解

元註解的作用就是負責註解其他註解,Java定義了4個標準的meta-annotation型別,他們被用來提供對其他annotation型別作說明.
這些型別和它們所支援的類在java.lang.annotation包中可以找到

@Target:用於描述註解的使用範圍(即:作用域,被描述的註解可以用在什麼地方)

@Target(value = {ElementType.TYPE,ElementType.CONSTRUCTOR})
@Target(value = ElementType.TYPE)
@Target(ElementType.TYPE)

類,介面(包括註釋型別)或列舉宣告
  TYPE

欄位宣告(包括列舉常量)
  FIELD

方法宣告
  METHOD

形式引數宣告
  PARAMETER

構造宣告
  CONSTRUCTOR

區域性變數宣告
  LOCAL_VARIABLE

註解型別宣告
  ANNOTATION_TYPE

包宣告
  PACKAGE

型別引數宣告 @since 1.8
  TYPE_PARAMETER

使用型別 @since 1.8
  TYPE_USE

@Retention:表示需要在什麼級別儲存該註釋資訊,用於描述註解的生命週期 (SOURCE<CLASS<RUNTIME)

@Retention(value = RetentionPolicy.CLASS)
@Retention(RetentionPolicy.CLASS)

註解將被編譯階段丟棄
  SOURCE
註解將由編譯器記錄在類檔案中,但VM不必在執行時保留它們。這是預設行為。
  CLASS
註解由編譯器記錄在類檔案中,並在執行時由VM保留,因此可以通過反射方式讀取它們
  RUNTIME

@Document:說明該註解將被包含在javadoc中

@lnherited:說明子類可以繼承父類中的該註解

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。