手寫簡易版SpringIOC
阿新 • • 發佈:2019-01-24
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 { privateString 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方法就可以獲取到相應的值啦.