0
点赞
收藏
分享

微信扫一扫

MVC设计模式(2)

拾杨梅记 2022-03-30 阅读 56
MVC

 

  •  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);
    }
}

举报

相关推荐

0 条评论