天天看点

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

继续阅读