深入理解spring–手写简易的IOC容器
使用过 spring 之后,发现很多东西都不用自己 new 很神奇,这些对象都是怎么来的呢?
IOC控制反转是怎么实现的呢?
spring IOC 容器可以在对象生成或初始化的时候就把属性注入到对象中,如果对象 A 的一个属性为对象 B ,还可以将对象 B 的引用注入到对象 A 的数据域中。如果对象 A 在初始化的时候对象 B 还没有初始化,而 A 又需要 B 作为自己的属性,这时 IOC 容器就会递归的先实例化对象 B ,这样也可以把对象之间的依赖清晰的建立起来。
IOC容器把对象的创建工作从具体的业务对象手中抢过来,这样也达到了解耦的目的,开发更加方便。
项目 github 地址(建议加星,当然我只是建议):https://github.com/Archivev/MyIOC
这里 ApplicationContext 里并没有任何方法,只是模仿 spring 继承了 BeanFactory,真正的 spring 结构要更加复杂,这里只是梳理一下大致关系
容器要实现的功能
可以正确的解析xml文件,并将对象的实例创建出来
借助 BeanUtils 可以将属性值注入到对象实例中
可以设置 Scope 作用域为 singleton(单例模式)、prototype(多例模式)
可以实现对象类型的注入 ref
所需技术
dom4j 用来解析 xml 配置文件
BeanUtils 用来帮助我们对对象注入属性
Java反射机制
所需 jar 包:
项目结构图及介绍:
代码部分 applicationContext.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <?xml version="1.0" encoding="utf-8"?> <beans > <bean id ="people" class ="com.Archiver.entity.People" > <property name ="name" value ="张三" /> <property name ="sex" value ="男" /> </bean > <bean id ="user" class ="com.Archiver.entity.User" > <property name ="people" ref ="people" /> <property name ="userId" value ="11" /> </bean > <bean id ="man" class ="com.Archiver.entity.Man" > <property name ="age" value ="123" /> <property name ="people" ref ="people" /> </bean > </beans >
Bean.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 public class Bean { private String name; private String className; private String scope="singleton" ; private List<Property> properties = new ArrayList<>(); public String getName () { return name; } public void setName (String name) { this .name = name; } public String getClassName () { return className; } public void setClassName (String className) { this .className = className; } public String getScope () { return scope; } public void setScope (String scope) { this .scope = scope; } public List<Property> getProperties () { return properties; } public void setProperties (List<Property> properties) { this .properties = properties; } @Override public String toString () { return "Bean{" + "name='" + name + '\'' + ", className='" + className + '\'' + ", scope='" + scope + '\'' + ", properties=" + properties + '}' ; } }
Property.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 public class Property { private String name; private String value; private String ref; public String getName () { return name; } public void setName (String name) { this .name = name; } public String getValue () { return value; } public void setValue (String value) { this .value = value; } public String getRef () { return ref; } public void setRef (String ref) { this .ref = ref; } @Override public String toString () { return "Property{" + "name='" + name + '\'' + ", value='" + value + '\'' + ", ref='" + ref + '\'' + '}' ; } }
Man.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public class Man { private People people; private String age; public People getPeople () { return people; } public void setPeople (People people) { this .people = people; } public String getAge () { return age; } public void setAge (String age) { this .age = age; } @Override public String toString () { return "Man{" + "people=" + people + ", age='" + age + '\'' + '}' ; } }
User.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public class User { private String userId; private People people; public String getUserId () { return userId; } public void setUserId (String userId) { this .userId = userId; } public People getPeople () { return people; } public void setPeople (People people) { this .people = people; } @Override public String toString () { return "User{" + "userId='" + userId + '\'' + ", people=" + people + '}' ; } }
People.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public class People { private String name; private String sex; public String getName () { return name; } public void setName (String name) { this .name = name; } public String getSex () { return sex; } public void setSex (String sex) { this .sex = sex; } @Override public String toString () { return "People{" + "name='" + name + '\'' + ", sex='" + sex + '\'' + '}' ; } }
BeanFactory.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public interface BeanFactory { Object getBean (String name) ; boolean isSingleton (String name) ; boolean isPrototype (String name) ; boolean containsBean (String name) ; }
ApplicationContext.java 1 2 3 4 public interface ApplicationContext extends BeanFactory {}
ClassPathXmlApplicationContext.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 public class ClassPathXmlApplicationContext implements ApplicationContext { private Map<String, Bean> map; private Map<String,Object> context = new HashMap<>(); public ClassPathXmlApplicationContext (String pathName) { map = ParseXML.getConfig(pathName); for (Map.Entry<String,Bean> e: map.entrySet()) { String beanName = e.getKey(); Bean bean = e.getValue(); Object exsitBean = context.get(beanName); if (exsitBean == null && "singleton" .equals(bean.getScope())){ exsitBean = createBean(bean); context.put(beanName,exsitBean); } } } public Object createBean (Bean bean) { Class clazz = null ; try { clazz = Class.forName(bean.getClassName()); }catch (ClassNotFoundException e){ e.printStackTrace(); throw new RuntimeException("没有找到该类" ); } Object object = null ; try { object = clazz.newInstance(); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("没有提供无参构造器" ); } Map<String,String> pmap = new HashMap<>(); for (Property p: bean.getProperties()) { String name = p.getName(); String value = p.getValue(); String ref = p.getRef(); if (value != null ){ pmap.put(name,value); try { BeanUtils.populate(object,pmap); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("请检查你的" + name + "属性" ); } } if (ref != null ){ Object exsitBean = context.get(ref); if (exsitBean == null ){ exsitBean = createBean(map.get(ref)); } try { BeanUtils.setProperty(object,name,exsitBean); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("您的bean的属性" + name + "未设置set方法" ); } } } return object; } @Override public Object getBean (String name) { Object o = context.get(name); if (o == null ) { o = createBean(map.get(name)); } return o; } @Override public boolean isSingleton (String name) { Bean bean = map.get(name); return Objects.equals(bean.getScope(),"singleton" ); } @Override public boolean isPrototype (String name) { Bean bean = map.get(name); return Objects.equals(bean.getScope(),"prototype" ); } @Override public boolean containsBean (String name) { Object o = map.get(name); if (o != null ){ return true ; }else { return false ; } } }
ParseXML.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 public class ParseXML { private static Map<String,Bean> map = new HashMap<>(); public static Map<String, Bean> getConfig (String pathName) { SAXReader saxReader = new SAXReader(); Document document = null ; try { document = saxReader.read(pathName); }catch (Exception e){ e.printStackTrace(); throw new RuntimeException("请检查您的xml配置文件是否正确" ); } String Xpath = "//bean" ; List<Element> beans = document.selectNodes(Xpath); for (Element element: beans) { Bean bean = new Bean(); String name = element.attributeValue("id" ); String className = element.attributeValue("class" ); String scope = element.attributeValue("scope" ); bean.setName(name); bean.setClassName(className); if (scope!=null ){ bean.setScope(scope); } List<Element> properties = element.elements("property" ); if (properties != null ) { for (Element e:properties) { Property property = new Property(); String pname = e.attributeValue("name" ); String value = e.attributeValue("value" ); String ref = e.attributeValue("ref" ); property.setName(pname); property.setValue(value); property.setRef(ref); bean.getProperties().add(property); } } map.put(name,bean); } return map; } }
Test.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class Test { public static void main (String[] args) { ApplicationContext ct = new ClassPathXmlApplicationContext("resources/applicationContext.xml" ); People people = (People) ct.getBean("people" ); User user = (User) ct.getBean("user" ); Man man = (Man) ct.getBean("man" ); System.out.println(ct.containsBean("man" )); System.out.println(ct.getBean("people" )); System.out.println(ct.getBean("people" )); System.out.println(ct.isSingleton("man" )); System.out.println(ct.isPrototype("man" )); System.out.println(people); System.out.println(user); System.out.println(man); } }