天天看點

Spring 依賴注入——模拟實作(一)(對象注入)Spring 依賴注入(對象注入)

Spring 依賴注入(對象注入)

        首先要明确一下,類與類之間有依賴關系,我們這裡解決的依賴是指一種弱依賴。這樣直接說弱依賴,你可能不太了解是什麼意思,我舉個例子說明一下弱依賴的場景。

比如,對于這樣的場景:

        對于兩個類A、B,A弱依賴于B(我們可以這樣了解,建立A的執行個體不需要B來參加,但是A實作功能需要調用B的方法),A裡面有B成員的執行個體成員b(未初始化的),但是b不在類A的構造方法裡,是以通過構造方法來建立A的執行個體a,如果不做額外處理,a裡面的b成員肯定為null,那麼在a實作某一功能需要調用b的方法時,就會報出空指針異常!。

        對于此,這裡我們提出了依賴注入的概念,即對a的成員b從外界注入一個類B的執行個體,這樣a就可以完成需要b調用方法來實作的某一功能。

        需要指明的是,考慮到一些時間上的成本,我們這裡是采用懶漢模式(需要的時候才注入)來實作的,而不是餓漢模式(不管需不需要,都注入)。

設計思路

1.給平凡的類以及需要被注入的成員加辨別的注解;

2.通過包掃描,将這些平凡的類的對象組織到一個容器裡面。

3.當被注解的成員需要注入時,調用容器提供的注入方法。

難點

        當出現以下情況時,會産生循環依賴,為了避免這種情況,我們給封裝平凡的類(如A、B、C)相關資訊的BeanDefinition類裡面加上了inject屬性。

Spring 依賴注入——模拟實作(一)(對象注入)Spring 依賴注入(對象注入)
Spring 依賴注入——模拟實作(一)(對象注入)Spring 依賴注入(對象注入)

具體實作

        第一個注解Component用于辨別平凡的類,以下是注解的具體内容。

package com.mec.spring_imitate.annotation;

import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Retention(RUNTIME)
@Target(TYPE)
public @interface Component {
	String name() default "";
}

           

        第二個注解Autowired用于辨別需要注入的成員,以下是注解的具體内容。

package com.mec.spring_imitate.annotation;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Retention(RUNTIME)
@Target(FIELD)
public @interface Autowired {
	String name() default "";
}

           

        我們把平凡的類的中繼資料類、對象、以及判斷是否被注入的屬性封裝成一個BeanDefinition類,如下圖所示。

package com.mec.spring_imitate.core;

public class BeanDefinition {
	private Class<?> klass;
	private Object object;
	private boolean inject;
	
	BeanDefinition() {
		this.inject = false;
	}

	boolean isInject() {
		return inject;
	}

	void setInject(boolean inject) {
		this.inject = inject;
	}

	Class<?> getKlass() {
		return klass;
	}

	void setKlass(Class<?> klass) {
		this.klass = klass;
	}

	Object getObject() {
		return object;
	}

	void setObject(Object object) {
		this.object = object;
	}

	@Override
	public String toString() {
		return klass.getSimpleName() + " : " + object;
	}
	
}

           

        然後接着,我們把BeanDefinition按照某種對應關系組織到類BeanFactory的Map<String,BeanDefinition >容器裡,BeanFactory的具體内容如下。

package com.mec.spring_imitate.core;

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

import com.mec.orm.core.PackageScanner;
import com.mec.spring_imitate.annotation.Autowired;
import com.mec.spring_imitate.annotation.Bean;
import com.mec.spring_imitate.annotation.Component;

public class BeanFactory {
	private static final Map<String, BeanDefinition> beanPool;//單例實作,隻需要一份就夠了。
	static {
		beanPool = new HashMap<String, BeanDefinition>();
	}
	
	public BeanFactory() {
	}

	public void scanBeanByPackage(String packageName) {
		new PackageScanner() {//包掃描
			@Override
			public void dealClass(Class<?> klass) {
				if (klass.isPrimitive()//八大基本類型
						|| klass.isInterface()//接口
						|| klass.isAnnotation()//注解
						|| klass.isEnum()//枚舉
						|| klass.isArray()//數組
						|| !klass.isAnnotationPresent(Component.class))//不包含Component注解的類
			    {
					return;
				}
				
				Object object = null;
				try {//這樣的設定方法保證了注入的對象是單例的。
					object = klass.newInstance();
					BeanDefinition bd = new BeanDefinition();
					bd.setKlass(klass);
					bd.setObject(object);
					//把被Component注解的類形成的對應關系加入到beanpool容器裡
					beanPool.put(klass.getName(), bd);
				} catch (InstantiationException e) {
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				}
			}
		}.packageScanner(packageName);
	}

	boolean hasClass(String className) {
		return beanPool.containsKey(className);
	}
	
	BeanDefinition getBeanDefinition(String klassName) {
		return beanPool.get(klassName);
	}
	
	BeanDefinition getBeanDefinition(Class<?> klass) {
		return getBeanDefinition(klass.getName());
	}
	
	private void injectProperties(BeanDefinition beanDefinition) throws RuntimeException {
		Class<?> klass = beanDefinition.getKlass();
		Object object = beanDefinition.getObject();
		
		Field[] fields = klass.getDeclaredFields();
		for (Field field : fields) {
			if (!field.isAnnotationPresent(Autowired.class)) {//如果沒有需要注入的注解标記的成員,則跳過。
				continue;
			}
			field.setAccessible(true);//設定成員可通路
			Object value = getBean(field.getType());//擷取被@Autowired标記的成員的BeanDefinition
			if (value == null) {//如果擷取的被@Autowired标記的成員的BeanDefinition為null,則說明沒有對應的BeanDefinition
				throw new HasNoBeanException("類[" + klass.getName()
						+ "]的[" + field.getName()
						+ "]成員沒有對應的BeanDefinition!");
			}
			try {
				field.set(object, value);
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
		}
	}
	
	@SuppressWarnings("unchecked")
	public <T> T getBean(String klassName) throws RuntimeException {
		BeanDefinition bd = getBeanDefinition(klassName);
		if (bd == null) {
			return null;
		}
		
		Object result = bd.getObject();
		if (!bd.isInject()) {//如果沒有被注入,則設定為注入,這樣可以解決循環依賴問題
			bd.setInject(true);
			injectProperties(bd);//注入
		}
		
		return (T) result;
	}
	
	public <T> T getBean(Class<?> klass) throws RuntimeException {
		return getBean(klass.getName());
	}
	
}

           

現在開始測試,首先測試單例以及異常處理:

被測試的平凡的類

Complex類,具體内容如下。

package com.mec.spring_imitate.some_class;

import com.mec.spring_imitate.annotation.Component;

@Component
public class Complex {
	private double real;
	private double vir;
	
	public Complex() {
	}

	public double getReal() {
		return real;
	}

	public void setReal(double real) {
		this.real = real;
	}

	public double getVir() {
		return vir;
	}

	public void setVir(double vir) {
		this.vir = vir;
	}

	@Override
	public String toString() {
		return "(" + real + ", " + vir + ")";
	}

}

           

ClassOne類,具體内容如下。

package com.mec.spring_imitate.some_class;

import com.mec.spring_imitate.annotation.Autowired;
import com.mec.spring_imitate.annotation.Component;

@Component
public class ClassOne {
	@Autowired
	private Complex complex;
	@Autowired
	private Point point;
	private String str;
	
	public ClassOne() {
	}

	public Complex getComplex() {
		return complex;
	}

	public void setComplex(Complex complex) {
		this.complex = complex;
	}

	public Point getPoint() {
		return point;
	}

	public void setPoint(Point point) {
		this.point = point;
	}

	public String getStr() {
		return str;
	}

	public void setStr(String str) {
		this.str = str;
	}

	@Override
	public String toString() {
		return "ClassOne [complex=" + complex + ", str=" + str 
				+ ", point=" + point + "]";
	}
	
}

           

Point類,具體内容如下。

package com.mec.spring_imitate.some_class;

public class Point {
	private int row;
	private int col;
	private Complex complex;
	
	public Point() {
	}

	public Point(Complex complex) {
		this.complex = complex;
	}

	int getRow() {
		return row;
	}

	void setRow(int row) {
		this.row = row;
	}

	int getCol() {
		return col;
	}

	void setCol(int col) {
		this.col = col;
	}

	public Complex getComplex() {
		return complex;
	}

	public void setComplex(Complex complex) {
		this.complex = complex;
	}

	@Override
	public String toString() {
		return "[" + row + ", " + col + "]";
	}
	
}

           

測試類和測試結果截圖一:

package com.mec.spring_imitate.test;

import com.mec.spring_imitate.core.BeanFactory;
import com.mec.spring_imitate.some_class.ClassOne;
import com.mec.spring_imitate.some_class.Complex;

public class Demo {

	public static void main(String[] args) {
		BeanFactory.scanBeanByPackage("com.mec.spring_imitate.some_class");
		BeanFactory beanFactory = new BeanFactory();
		
		Complex c1 = beanFactory.getBean(Complex.class.getName());
		Complex c2 = beanFactory.getBean(Complex.class.getName());
		System.out.println(c2 == c1);
		ClassOne classOne = beanFactory.getBean(ClassOne.class);
		ClassOne classTwo = beanFactory.getBean(ClassOne.class);
		System.out.println(classOne + "\n" + classTwo);
	}

}
/*
輸出結果:
true
Exception in thread "main" com.mec.spring_imitate.core.HasNoBeanException: 類[com.mec.spring_imitate.some_class.ClassOne]的[point]成員沒有對應的BeanDefinition!
	at com.mec.spring_imitate.core.BeanFactory.injectProperties(BeanFactory.java:113)
	at com.mec.spring_imitate.core.BeanFactory.getBean(BeanFactory.java:135)
	at com.mec.spring_imitate.core.BeanFactory.getBean(BeanFactory.java:142)
	at com.mec.spring_imitate.test.Demo.main(Demo.java:16)

*/
           

測試循壞依賴:

修改Complex類(增加一個Point類型的成員,并加上注解Autowired),如下所示

package com.mec.spring_imitate.some_class;

import com.mec.spring_imitate.annotation.Autowired;
import com.mec.spring_imitate.annotation.Component;

@Component
public class Complex {
	private double real;
	private double vir;
	@Autowired
	private Point point;
	//其餘内容同上
	@Override
	public String toString() {
		return "(" + real + ", " + vir + "), Point:" + point;
	}
}
           

再修改Point類(增加一個Complex類型的成員,并增加注解Autowired),如下所示

package com.mec.spring_imitate.some_class;

import com.mec.spring_imitate.annotation.Autowired;
import com.mec.spring_imitate.annotation.Component;

@Component
public class Point {
	private int row;
	private int col;
	@Autowired
	private Complex complex;
	//其餘内容同上
}
           

測試類和測試結果截圖二:

package com.mec.spring_imitate.test;

import com.mec.spring_imitate.core.BeanFactory;
import com.mec.spring_imitate.some_class.ClassOne;
import com.mec.spring_imitate.some_class.Complex;
import com.mec.spring_imitate.some_class.Point;


public class Demo {

	public static void main(String[] args) {
		BeanFactory beanFactory = new BeanFactory();
		beanFactory.scanBeanByPackage("com.mec.spring_imitate.some_class");
		
		Complex c1 = beanFactory.getBean(Complex.class.getName());
		System.out.println(c1);
		Point point = beanFactory.getBean(Point.class);
		System.out.println(point);
		ClassOne classOne = beanFactory.getBean(ClassOne.class);
		System.out.println(classOne);
	}
}
		/*
		輸出結果:
		(0.0, 0.0), Point:[0, 0]
		[0, 0]
		ClassOne [complex=(0.0, 0.0), Point:[0, 0], str=null, point=[0, 0]]

		*/
           

        總的來說,我們的測試結果還是令人滿意的,我們的BeanFactory裡面提供的getBean方法還是能實作弱依賴注入的,并且成功的解決了循環依賴問題。

反思

        對于有些類,比如已經在jar包裡面的類,我們是無法加注解的,這種方式行不通,那麼我們怎麼處理呢?

        是以,敬請期待下一篇Spring 依賴注入——模拟實作(二)(方法注入)。

        下載下傳位址:https://github.com/dx99606707/depository/blob/master/Spring模拟實作對象注入及循環依賴的處理.rar

繼續閱讀