SpringIOC就不在介绍了的吧,对容器创建,管理对象的过程非常有必要自己熟悉了解一下其中的生产规程。手写一个简易版的吧。(BeanFactory,ApplicationContext的,FileSystemXmlApplicationContext,ClassPathXmlApplicationContext,
环境搭建: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方法就可以获取到相应的值啦.