1. 程式人生 > >手寫簡易版SpringIOC

手寫簡易版SpringIOC

     SpringIOC就不在介紹了的吧,對容器建立,管理物件的過程非常有必要自己熟悉瞭解一下其中的生產規程。手寫一個簡易版的吧。(BeanFactory,ApplicationContext的,FileSystemXmlApplicationContext,ClassPathXmlApplicationContext,

AnnotationConfigApplicationContextSpring的各個模組構建的核心模組之上,Bean包裝的是Object物件,Object裡面一定是存在資料的,Context給資料提供一個生存空間的,Context就是發現和維護Bean之間的關係的,這個複雜的Bean的關係集合可以說是SpringIOC容器.)

    環境搭建:IDEA,Java8,搭建Maven工程。工程圖如下,需要依賴jar包的,如果感覺找jar包比較麻煩的,就使用Maven的Java工程吧,繪製採用百度腦圖。

   手寫簡易版的SpringIOC步驟如下,本次實現過程基於如下的步驟的.

  


依賴的POM檔案如下:

 <dependency>
  <! -  jdom依賴 - >
  <dependency>
          <groupId> org.jdom </ groupId>
          <artifactId> jdom </ artifactId>
          <version> 2.0.2 </ version>
      </ dependency>
  
<! - jaxen解析依賴 - > <dependency> <groupId> jaxen </ groupId> <artifactId> jaxen </ artifactId> <version> 1.1.6 </ version> </ dependency> </ dependencies>

這個以一個學生類為依賴注入的案例.

Student

public class Student {
    private 
String name; private String grade; private String major; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getGrade() { return grade; } public void setGrade(String grade) { this.grade = grade; } public String getMajor() { return major; } public void setMajor(String major) { this.major = major; } /** 自我介紹的方法*/ public void introduce(){ System.out.println("我是叫"+name+",是一名"+major+"專業的"+grade+"學生!"); } }

StudentService

public class StudentService {
    private Student student;
    public Student getStudent() {
        return student;
}

    public void setStudent(Student student) {
        this.student = student;
}
}

ApplicationContext

public interface ApplicationContext {
    Object getBean(String name);
}

ClassPathXmlApplicationContext(注意觀察導包)

import com.lx.spring.myioc.iocfactory.ApplicationContext;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import org.jdom2.xpath.XPath;
import org.jdom2.xpath.XPathExpression;
import org.jdom2.xpath.XPathFactory;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
 * title: com.lx.spring.myioc.iocfactory.impl
 * @author:  * date: 
 * description:基於類路徑載入配置檔案
 */
public class ClassPathXmlApplicationContext implements ApplicationContext {
    /** 要解析的配置檔案*/
private File file;
/** 存放Bean物件的例項*/
private Map map=new HashMap();
/** 解析配置檔案,例項化容器,將物件存放入容器當中*/
public ClassPathXmlApplicationContext(String configfile) throws Exception {
       URL url =this.getClass().getClassLoader().getResource(configfile);
        try {
            file=new File(url.toURI());
xmlParse(file);
} catch (URISyntaxException e) {
            e.printStackTrace();
}
    }
    /**
     * 解析xml檔案
     * @param file
*/
private void xmlParse(File file) throws JDOMException, IOException, ClassNotFoundException, IllegalAccessException, InstantiationException, InvocationTargetException {
        SAXBuilder builder=new SAXBuilder();
Document document=builder.build(file);
// 建立XPath物件,反射獲取XPath物件
XPathFactory factory = XPathFactory.instance();
        // 獲取所有的Bean節點,XPath解析寫的寫法可以瞭解一下
        XPathExpression expression=factory.compile("//bean");
// 註釋了舊版本的寫法獲取所有的Bean節點
        // XPath xPath=XPath.newInstance("//bean");
        //List beans=xPath.selectNodes(document);
List beans=expression.evaluate(document);
Iterator iterator=beans.iterator();
        while(iterator.hasNext()){
            Element bean=(Element) iterator.next();
            // 獲取配置檔案的id屬性值  
            String id=bean.getAttributeValue("id");
String cls=bean.getAttributeValue("class");
// 反射拿到類的相應資訊,首先是拿到類的例項物件
Class clazz=Class.forName(cls);
Object object=clazz.newInstance();
// 獲取類的所有方法,然後通過set方法給這個物件設定屬性值
Method[] methods=clazz.getDeclaredMethods();
// 遍歷Bean節點下的所有屬性和方法,一一匹配,反射設定物件的屬性值
List<Element> list=bean.getChildren("property");
            for(Element element:list){
                for(int i=0;i<methods.length;i++){
                   String methodName=methods[i].getName();
// 屬性名
String temp="";
                   // 這裡檢索set方法 
                   if(methodName.startsWith("set")){
                   // 這裡就只擷取set方法的方法名並且轉換為小寫的名字
                      temp=methodName.substring(3).toLowerCase();
// 屬性為普通物件的屬性
if(element.getAttribute("name")!=null){
                           if(temp.equals(element.getAttributeValue("name"))){
                               // 反射給物件設定值
methods[i].invoke(object,element.getAttributeValue("value"));
}
                       }else{ // 屬性為引用物件的屬性
methods[i].invoke(object,map.get(element.getAttributeValue("ref")));
}
                   }
                }
            }
            // 將物件新增到容器裡面
map.put(id,object);
}
    }
    /** 獲取Bean物件*/
public Object getBean(String name) {
        return map.get(name);
}
}

application.xml檔案如下.

<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <bean id="Student" class="com.lx.spring.myioc.pojo.Student">
        <property name="name" value="Tom" />
        <property name="grade" value="大三" />
        <property name="major" value="電腦科學與技術" />
    </bean>
    <bean id="StudentService" class="com.lx.spring.myioc.service.StudentService">
        <property ref="Student" />
    </bean>
</beans>

測試一下:MyIocTest.(這裡根據類路徑下的配置檔案)

import com.lx.spring.myioc.iocfactory.impl.ClassPathXmlApplicationContext;
import com.lx.spring.myioc.service.StudentService;
/**
 * title: com.lx.spring.myioc.test
 * @author:  * date: 
 * description:測試
 */
public class MyIocTest {
    public static void main(String[] args) throws Exception {
        ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext("application.xml");
StudentService student=(StudentService)context.getBean("StudentService");
student.getStudent().introduce();
}
}

執行結果:(和呼叫相應的introduce的方法結果是一樣的.)


原理註釋部分已寫的非常清晰了的,SpringIOC的三種注入方式這裡以Set方法注入的進行介紹的.

總結一句話:Java通過配置檔案的解讀,反射拿到類例項,獲取該類的所有Set方法,過濾取出和Xml中配置的屬性值,然後反射動態給該屬性設定(Xml中配置的值),這樣呼叫相應屬性的get方法就可以獲取到相應的值啦.