一、MVC概念
1. MVC : Model(模型)、View(视图)、Controller(控制器)
视图层:用于做数据展示以及和用户交互的一个界面
控制层:能够接受客户端的请求,具体的业务功能还是需要借助于模型组件来完成
模型层:模型分为很多种:有比较简单的pojo/vo(value object),有业务模型组件,有数据访问层组件。
1)pojo/vo : 值对象模型 2)DAO : 数据访问对象模型
3)BO : 业务对象模型 4)DTO:数据传输对象模型
2. 区分业务对象和数据访问对象
DAO
中的方法都是单精度方法
或者称之为细粒度方法
。什么叫单精度?一个方法只考虑一个操作,比如添加,那就是insert操作、查询那就是select操作…BO
中的方法属于业务方法
或者称之为粗粒度方法
,而实际的业务是比较复杂的,因此业务方法的粒度是比较粗的。例如:
注册这个功能属于业务功能,也就是说注册这个方法属于业务方法。
那么这个业务方法中包含了多个DAO方法。也就是说注册这个业务功能需要通过多个DAO方法的组合调用,从而完成注册功能的开发。
注册举例:
1)检查用户名是否已经被注册 - DAO中的select操作
2)向用户表新增一条新用户记录 - DAO中的insert操作
3)向用户积分表新增一条记录(新用户默认初始化积分100分) - DAO中的insert操作
4)向系统消息表新增一条记录(某某某新用户注册了,需要根据通讯录信息向他的联系人推送消息) - DAO中的insert操作
5)向系统日志表新增一条记录(某用户在某IP在某年某月某日某时某分某秒某毫秒注册) - DAO中的insert操作
6)…
3. 在系统中添加业务层组件。(FruitService 调用FruitDao)图示:
public class FruitServiceImpl implements FruitService { private FruitDAO fruitDAO = new FruitDAOImpl(); @Override public List<Fruit> getFruitList() { return fruitDAO.getFruitList(); } @Override public Fruit getFruitById(int fid) { return fruitDAO.getFruitById(fid); } } public class FruitController { private FruitService fruitService = new FruitServiceImpl(); //调用改为 fruitService.XXX }
二、MVC-IOC
1. 耦合/依赖:
在软件系统中,层与层之间是存在依赖的。我们也称之为耦合。
我们系统架构或者是设计的一个原则是: 高内聚低耦合。层内部的组成应该是高度聚合的,而层与层之间的关系应该是低耦合的,最理想的情况0耦合(就是没有耦合)
实现下层改动不会影响上层(比如:删掉serviceImpl不会出错)
代码实现
1)配置文件
<beans> <!--1. 先描述需要什么组件 2. 再描述组件与组件之间的依赖关系--> <bean id ="fruitDAO" class="com.fruits.dao.Impl.FruitDAOImpl"> </bean> <bean id ="fruitService" class="com.fruits.ser.impl.FruitServiceImpl"> <!-- property 标签用来表示属性:name表示属性名,ref表示引用其他bean的id值 --> <!-- FruitServiceImpl需要DAO: private FruitDAO fruitDAO = new FruitDAOImpl(); 所以 name 就是FruitServiceImpl中需要的属性名 而 ref 就引用到 某个bean的id:fruitDAO --> <property name="fruitDAO" ref="fruitDAO"></property> </bean> <bean id="fruit" class="com.fruits.controller.FruitController"> <property name="fruitService" ref="fruitService"></property> </bean> </beans>
2)创建一个Bean工厂,用来实现根据bean中的id获得class对象
package com.fruits.io; public interface BeanFactory { Object getBean(String id); //根据bean中的id 获得class对象 }
实现类
public class ClassPathXmlApplicationContext implements BeanFactory{ private Map<String,Object> beanMap = new HashMap<>(); public ClassPathXmlApplicationContext() { try { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("applicationContext.xml"); //1.创建DocumentBuilderFactory DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); //2.创建DocumentBuilder对象 DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); //3.创建Document对象 Document document = documentBuilder.parse(inputStream); //4.获取所有的bean节点 NodeList beanNodeList = document.getElementsByTagName("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"); String className = beanElement.getAttribute("class"); Class beanClass = Class.forName(className); //创建bean实例 Object beanObj = beanClass.newInstance(); //将bean实例对象保存再map容器中 beanMap.put(beanId, beanObj); } } //5. 组装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"); //fruitService NodeList beanChildNodesList = beanElement.getChildNodes(); for (int j = 0; j < beanChildNodesList.getLength(); j++) { Node beanChildNode = beanChildNodesList.item(j); if(beanChildNode.getNodeType()==Node.ELEMENT_NODE && "property".equals(beanChildNode.getNodeName())){ Element propertyElement = (Element)beanChildNode; String propertyName = propertyElement.getAttribute("name"); // propertyName=fruitDAO String propertyRef = propertyElement.getAttribute("ref"); // propertyRef=fruitDAO //1)找到propertyRef对应的实例 Object refObj = beanMap.get(propertyRef); //refObj = com.fruits.dao.Impl.FruitDAOImpl //2)将refObj设置到当前bean对应的实例的property属性上去 Object beanObj = beanMap.get(beanId); // beanObj = com.fruits.ser.impl.FruitServiceImpl Class beanClass = beanObj.getClass(); // beanClass = FruitServiceImpl Field propertyField = beanClass.getDeclaredField(propertyName); //获取beanClass 中与propertyName有关的属性 propertyField = private FruitDAO fruitDAO propertyField.setAccessible(true); propertyField.set(beanObj,refObj); //将refObj设置到beanObj对应的propertyField中去 } //refObj = FruitDAOImpl beanObj = FruitServiceImpl propertyField = fruit } } } } catch (IOException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (ParserConfigurationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (SAXException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (NoSuchFieldException e) { e.printStackTrace(); } } @Override public Object getBean(String id) { return beanMap.get(id); } }
3) FruitServiceImpl
public class FruitServiceImpl implements FruitService { private FruitDAO fruitDAO = null; @Override public List<Fruit> getFruitList() { return fruitDAO.getFruitList(); } }
4)FruitController
public class FruitController { private FruitService fruitService = null; }
2. IOC-控制反转 / DI - 依赖注入
控制反转:
1) 之前在Servlet中,我们创建service对象 , FruitService fruitService = new FruitServiceImpl();
这句话如果出现在servlet中的某个方法内部,那么这个fruitService的作用域(生命周期)应该就是这个方法级别;
如果这句话出现在servlet的类中,也就是说fruitService是一个成员变量,那么这个fruitService的作用域(生命周期)应该就是这个servlet实例级别
2) 之后我们在applicationContext.xml中定义了这个fruitService。然后通过解析XML,产生fruitService实例,存放在beanMap中,这个beanMap在一个BeanFactory中
因此,我们转移(改变)了之前的service实例、dao实例等等他们的生命周期。控制权从程序员转移到BeanFactory。这个现象我们称之为控制反转
依赖注入:
1) 之前我们在控制层出现代码:FruitService fruitService = new FruitServiceImpl();
那么,控制层和service层存在耦合。
2) 之后,我们将代码修改成FruitService fruitService = null ;
然后,在配置文件中配置:
<bean id="fruit" class="FruitController">
<property name="fruitService" ref="fruitService"/>
</bean>