【JavaWeb】循序渐进做项目——水果库存系统02

发布于:2023-02-02 ⋅ 阅读:(555) ⋅ 点赞:(0)


本文项目见: JavaWeb: 本仓库介绍JavaWeb的三个项目。 (gitee.com)

项目流程图

  • JavaWeb_mvc
    将各种Servlet转为一个FruitServlet。
    之前的做法是,一个请求对应一个Servlet,这样Servlet太多了。所以把各种Servlet整合为一个FruitServlet。通过一个operator的值决定调用哪个Servlet。这里使用反射获取operator的值。
    在这里插入图片描述

  • JavaWeb_mvc00_dispatcher:引入使用中央控制器。
    由于每一个Servlet中都有反射代码,所以继续抽取,设计一个中央控制器。

  • JavaWeb_mvc01_controller:优化Controller中获取参数和重定向/转发的操作,将视图处理抽取到DispatcherServlet中。

在这里插入图片描述
现在的DispatcherServlet类的工作分为两部分:

  1. 根据url定位到能够处理这个请求的Controller组件

    1. 从url中提取ServletPath:/fruit.do——fruit
    2. 根据fruit找到对应的FruitController组件。具体通过applicationController.xml文件,并且使用DOM技术读取配置文件,在中央控制器中形成一个beanMap容器,存放所有的对应关系。
    3. 根据operator的值定位到FruitController需要调用的方法。
  2. 通过反射调用Controller组件中的方法(index、add、del等)

    1. 获取参数

      获取即将要调用的方法的参数签名信息: Parameter[] parameters = method.getParameters();
      通过parameter.getName()获取参数的名称;准备了Object[] parameterValues 这个数组用来存放对应参数的参数值
      另外,我们需要考虑参数的类型问题,需要做类型转化的工作。通过parameter.getType()获取参数的类型

    2. 执行方法

      Object returnObj = method.invoke(controllerBean , parameterValues);

    3. 视图处理

      String returnStr = (String)returnObj;
      if(returnStr.startWith(“redirect:”)){

      }else if…

  • JavaWeb_mvc02_BO
    在库存系统中添加业务层组件,引入IOC。这里需要我们了解MVC和IOC的基本概念。
    使用ClassPathXmlApplicationContext类创建IOC容器,在其中实现控制反转和依赖注入。
    在这里插入图片描述

补充基础知识

Servlet

Servlet生命周期

Servlet生命周期:实例化、初始化、服务、销毁。

	void init(config) - 初始化方法
	//客户端发送请求,service方法自动执行。
	void service(requst,response) - 服务方法
	void destory() - 销毁方法
  • 继承关系
    • javax.servlet.Servlet接口
      • javax.servlet.GenericServlet抽象类
        • javax.servlet.http.HttpServlet抽象子类
  1. 生命周期:从出生到死亡的过程就是生命周期。对应Servlet中的三个方法:init()、service()、destroy()。
  2. 默认情况下,第一次接收请求:Servlet实例化(调用构造方法)、初始化(调用init)、服务(调用service)。
    从第二次请求开始:每一次都是服务。
    当容器关闭:销毁服务。
  3. Servlet实例tomcat只会创建一个,所有的请求都通过这个实例去响应。
    好处:提高系统的启动速度。
    缺点:第一次请求时耗时较长。
    如果需要提高第一次请求的响应速度,应该设置Servlet初始化时机。
  4. Servlet的初始化时机:
    可以通过<load-on-startup>来设置servlet的启动先后顺序,数字越小,启动越靠前。
  5. Servlet在容器中是单例的(只有一个servlet实例去响应线程)、线程不安全的
    所以:不要在servlet中定义成员变量。如果不得不定义成员变量的值,不要去修改值或做逻辑判断。

Servlet的初始化方法

Servlet中的初始化方法有两个:init() , init(config)。

//其中带参数的方法代码
public void init(ServletConfig config) throws ServletException {
       this.config = config ;
       init();
}
          
//另外一个无参的init方法如下:
public void init() throws ServletException{
}

如果我们想要在Servlet初始化时做一些准备工作,那么我们可以重写init方法。

我们可以通过如下步骤去获取初始化设置的数据

ServletConfig config = getServletConfig(); //获取config对象
config.getInitParameter(key);   //获取初始化参数值
并且在web.xml文件中配置Servlet
<servlet>
    <servlet-name>Demo01Servlet</servlet-name>
    <servlet-class>Demo01Servlet</servlet-class>
    <init-param>
        <param-name>hello</param-name>
        <param-value>world</param-value>
    </init-param>
</servlet>
<servlet-mapping>
    <servlet-name>Demo01Servlet</servlet-name>
    <url-pattern>/demo01</url-pattern>
</servlet-mapping>

也可以通过注解的方式进行配置:

@WebServlet(urlPatterns = {"/demo01"} ,
initParams = {
    @WebInitParam(name="hello",value="world"),
    @WebInitParam(name="uname",value="jim")
})

Servlet的服务方法

javax.servlet.http.HttpServlet抽象类中,service不是抽象方法。

service方法执行流程:
 1. String method = req.getMethod(); 获取请求的方式。
 2. 判断,根据请求方式不同,决定调用不同do方法。
       if (method.equals("GET")) {
                      this.doGet(req, resp);
                  } else if (method.equals("HEAD")) {
                      this.doHead(req, resp);
                  } else if (method.equals("POST")) {
                      this.doPost(req, resp);
                  }
doPost方法:
          protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
                  String msg = lStrings.getString("http.method_post_not_supported");
                  this.sendMethodNotAllowed(req, resp, msg);
              }
sendMethodNotAllowed方法:
          private void sendMethodNotAllowed(HttpServletRequest req, HttpServletResponse resp, String msg) throws IOException {
                  String protocol = req.getProtocol();
                  if (protocol.length() != 0 && !protocol.endsWith("0.9") && !protocol.endsWith("1.0")) {
                      resp.sendError(405, msg);
                } else {
                      resp.sendError(400, msg);
               }

小结:

  1. 服务方法:当有请求过来时,service方法会自动响应(其实是tomcat容器调用的)
  2. 在HttpServlet中的service方法会分析发送请求的方式:到底是get、post还是…然后再决定调用哪个do方法。在HttpServlet中这些do方法都是405的实现风格,所以要我们子类去实现对应的方法,否则默认会报405。

ServletContext(application)和< context-param>

配置文件中的配置如下:

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>applicationContext.xml</param-value>
    </context-param>

要想让application读取到配置文件的信息,可以用以下方法:

  1. 获取ServletContext。
    获取ServletContext,有很多方法。
    可以在初始化方法中: ServletContxt application= getServletContext();
    也可以在服务方法中通过request对象获取,也可以通过session获取:
    request.getServletContext(); session.getServletContext()
  2. 获取上下文初始化参数:String path = application.getInitParameter("contextConfigLocation");

MVC

MVC:Model(模型)、View(视图)、Controller(控制器)

  • 视图层:用于做数据展示以及和用户交互的一个界面。
  • 控制层:能够接受客户端的请求,具体的业务功能还是需要借助于模型组件来完成。
  • 模型层:
    模型分为很多种,有比较简单的pojo/vo(value object),有业务模型组件,有数据访问层组件
    • pojo/vo : 值对象
    • DAO : 数据访问对象
    • BO : 业务对象

    区分业务对象和数据访问对象:
    1) DAO中的方法都是单精度方法(细粒度方法)。什么叫单精度?一个方法只考虑一个操作,比如添加,那就是insert操作、查询那就是select操作…
    2) BO中的方法属于业务方法,而实际的业务是比较复杂的,因此业务方法的粒度是比较粗的。

    举例:
    注册这个功能属于业务功能,也就是说注册这个方法属于业务方法。
    那么这个业务方法中包含了多个DAO方法。也就是说注册这个业务功能需要通过多个DAO方法的组合调用,从而完成注册功能的开发。
    注册:

    • 检查用户名是否已经被注册 - DAO中的select操作

    • 向用户表新增一条新用户记录 - DAO中的insert操作

    • 向用户积分表新增一条记录(新用户默认初始化积分100分) - DAO中的insert操作等。

IOC

  1. 耦合/依赖
    依赖指的是某某某离不开某某某。在软件系统中,层与层之间是存在依赖的。我们也称之为耦合。我们设计的一个原则是: 高内聚低耦合
    即:层内部的组成应该是高度聚合的,而层与层之间的关系应该是低耦合的,最理想的情况0耦合(就是没有耦合)
  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>
      

      这样控制层中就存在了Service的依赖。

  • 关于XML:
    概念
    HTML:超文本标记语言
    XML:可扩展的标记语言
    可以认为:HTML是XML的子集。
  • XML的三个部分:
  1. XML声明 , 必须在文件的第一行。
  2. DTD 文档类型定义
  3. XML正文
  • Java8新特性
    Parameter[] parameters = method.getParameters();
    String name = parameters[0].getName();
    默认获取名称为args0、1等。其实可以获取到属性名。
    操作:
    IDEA下:文件——设置——构建、运行、部署——Java编译器—— 在这里插入图片描述

常见错误:argument type mismatch
使用反射传入pageNo时,parameterValue获取的是字符串类型。所以错误。

本文含有隐藏内容,请 开通VIP 后查看