今日,閒來無事,根據自己的理解手寫一版SpringIOC的注入流程,儘管是簡化版本的,但是依舊能夠幫助我們很好的理解SpringIOC爲什麼能夠做到自動注入,他的底層實現,又究竟做了哪些操作呢?下面讓我們一起進行探索吧!
一.項目中可能會使用到的jar資源 本想木採用maven 的形式,因爲要解析xml所以我這裏的技術選型是 dom4j 有其他想法的小夥伴可以自己選擇!
1.pom文件
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
2.自定義異常
public class LubanSpringException extends RuntimeException {
public LubanSpringException(String msg){
super(msg);
}
}
3.SpringIOC的手動注入流程
import com.luban.exception.LubanSpringException;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* @author 皇甫
*/
public class SpringBeanFactory {
/**
* 定義一個HashMap映射,映射beanName和對應類的關係
*/
private Map<String,Object> map = new HashMap<String, Object>();
public void springBeanFactory(String xml){
xmlAnalysis(xml);
}
/**
* 解析XML
* @param xml
*/
private void xmlAnalysis(String xml){
//讀取配置文件
File file = new File(this.getClass().getResource("/").getPath()+"/"+xml);
SAXReader reader = new SAXReader();
try {
Document document = reader.read(file);
Element rootElement = document.getRootElement();
//判斷是否有自動注入的配置
Attribute attribute = rootElement.attribute("default-autowire");
boolean flag = false;
if(attribute != null){
flag = true;
}
for (Iterator<Element> it = rootElement.elementIterator(); it.hasNext();) {
Element element = it.next();
// do something
Attribute attributeId = element.attribute("id");
String beanId = attributeId.getValue();
Attribute attributeClassName = element.attribute("class");
String beanClassName = attributeClassName.getValue();
Class clazz = Class.forName(beanClassName);
/**
* 維護依賴關係
* 遍歷子集標籤 手動設置注入
*/
Object object = null;
//遍歷子集 bean
for (Iterator<Element> childIt = element.elementIterator(); childIt.hasNext();) {
Element childElement = childIt.next();
// do something
//如果爲set注入
if(childElement.getName().equals("property")){
//獲取當前對象的類對象
object = clazz.newInstance();
//獲取當前類所依賴的類的BeanName
String childElementRefValue = childElement.attribute("ref").getValue();
//根據需要的BeanName去Map映射中尋找對應的對象
Object o = map.get(childElementRefValue);
String nameValue = childElement.attribute("name").getValue();
//根據注入的名字獲取屬性名
Field field = clazz.getDeclaredField(nameValue);
field.setAccessible(true);
//設置屬性的值
field.set(object, o);
//如果爲構造方法注入
}else if(childElement.getName().equals("constructor-arg")){
//獲取要注入的對象的id
String refValue = childElement.attribute("ref").getValue();
//取出Map
Object o = map.get(refValue);
//向構造方法中注入文件
Constructor constructor = clazz.getConstructor(o.getClass().getInterfaces()[0]);
object = constructor.newInstance(o);
}else{
}
}
//如果設置了自動注入
if(flag){
//如果爲根據類型注入
if(attribute.getValue().equals("byType")){
//判斷此類是否有依賴 和獲取全部的屬性
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
//獲取對象的類型
Class fieldType = field.getType();
int count = 0;
Object injectObject = null;
for (String s : map.keySet()) {
if(map.get(s).getClass().getInterfaces()[0].getName().equals(fieldType.getName())){
count++;
injectObject = map.get(s);
}
}
//如果對象類型匹配個數大於1證明由兩個類型,需要拋出異常
if(count>1){
throw new LubanSpringException("只需要一個類型,卻找到了兩個類型");
}else{
object = clazz.newInstance();
Field[] fs = clazz.getDeclaredFields();
field.setAccessible(true);
field.set(object, injectObject);
}
}
//如果設置爲根據名字注入
}else if(attribute.getValue().equals("byName")){
//判斷此類是否有依賴
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
Class fieldType = field.getType();
for (String s : map.keySet()) {
if (map.get(s).getClass().getInterfaces()[0].getSimpleName().equals(fieldType.getSimpleName())) {
object = clazz.newInstance();
field.setAccessible(true);
field.set(object, map.get(s));
}
}
}
}
}
//如果沒有依賴,創建對象
if(object==null){
object = clazz.newInstance();
}
map.put(beanId, object);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public Object getBean(String beanName){
return map.get(beanName);
}
}
二.測試
1.定義DAO
/**
* @author 皇甫
*/
public interface UserDao {
/**
* 查詢數據路
*/
public void find();
}
2.定義DAO實現類
import com.luban.dao.UserDao;
public class UserDaoImpl implements UserDao {
public void find() {
System.out.println("查詢數據庫");
}
}
3.定義Service接口
/**
* @author 皇甫
*/
public interface UserService {
/**
* 查詢
*/
public void find();
}
4.定義Service實現類
import com.luban.dao.UserDao;
import com.luban.service.UserService;
/**
* @author 皇甫
*/
public class UserServiceImpl implements UserService {
private UserDao userDao;
public void find() {
userDao.find();
}
}
5.定義xml
<?xml version="1.0" encoding="UTF-8"?>
<beans default-autowire = "byName">
<bean id="dao" class="com.luban.dao.impl.UserDaoImpl"></bean>
<bean id="dao1" class="com.luban.dao.impl.UserDaoImpl"></bean>
<bean id="service" class="com.luban.service.impl.UserServiceImpl"></bean>
</beans>
6.測試
import com.luban.service.UserService;
import com.luban.util.SpringBeanFactory;
import org.junit.Test;
/**
* @author 皇甫
*/
public class TestSpringIoc {
@Test
public void test1(){
SpringBeanFactory springBeanFactory = new SpringBeanFactory();
springBeanFactory.springBeanFactory("spring.xml");
UserService service = (UserService) springBeanFactory.getBean("service");
service.find();
}
}