IOC-Inversion of Control-控制反转
controller,service,DAO层之间的问题
controller,service,DAO在这三层之间调用时,以前都是通过直接new下层实现类的对象,
在controller层中用UserService userService = new UserService();
在
这就导致上层和下层的依赖很强,如果删除下层的话,上层会报错。
系统架构的设计原则是高内聚,低耦合。那如何实现控制反转呢?
1.将原先的直接创建对象的方式摒弃,如在service层中不直接newDAO的对象,而是不赋值。
2.将类之间的关系写在配置文件中。
bean表示这个类,property表示类中的属性,这个属性是自定义的类。
其中全类名是controller的bean中,id值是用来被请求识别的。
全类名是Service,DAO的,id值是被属性的ref识别的。
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE beans [ <!ELEMENT beans (bean*)> <!ELEMENT bean (property*)> <!ELEMENT property (#PCDATA)> <!ATTLIST bean id ID #REQUIRED> <!ATTLIST bean class CDATA #IMPLIED> <!ATTLIST property name CDATA #IMPLIED> <!ATTLIST property ref IDREF #IMPLIED> ]> <!-- IMPLIED 表示可有可无--> <beans> <bean id="userBasicDAO" class="DAO.impl.UserBasicDAOImpl"/> <bean id="topicDAO" class="DAO.impl.TopicDAOImpl"/> <bean id="userBasicService" class="service.impl.UserBasicServiceImpl"> <property name="userBasicDAO" ref="userBasicDAO"/> </bean> <bean id="topicService" class="service.impl.TopicServiceImpl"> <property name="topicDAO" ref="topicDAO"/> </bean> <bean id="user" class="controller.UserController"> <property name="userBasicService" ref="userBasicService"/> <property name="topicService" ref="topicService"/> </bean> <bean id="page" class="myssm.myspringmvc.PageController"/> </beans>
3.将所有的bean加载到Map集合中
形成了这样的对应关系:String:类
userBasic:UserBasicDAOImpl类
user:UserController对象
....
4.直接通过这个方法给属性赋值,这个成为依赖注入(DI-dependency injection)
public class ClassPathXmlApplicationContext implements BeanFactory { //保存每个bean的id和对应的实例对象 private Map<String,Object> beanMap = new HashMap<>(); private String path = "applicationContext.xml" ; //利用这个无参构造器去调用下面的有参构造器 public ClassPathXmlApplicationContext(){ this("applicationContext.xml"); } public ClassPathXmlApplicationContext(String path){ if(StringUtil.isEmpty(path)){ throw new RuntimeException("IOC容器的配置文件没有指定..."); } try { //1.加载xml配置文件,目的是获取xml文件中id和class属性的对应关系并保存到map集合中 InputStream inputStream = getClass().getClassLoader().getResourceAsStream(path); DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); //2.获取到xml文件的对象 Document document = documentBuilder.parse(inputStream); //3.获取xml文件中所有的bean标签对象,放到list集合中 NodeList beanNodeList = document.getElementsByTagName("bean"); //4.遍历每个bean标签对象,将其属性id和class保存到map中 for(int i = 0 ; i<beanNodeList.getLength() ; i++){ //4.1获取每一个bean标签对象 Node beanNode = beanNodeList.item(i); //4.2这行代码先不管,是为了用Element类中的方法 if(beanNode.getNodeType() == Node.ELEMENT_NODE){ Element beanElement = (Element)beanNode; //4.3获取bean标签的属性 String beanId = beanElement.getAttribute("id"); String className = beanElement.getAttribute("class"); //4.4class属性是字符串,转为对应的类 //4.4.1用全类名获取Class对象,controller层,service层,DAO层类和对应的id都放了进去 Class beanClass = Class.forName(className); //4.4.2创建bean实例 Object beanObj = beanClass.newInstance() ; //4.5将bean实例对象保存到map容器中 beanMap.put(beanId , beanObj); } } //设置bean和bean之间的依赖关系 for(int i = 0 ; i<beanNodeList.getLength() ; i++){ Node beanNode = beanNodeList.item(i); if(beanNode.getNodeType() == Node.ELEMENT_NODE) { Element beanElement = (Element) beanNode; String beanId = beanElement.getAttribute("id"); //以上是先获取到xml文件每个bean的id //获取bean节点的子节点,子节点包括注释,换行和空格,元素节点 NodeList beanChildNodeList = beanElement.getChildNodes(); //获取bean节点的总共的子节点进行循环 for (int j = 0; j < beanChildNodeList.getLength() ; j++) { //获取到每个子节点 Node beanChildNode = beanChildNodeList.item(j); //如果这个子节点是元素节点并且元素名等于property if(beanChildNode.getNodeType()==Node.ELEMENT_NODE && "property".equals(beanChildNode.getNodeName())){ //将子节点转为Element类型,是为了使用getAttribute方法 Element propertyElement = (Element) beanChildNode; //获取到子节点的属性 String propertyName = propertyElement.getAttribute("name"); String propertyRef = propertyElement.getAttribute("ref"); //接下来要根据ref对应具体bean的id然后找到对象,再给name对象赋值 //(1)找到ref对应的实例 Object refObj = beanMap.get(propertyRef); //(2)找到当前bean(不是子节点)的类 Object beanObj = beanMap.get(beanId); Class beanClazz = beanObj.getClass(); //(3)找到这个类中的下层属性 Field propertyField = beanClazz.getDeclaredField(propertyName); //(4)给这个属性赋值 propertyField.setAccessible(true); propertyField.set(beanObj,refObj); } } } } } catch (Exception e) { e.printStackTrace(); } } @Override public Object getBean(String id) { return beanMap.get(id); } }
5.上面的这个内容写在构造方法里。
在dispatcher的init方法中创建BeanFactory的对象,就自动将controller,service中的属性赋值了。