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屬性。
具體實作
第一個注解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