Javaweb - Fruit

这一篇博客的主要内容是我跟着尚硅谷敲的一个简单的项目。主要目的就是自己梳理一遍,加深记忆。

项目的结构

  • src
    • fruit
      • controllers
      • dao
      • pojo
    • myssm
      • basedao
      • myspringmvc

其中,dao、pojo、basedao里面是有关JDBC的代码,不是重点。
重点是下面几个类:

  • controllers
    • FruitContriller
  • myspringvc
    • DispatcherServlet
    • ViewBaseServlet

还有:applicationContext.xml和web.xml这两个配置文件

具体实现

ViewBaseServlet

这一个是继承与HttpServlet的一个类,之后我们的servlet去继承ViewBaseServlet的类的目的是用里面的一个方法。
这其实是在使用Thymeleaf视图模块技术。
而我们这样做的目的是使用ViewBaseServlet中的一个方法:
在这里插入图片描述
这个方法有三个参数:
1.templateName
2.req
3.resp
此处templateName是视图名称
req和resp对应的就是view-prefixview-suff
view-prefixview-suff是在web.xml文件,被成为前缀和后缀。
配置文件的写法

其中里面的<param-value>value</param-value>中的value的值就是后面会替换的值

即上面的方法调用后会得到下面的值:
thymeleaf会将这个 逻辑视图的名称 对应到 物理视图 名称上去
逻辑视图名称 :templateName
物理视图名称 : view-prefix + 逻辑视图名称 + view-suffix
所以真是的视图名称是 : / templateName.html

DispatcherServlet

这个类是这个项目的核心类。
首先,我们思考一个问题,如果我们为每一个请求都设置一个Servlet,那么我们在一个项目里面要写多少个Servlet,里面又有多少重复冗余的代码。因此,我们有必要创建一个类,来统合并且将请求分配给对应的Servlet。
这一个类的主要思想就是利用了反射技术,来获取各种信息,并分配到对应的类中处理。

1)第一步:
我们需要根据收到的请求来分配到对应的Servlet。
这里我们需要用到request.getServletPath()方法

具体思想如下
假设url时:http://localhost:8080/08_fruit2_0_mvc_dispatcherServlet/hly.do
那么servletPath是: /hly.do
思路是:
第一步:/hly.do -> hly
第二步: hly -> hlyController

代码如下:

	String servletPath = req.getServletPath();
	servletPath = servletPath.substring(1);
	int lastDotIndex = servletPath.lastIndexOf(".do");
	servletPath = servletPath.substring(0,lastDotIndex);

此时,我们就可以获得一个 fruit。之后,我们就需要根据fruit来寻找对应的Servlet,如何来实现这一个功能呢?

2)第二步
此时,就需要看我们写的applicationContext.xml文件
applicationContext.xml
此时就将fruit和com.vector.fruit.controllers.FruitController联系到一起。
所以,我们需要加载一下applicationContext.xml配置文件
我们在init()方法中将这两个值存储在HashMap集合中
完整代码如下:

private Map<String,Object> beanMap = new HashMap<>();
public void init() throws ServletException {
        super.init();
        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 controllerBeanClass = Class.forName(className);
                    Object beanObj = controllerBeanClass.newInstance();

                    beanMap.put(beanid,beanObj);

                }
            }

        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }  catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (SAXException e) {
            e.printStackTrace();
        }

    }

逐一分析:
我们首先需要获取配置文件的信息
用Document对象获取applicationContext.xml文件
我们使用Document对象获取applicationContext.xml的文件信息。之后,获取配置文件中的所有bean节点。

	NodeList beanNodeList = document.getElementsByTagName("bean");

然后,利用循环,将所有bean节点的信息存储进Map中。
在这里插入图片描述
可以看到,beanid=“fruit”,beanObj=“FruitConttoller@3217”。
在这里插入图片描述
在Map中也成功存入。然后,就可以进行下一步操作。

3)第三步
我们创建一个service方法,将第一步的代码放入,然后我们利用第一步获得的fruit来获得Map中对应的类名

	Object controllerBeanObj = beanMap.get(servletPath);

接下来看下面的代码

String operate = req.getParameter("operate");
        if (StringUtil.isEmpty(operate)){
            operate = "index";
        }

根据html的代码,可以知道,operate是各种操作的name。所以,当operate为空时,我们是要进入默认页面(“index.html”)

4)第四步
在判断用过使用那个Servlet后,根据用户的请求来安排对应的方法。

Method[] methods = controllerBeanObj.getClass().getDeclaredMethods();
根据反射来获取类的所有方法,然后循环判断使用那个方法

	for (Method method : methods){
         if (operate.equals(method.getName())) {
         	.......
         }

之后,获取当前方法的参数,返回参数数组。(使用getParameters())
Parameter[] parameters = method.getParameters();
定义一个parameterValues 用来承载参数的值

 		Object[] parameterValues = new Object[parameters.length];
				for(int i = 0; i < parameters.length; i++){
                        Parameter parameter = parameters[i];
                        String parameterName = parameter.getName();
                        ......
              }

然后,判断是否是通过请求来获取的参数(即参数名不是req,resp,session)

		if ("req".equals(parameterName)){
                 parameterValues[i] = req;
         }else if ("resp".equals(parameterName)){
                parameterValues[i] = resp;
         }else if ("session".equals(parameterName)){
                parameterValues[i] = req.getSession();
         }else{
         	.......
         }

到此,判断调用那个方法结束

5)第五步
我们要从请求中获取参数的值和类型

	String parameterValue = req.getParameter(parameter.getName());
	String typeName = parameter.getType().getName();

获取类型的原因是,我们通过反射获得的参数值的类型都是String类型,但参数实际上会存在其他类型。所以,我们知道它的类型然后进行转型。如下:

		Object parameterObj = parameterValue;
        if (parameterObj != null){
           if ("java.lang.Integer".equals(typeName)){
                parameterObj = Integer.parseInt(parameterValue);
              }
       }

最后将结果放入parameterValues[]数组中。
parameterValues[i] = parameterObj;

6)第六步
在controller(Servlet)组件中的方法调用,找到和operate同名的方法,通过反射技术调用它

	method.setAccessible(true);
	Object returnObj = method.invoke(controllerBeanObj,parameterValues);

最后,我们进行视图处理

	String methodeReturnStr = (String) returnObj;
    if (methodeReturnStr.startsWith("redirect:")){//比如:redirect:fruit.do
         String redirectStr = methodeReturnStr.substring("redirect:".length());
         resp.sendRedirect(redirectStr);
    }else {
         super.processTemplate(methodeReturnStr,req,resp);   //比如:"edit"
   }
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值