- M:model
- V:View
- C:controller
controller
filter
dispatcher
一些区别
model
包括了controller,service, DAO,POJO
controller
一个controller包含几个service。
比如发来了关于user的请求。
先根据.do定位到具体的controller,再根据operate定位具体的方法。
比如定位到登录这个方法,登录时需要先校验,用到UserService,登录后再显示日志,再用到TopicService。
service
一个service包含几个DAO。来得到DAO层的数据。
比如用户登录这个业务操作,登录时需要校验用户密码,登录后需要显示好友列表,这两个操作对应两个DAO,但都和登录有关。
DAO
直接通过sql语句与数据库交互,来返回数据库结果。
如UserDAOImpl就主要负责查询用户表的数据。
TopicDAOImpl就主要负责查询日志表的数据。
当然UserDAOImpl也可能查询别的表中的数据,但这个表和User有关系,比如根据User的Id查询User的好友,就需要去friend表中查。
POJO
简单类,主要是实例化数据库的表,每一张表对应一个类。
整体的过程
想要controller完成登录功能,需要有登录验证和登陆后显示好友列表和日志列表。
登陆验证需要与数据库中取出的数据比对,好友和日志列表需要从数据库取数据
需要有userBasicService实现登录的校验和得到好友列表的数据。
topicService实现得到日志列表的数据。
UserBasicService通过UserDAO获取好友列表和校验数据
TopicService通过调用TopicDAO获取日志列表
dispatcher(中央控制器):拦截servlet请求,通过对URL修改和配置文件比对,找到具体的controller类。
调用controller类后,每个controller都会跳转,则返回字符串参数让中央控制器统一做跳转。
加载配置文件,创建配置文件对象,通过对象获取配置文件中的标签对象
package myssm.myspringmvc;
import myssm.ioc.BeanFactory;
import myssm.myspringmvc.DispatcherServletException;
import myssm.myspringmvc.ViewBaseServlet;
import myssm.util.StringUtil;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
//根据路径找到controller,根据operate参数找到方法,再传入其他参数执行方法
//执行的操作要和方法名相同
//传入的参数的参数名要和方法的参数名相同
@WebServlet("*.do")
public class DispatcherServlet extends ViewBaseServlet {
private BeanFactory beanFactory ;
public DispatcherServlet(){
}
public void init() throws ServletException {
super.init();
ServletContext application = getServletContext();
Object beanFactoryObj = application.getAttribute("beanFactory");
if(beanFactoryObj!=null){
beanFactory = (BeanFactory)beanFactoryObj ;
}else{
throw new RuntimeException("IOC容器获取失败!");
}
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//拦截所以的servlet请求,并删去后缀,以便找到想要访问的具体controller,比如user.do->user
String servletPath = request.getServletPath();
servletPath = servletPath.substring(1);
int lastDotIndex = servletPath.lastIndexOf(".do") ;
servletPath = servletPath.substring(0,lastDotIndex);
//得到想问访问的controller的字符串形式,去找到具体要执行操作的controller的实例
//对应关系已经写到了配置文件中,并将配置文件中的对应关系保存到了map集合中
Object controllerBeanObj = beanFactory.getBean(servletPath);
//找到了具体的controller类后,就要考虑执行这个类的什么方法,先获取页面发过来想要进行的操作
//注意操作名和方法名是相同的
String operate = request.getParameter("operate");
//如果请求中没有想要进行的操作,则默认进行index操作即返回主页。
if(StringUtil.isEmpty(operate)){
operate = "index" ;
}
//获取到方法名之后就要执行这个方法,但是执行方法时要传入参数,所以接下来要获取方法列表中的参数名
try {
//先得到这个类的所以方法名
Method[] methods = controllerBeanObj.getClass().getDeclaredMethods();
for(Method method : methods){
//找到想要执行的方法
if(operate.equals(method.getName())){
//获取这个方法中所以参数的方法名
//getParameter方法返回的是方法名的别名 arg0,arg1....
//通过设置来使编译的class文件中带有具体的方法名
//所以设置完后要进行重新编译 Build
Parameter[] parameters = method.getParameters();
//获取到参数名后,要给参数赋值
Object[] parameterValues = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
//拿到一个参数(参数名)
Parameter parameter = parameters[i];
//先处理特殊情况的赋值,有的方法需要传入HttpServletRequest类型的参数来保存作用域
//但这种参数请求中不会写在URL或表单中,所以要单独赋值
String parameterName = parameter.getName();
if("request".equals(parameterName)){
parameterValues[i] = request ;
}else if("response".equals(parameterName)){
parameterValues[i] = response ;
}else if("session".equals(parameterName)){
parameterValues[i] = request.getSession();
}else{
//一般情况的赋值,参数的名字和传进的参数名字要相同,如loginId
//这里只考虑参数是单个值的情况,不考虑复选框
//通过页面的请求,从请求中获取参数值
String parameterValue = request.getParameter(parameterName);
//获取到的参数都是字符串类型(URL中?后面的参数或表单中的参数),假如方法执行要使用int类型,就会发生参数类型不匹配的错误
//获取要执行的方法中参数的类型,目的是如果参数类型是Integer类型的,则要将页面获取的参数转为Integer类型
String typeName = parameter.getType().getName();
//先将从页面获取到的参数赋给Object类型的对象
Object parameterObj = parameterValue;
if(parameterObj!=null) {
//如果方法中的参数类型是Integer,则将页面获取到的参数转为int类型再赋值
if ("java.lang.Integer".equals(typeName)) {
parameterObj = Integer.parseInt(parameterValue);
}
//按道理来说,从页面上获取到的参数都是String类型,所以如果要传入的参数中有double,Boolean等都要转换
}
parameterValues[i] = parameterObj ;
}
}
//知道了要具体执行什么方法(根据operate判断)和方法的参数,下面就开始执行方法
method.setAccessible(true);
//执行方法controllerBeanObj这个类中的方法,并将参数数组传入
Object returnObj = method.invoke(controllerBeanObj,parameterValues);
String methodReturnStr = (String)returnObj ;
//执行方法后返回一个字符串,根据字符串进行跳转
if(methodReturnStr.startsWith("redirect:")){ //比如: redirect:fruit.do
String redirectStr = methodReturnStr.substring("redirect:".length());
response.sendRedirect(redirectStr);
}else{
super.processTemplate(methodReturnStr,request,response); // 比如: "edit"
}
}
}
} catch (Exception e) {
e.printStackTrace();
throw new DispatcherServletException("DispatcherServlet出错了...");
}
}
}
// 常见错误: IllegalArgumentException: argument type mismatch 需要将页面获取的参数与方法的参数进行匹配
package myssm.ioc;
import myssm.util.StringUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
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对象
Class beanClass = Class.forName(className);
//4.4.2创建bean实例
Object beanObj = beanClass.newInstance() ;
//4.5将bean实例对象保存到map容器中
beanMap.put(beanId , beanObj);
}
}
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");
NodeList beanChildNodeList = beanElement.getChildNodes();
for (int j = 0; j < beanChildNodeList.getLength() ; j++) {
Node beanChildNode = beanChildNodeList.item(j);
if(beanChildNode.getNodeType()==Node.ELEMENT_NODE && "property".equals(beanChildNode.getNodeName())){
Element propertyElement = (Element) beanChildNode;
String propertyName = propertyElement.getAttribute("name");
String propertyRef = propertyElement.getAttribute("ref");
Object refObj = beanMap.get(propertyRef);
Object beanObj = beanMap.get(beanId);
Class beanClazz = beanObj.getClass();
Field propertyField = beanClazz.getDeclaredField(propertyName);
propertyField.setAccessible(true);
propertyField.set(beanObj,refObj);
}
}
}
}
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
@Override
public Object getBean(String id) {
return beanMap.get(id);
}
}