Servlet 之超详解【2023年最新版】

编译软件:IntelliJ IDEA 2019.2.4 x64
操作系统:win10 x64 位 家庭版
服务器软件:apache-tomcat-8.5.27



一. 什么是Servlet?

Servlet,英文全称为Server Applet,意为服务器端的一小程序。它运行在支持Java Servlet规范的Web服务器上。在服务器上部署的众多web应用程序中,无论它是B/S架构,还是C/S架构,它们的基本运行模式如下图所示(以Java程序为例):
在这里插入图片描述

如果把上述web应用的运行模式比作是一个正在在营业的餐厅,那Servlet在其中扮演着服务员的角色,客人进店吃饭(客户端发出请求),它负责给客人上菜单,等客人点好菜单,servlet就会通知厨子(web应用)做菜(处理业务),厨子做好菜,就让服务员去上菜(响应)给客人。

总的来说,Servlet就是和客户端进行交互(处理请求和响应)

Servlet通常被用于创建像电子商务网站、社交媒体应用、在线银行网站等类似的Web应用程序。通过使用Servlet,开发人员可以以一种可移植、灵活、高效和可重用的方式构建Web应用程序。


二. 如何编写第一个servlet程序?

步骤:

①创建一个html页面,在上面创建一个超链接(计划访问后台的HelloServlet)

<a href="">访问HelloServlet</a>

②创建HelloServlet

  1. 新建一个普通类HelloServelet
  2. HelloServelet实现接口Servlet
  3. 实现接口中的所有抽象方法

代码演示如下:

import javax.servlet.*;
import java.io.IOException;
import java.util.Enumeration;

public class HelloServelet implements Servlet {
    
    

   

    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
    
    
	

    }


    @Override
    public ServletConfig getServletConfig() {
    
    
        return null;
    }

    /**
     * 主要功能:处理客户端的请求和响应
     * @param servletRequest  处理请求
     * @param servletResponse  处理响应
     * @throws ServletException
     * @throws IOException
     */
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
    
    
        System.out.println("访问了HelloServlet类的service方法.......");


     
    }

    @Override
    public String getServletInfo() {
    
    
        return null;
    }

    @Override
    public void destroy() {
    
    
        
    }
}

  1. 在web.xml中为HelloServlet设置访问路径

代码演示如下:

<!--  HelloServlet的访问路径:/hello  -->
<servlet>
    <!--   为Servlet起个名字     -->
    <servlet-name>abc</servlet-name>
    <!--   Servlet的全类名     -->
    <servlet-class>t1.HelloServelet</servlet-class>
</servlet>
<servlet-mapping>
    <!--  要和Servlet中的servlet-name的名字一致      -->
    <servlet-name>abc</servlet-name>
    <!--   设置访问路径,注意,必须是/开头     -->
    <url-pattern>/hello</url-pattern>
</servlet-mapping>

③将设置到的访问路径设置给超链接

<a href="hello">访问HelloServlet</a>

④测试HelloServelt

在这里插入图片描述
在这里插入图片描述

注意:

  1. 网页必须直接在web下(不能放在WEB-INF下)
  2. web.xml中url-pattern的值必须以/开头
  3. 网页的请求路径,不能以/开头
  4. 网页不能采用静态的打开方式

请求原理如下所示:

在这里插入图片描述

Servlet的对象管理:

  • Servlet的实例对象由Servlet容器(tomcat)负责创建

  • Servlet的方法由容器在特定情况下调用

  • Servlet容器会在web应用卸载时(tomcat停止)销毁Servlet对象的实例


三. Servlet的生命周期

Servlet接口中一共有五个方法,其中有三个和生命周期有(init/service/destroy)的方法。

请求过程:

  1. 默认情况下在第一次发出请求时,Servlet容器(tomcat服务器)会创建相应的servlet对象、进行初始化(执行init方法)、执行service方法
  2. 第二次以及以后的每一次请求发出,都直接执行service方法
  3. 在web应用被卸载(服务器被停止时)的时候,servlet对象会被销毁,销毁之前执行destroy方法

Servlet可以在启动服务器时就创建其对象吗?

可以

方法如下:

在web-xml中当前Servlet的servlet标签内添加标签

//设置自启动
<load-on-startup>l</1oad-on-startup>

代码演示如下:

<!--   设置自启动,若有多个servlet都设了自启动,根据标签体中的值来判定启动优先级,值越小,优先级越高     -->
<!--     <load-on-startup></load-on-startup>中的标签体为非0的整数     -->
        <load-on-startup>1</load-on-startup>

四. Servlet的技术体系

创建Servlet的三种方式:

①实现Servlet接口

重写其五个方法(重点关注的只有一个service),其他四个还必须重写

案例:创建HelloServelet 类实现Servlet 接口

代码演示如下:

import javax.servlet.*;
import java.io.IOException;
import java.util.Enumeration;

public class HelloServelet implements Servlet {
    
    

   

    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
    
    
	
    }


    @Override
    public ServletConfig getServletConfig() {
    
    
        return null;
    }

    /**
     * 主要功能:处理客户端的请求和响应
     * @param servletRequest  处理请求
     * @param servletResponse  处理响应
     * @throws ServletException
     * @throws IOException
     */
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
    
    
        System.out.println("访问了HelloServlet类的service方法.......");


     
    }

    @Override
    public String getServletInfo() {
    
    
        return null;
    }

    @Override
    public void destroy() {
    
    
        
    }
}

web-xml创建其访问路径:

<!--  HelloServlet的访问路径:/hello  -->
<servlet>
    <!--   为Servlet起个名字     -->
    <servlet-name>abc</servlet-name>
    <!--   Servlet的全类名     -->
    <servlet-class>t1.HelloServelet</servlet-class>
</servlet>
<servlet-mapping>
    <!--  要和Servlet中的servlet-name的名字一致      -->
    <servlet-name>abc</servlet-name>
    <!--   设置访问路径,注意,必须是/开头     -->
    <url-pattern>/hello</url-pattern>
</servlet-mapping>

在这里插入图片描述

在这里插入图片描述

②继承GenericServlet

步骤:

  1. 创建一个类
  2. 继承一个抽象类(GenericServlet)
  3. 实现抽象类中的抽象方法
  4. 在web-xml中配置Servlet的访问路径

注意:

GenericServlet主要的功能是将service以外的四个方法做了实现。我们自己的Servlet只需要实现service方法即可,如果想用其他的四个方法,可以采用重写。

案例:创建MyFirstServlet类继承GenericServlet类

代码演示如下:

public class MyFirstServlet extends GenericServlet {
    
    

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
    
    
        System.out.println("触发了继承GenericServlet类的子类MyFirstServlet类的service方法......");


    }
}

web-xm配置其访问路径:

<!-- 设置访问MyFirstServlet的路径:/myfirst   -->
    <servlet>
        <servlet-name>def</servlet-name>
        <servlet-class>t1.MyFirstServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>def</servlet-name>
        <url-pattern>/myfirst</url-pattern>
    </servlet-mapping>

在这里插入图片描述

在这里插入图片描述

③继承HttpServlet

步骤:

  1. 创建一个类

  2. 继承一个抽象类(HttpServlet)

  3. 重写两个方法doGet和doPost

  4. 配置Servlet的访问路径

注意:

HttpServlet主要功能是实现service方法,然后对请求进行分发的操作(不同的请求方式调用不同的方法)。get请求调用doGet方法post请求调用doPost方法

案例:创建MyFirstServlet类继承GenericServlet类

代码演示如下:

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class SecondServlet extends HttpServlet {
    
    
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
    
        System.out.println("访问了SecondServlet里的doPost方法");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
    
        System.out.println("访问了SecondServlet里的doGet方法");
    }
}

web-xm配置其访问路径:

<!-- 设置访问SecondServlet的路径:/second   -->
    <servlet>
        <servlet-name>SecondServlet</servlet-name>
        <servlet-class>t1.SecondServlet</servlet-class>
    </servlet>


    <servlet-mapping>
        <servlet-name>SecondServlet</servlet-name>
        <url-pattern>/second</url-pattern>
    </servlet-mapping>

在这里插入图片描述
在这里插入图片描述


五. web项目中的两个接口

5.1 Servletconfig接口

一个Servlet对象对应唯一的一个ServletConfigi配置对象

Servletconfig对象如何获得?

在init方法的形参位置,Servletconfig是在当前Servlet进行初始化的时候,传递给init方法的

主要功能:

  1. 获取Servlet名称(web.xml中servlet-name的值)

    在这里插入图片描述

       //获取Servlet名称
       String servletName = servletConfig.getServletName();
       System.out.println("servletName:"+servletName);
       
    
    
  2. 获取全局上下文(ServletContext)对象

    //获取全局上下文对象
    ServletContext servletContext = servletConfig.getServletContext();
    System.out.println("servletContext:"+servletContext);
    
  3. 获取Servlet初始化参数

    获取Servlet初始化参数的前提是要有servlet的初始化参数

    如何设置servlet的初始化参数?

    位置:web.xml的servlet标签内,在子标签1–>之上设置,不然报错

    代码演示如下:

     		<!--设置当前Servlet的初始化参数-->
    	   <init-param>
    	   <param-name>path</param-name>
    	   <param-value>classpath:springmvc.xml</param-value>
    	   </init-param>
    

    获取Servlet初始化参数:

    	   //获取web-xml中的servlet标签内的初始化参数<param-name>为path的value值
    	   String path = servletConfig.getInitParameter("path");
    	   System.out.println("path:"+path);
    	   
    	   //获取HelloServelet内所有初始化参数的<param-name>【key】值
    	   Enumeration<String> initParameterNames = servletConfig.getInitParameterNames();
    	   while (initParameterNames.hasMoreElements()){
          
          
    	         System.out.println("initParameterNames.nextElement():"+initParameterNames.nextElement());
    	           
    	   }
    

案例:在HelloServelet类实现上述功能

代码演示如下:

import javax.servlet.*;
import java.io.IOException;
import java.util.Enumeration;

public class HelloServelet implements Servlet {
    
    

    public HelloServelet() {
    
    
        System.out.println("HelloServelet类的构造方法被执行了");
    }

    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
    
    

        System.out.println("HelloServelet类的init方法被执行了");

        //1.获取的是当前Servlet名称(web.xml中配置servlet-name的值)
        String servletName = servletConfig.getServletName();
        System.out.println("servletName:"+servletName);

        //2.获取全局上下文ServletContext对象
        ServletContext servletContext = servletConfig.getServletContext();
        System.out.println("servletContext:"+servletContext);


        //3.获取Servlet初始化参数
        //获取web-xml中的servlet标签内的初始化参数<param-name>为path的value值
        String path = servletConfig.getInitParameter("path");
        System.out.println("path:"+path);

        //获取web-xml中的初始化参数<param-name>为aaa的value值
        String aaa = servletConfig.getInitParameter("aaa");
        System.out.println("aaa:"+aaa);


        //获取HelloServelet内所有初始化参数的<param-name>【key】值
        Enumeration<String> initParameterNames = servletConfig.getInitParameterNames();
        while (initParameterNames.hasMoreElements()){
    
    
            System.out.println("initParameterNames.nextElement():"+initParameterNames.nextElement());
        }

    }


    @Override
    public ServletConfig getServletConfig() {
    
    
        return null;
    }

    /**
     * 主要功能:处理客户端的请求和响应
     * @param servletRequest  处理请求
     * @param servletResponse  处理响应
     * @throws ServletException
     * @throws IOException
     */
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
    
    
        System.out.println("访问了HelloServlet类的service方法.......");

    }

    @Override
    public String getServletInfo() {
    
    
        return null;
    }

    @Override
    public void destroy() {
    
    
        System.out.println("HelloServelet类的destroy方法被执行了");
    }
}

在这里插入图片描述

5.2 ServletContext接口

注意:

  • 一个web项目只有一个ServletContext对象,代表当前Web应用
  • 所有的Servlet共享同一个ServletContext对象,所以ServletContext对象也被称为 application 对象(Web应用程序对象)
  • ServletContext对象是由服务器启动的时候,Tomcat去创建,在项目卸载时(服务器关机时)销毁。

如何获得到ServletContext对象?

  1. 通过ServletConfig去获得

    //使用servletConfig获取全局上下文ServletContext对象
    ServletContext servletContext = servletConfig.getServletContext();
    System.out.println("servletContext:"+servletContext);
    
  2. 通过request去获得

    //使用servletRequest获取servletcontext对象(全局上下文对象)
    ServletContext servletContext = servletRequest.getServletContext();
    System.out.println("servletContext:"+servletContext);
    

主要功能:

  • 获取项目的上下文路径(带"/"的项目名)

    //1.获取项目的上下文路径(带/的项目名)
    String contextPath = servletContext.getContextPath();
    System.out.println("contextPath:"+contextPath);
    
  • 获取虚拟路径所映射的本地真实路径

    用途:上传或下载文件时需要使用

    //2.获取虚拟路径所映射的本地真实路径(根据相对路径获得绝对路径)
    String realPath = servletContext.getRealPath("/upload");
    System.out.println("realPath:"+realPath);
    
  • 获取WEB应用程序的全局初始化参数

    获取全局初始化参数的前提是你得有全局初始化参数,没有的话就得设置

    如何设置全局初始化参数?

    位置:web.xml 的根节点上

    在这里插入图片描述

    web.xml上的相应代码如下所示:

    <context-param>
        <param-name>context-key</param-name>
        <param-value>context-value</param-value>
    </context-param>
     <context-param>
         <param-name>ccc</param-name>
         <param-value>ddd</param-value>
     </context-param>
    
    //3.获取WEB应用程序的全局初始化参数
    //获取全局初始化参数中<param-name>【 key】为ccc的value值
    String ccc = servletContext.getInitParameter("ccc");
    System.out.println("ccc="+ccc);
    
    //获取全局初始化参数中<param-name>【 context-key】为ccc的value值
    String context_key = servletContext.getInitParameter("context-key");
    System.out.println("context_key="+context_key);
    
    //获取全局初始化参数中所有的<param-name>【 key】值
    Enumeration<String> initParameterNames = servletContext.getInitParameterNames();
    while (initParameterNames.hasMoreElements()){
          
          
        System.out.println("nextElement:"+initParameterNames.nextElement());
    }
    

  • 作为域对象共享数据 ★

    域对象:在一定的作用域范围内共享数据的对象

    ServletContext所用域范围是整个web项目

    测试:

    1.在一个Servlet内,往ServletContext对象中设置共享数据

    2.在另外一个Servlet内获取到共享的数据

    方法:

    servletcontext.setAttribute(string key,object obj);
    object msg servletcontext.getAttribute(string key);
    
    //移除指定的共享数据(指定key,删除对应的value)
    servletcontext.removeAttribute(Sstring key);
    

    案例:尝试使用SecondServlet删除myFirstServlet新建的共享数据test,test的value值自定义

    代码演示如下:

    
     //MyFirstServle新增共享数据test并显示
    import javax.servlet.*;
    import java.io.IOException;
    
    public class MyFirstServlet extends GenericServlet {
          
          
    
        @Override
        public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
          
          
            System.out.println("触发了继承GenericServlet类的子类MyFirstServlet类的service方法......");
    
            //如何在servletContext作用域范围内添加共享数据
            ServletContext servletContext = servletRequest.getServletContext();
            servletContext.setAttribute("msg","这是MyFirstServlet设置的共享数据");
    
            //MyFirstServle新增共享数据test并显示
            servletContext.setAttribute("test","这是MyFirstServlet设置的test共享数据");
    
            System.out.println("test="+servletContext.getAttribute("test"));
        }
    }
    
    //SecondServlet尝试删除并显示MyFirstServlet设置的共享数据test
    import javax.servlet.ServletContext;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    public class SecondServlet extends HttpServlet {
          
          
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
          
          
            System.out.println("访问了SecondServlet里的doPost方法");
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
          
          
            System.out.println("访问了SecondServlet里的doGet方法");
    
            //如何获取MyFirstServlet在servletContext作用域范围内设置的共享数据
            ServletContext servletContext = request.getServletContext();
            Object msg = servletContext.getAttribute("msg");
            System.out.println("msg="+msg);
    
    
            //删除MyFirstServlet设置的共享数据test
            servletContext.removeAttribute("test");
            System.out.println("SecondServlet已经删除了共享数据test");
    
            System.out.println("共享数据test="+servletContext.getAttribute("test"));
        }
    }
    

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述


六. 处理请求和响应的接口

6.1 HttpservletRequest接口

简介:

该接口是ServletRequest接口的子接口,封装了HTTP请求的相关信息(请求报文中的所有信息都被封装在里面)

主要功能:

  1. 获得请求头信息

    request.getHeader(String key);
    
    //获取请求头中客户端的产品信息
    String header = request.getHeader("User-Agent");
    System.out.println("客户端的产品信息如下");
    System.out.println("header:"+header);
    
    //获取请求头中请求来源
    String referer = request.getHeader("Referer");
    System.out.println("请求来源如下");
    System.out.println("referer:"+referer);
    
  2. 获得url的路径信息

    request.getContextPath()//获取上下文路径
    request.getServerName()  //获取主机名
    request.getServerPort()  //获取端口号
    
    //获取url
    String contextPath = request.getContextPath();//获取上下文路径
    System.out.println("contextPath:"+contextPath);
    
    //获取主机名
    String serverName = request.getServerName();
    System.out.println("serverName:"+serverName);
    
    //获取端口号
    int serverPort = request.getServerPort();
    System.out.println("serverPort:"+serverPort);
    
  3. 获得请求参数

    请求参数是什么?
    请求参数就是浏览器向服务器提交的数据。

    浏览器向服务器如何发送数据?

    ① 附在url后面(和get请求一致,拼接的形式就行请求数据的绑定),如下下所示:

    http://localhost:8989/MyServlet/MyHttpServlet?userId=20&username=jack
    

    ② 通过表单提交

    	  <form action="MyHttpServlet" method="post">
    	  	你喜欢的足球队<br /><br />
    	  	巴西<input type="checkbox" name="soccerTeam" value="Brazil" />
    	  	德国<input type="checkbox" name="soccerTeam" value="German" />
    	  	荷兰<input type="checkbox" name="soccerTeam" value="Holland" />
    	  	<input type="submit" value="提交" />
    	  </form>
    	  
    

    使用HttpServletRequest对象获取请求参数

    string request.getparameter(string key);
    string[] request.getparametervalues("hobby");
    
    //获取请求参数
    //根据表单的name值获取它的value值
    String username = request.getParameter("username");//获取表单中name为username的value值,下同
    System.out.println("username="+username);
    
    String password = request.getParameter("password");
    System.out.println("password="+password);
    
    String gender = request.getParameter("gender");
    System.out.println("gender="+gender);
    
    //如何根据一个name获取有多个并存的value值
    String[] hobbies = request.getParameterValues("hobby");
    System.out.println("hobbies:");
    for (String hobby : hobbies) {
          
          
        System.out.println(hobby);
    }
    
  4. 获取请求方式

     request.getMethod()
    
    //获取请求方式
    String method = request.getMethod();
    System.out.println("请求方式:");
    System.out.println("method:"+method);	
    
  5. 转发并共享数据

    转发是进行页面跳转的主要手段之一

    什么是转发?如下图所示

    在这里插入图片描述

    应用场景如下:

    ​ a. 转发到第二个servlet

    //HttpServletRequest对象reg转发客户端请求至register_error的servlet中
    req.getRequestDispatcher("register_error").forward(req,resp);
    

    ​ b. 转发到一个网页

    //HttpServletRequest对象reg转发客户端请求至网页register_error中
    req.getRequestDispatcher("regist_error.html").forward(req,resp);
    

    使用步骤:

    a.获得转发器对象

    RequestDispatcher requestDispatcher=request.getRequestDispatcher(目标路径)

    b.进行转发操作(将request和response需要传递过去)

    requestDispatcher.forward(request,response);
    

    进行页面跳转的代码:

    //跳转至root.html
    request.getRequestDispatcher("root.html").forward(request,response);
    

    Secondservlet能否获得到请求中的请求参数?

    可以的,因为转发将request对象传递过去了

    request可以作为域对象共享数据(请求域)

    应用域(ServletContext):数据共享范围是整个web应用

    请求域(HttpServletRequest):数据共享的范围是本次请求,当响应结束了,请求也结束

    //添加共享数据
    setAttribute(string key,object value);
    //获取共享数据
    getAttribute(string key)
    //移除共享数据
    removeAttribute(string kev)
    

6.2 HttpservletResponse接口

简介:

  • 该接口是ServletResponse接口的子接口,封装了服务器针对于HTTP响应的相关信息。(暂时只有服务器的配置信息,没有具体的和响应体相关的内容)
  • 由Servlet容器创建其实现类对象,并传入service(HttpServletRequest req, HttpServletResponse res)方法中。
  • HttpServletResponse对象指的是容器提供的HttpServletResponse实现类对象。

主要功能:

a.通过输出流将响应数据输出给客户端

Printwriter writer=response.getwriter();
writer.write("<h1>success</h1>");

b.可以设置响应的乱码(添加响应头的方式)

response.addHeader("Content-Type","text/html;charset=utf-8");
简写:response.setContentType("text/html;charset=utf-8");

c.重定向
页面跳转的主要手段之一

什么是重定向?如下图所示:

在这里插入图片描述

应用场景如下:

①重定向至另一个Servlet

response.sendRedirect("second");

​ ②重定向至页面

response.sendRedirect("admin.html");

当发送到firstservlet请求中的请求参数,Secondservlet是否能获取到?

取不到;因为发送了第二次请求(第一次请求中的请求参数和第二次请求毫无关系)

重定向能否使用请求域共享数据?

不可以

转发和重定向的区别

转发 重定向
浏览器的感知 感知不到 感知到
网址栏里的网址 不变 会改变
请求的次数 一次请求 两次请求
请求域中的共享数据 可以访问 不可以访问
发起者 request response
WEB-INF 可以访问 不可以访问
目标资源 本服务器的资源 其他服务器的资源

ps:

WEB-INF下的资源有一个特性:客户端是访问不到的,但是服务器可以访问,可以把不想让客户端直接访问的资源,放在WEB-INF下


七. 请求和响应乱码的处理

7.1 请求乱码

get请求tomcat8已经处理好了

注意:

若你的Tomcat版本为7及以下,就需要修改tomcat的配置文件。在server.xml文件修改Connector标签,添加URIEncoding="utf-8"属性,如下图所示:
在这里插入图片描述
在这里插入图片描述

post请求中文乱码问题

解决方案:

在代码中获取请求参数之前,设置字符集

代码如下:

request.setcharacterEncoding("utf-8");

案例:form表单以post方式向FirstServlet提交数据,演示post乱码问题

代码演示如下:

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class FirstServlet extends HttpServlet {
    
    
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
    
        //请求
        //post方法的中文乱码问题
//        request.setCharacterEncoding("utf-8");
        String username = request.getParameter("username");
        System.out.println("username="+username);
        String password = request.getParameter("password");
        System.out.println("password="+password);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
    
        System.out.println("触发了FirstServlet的doGet方法.......");

       /* //设置请求域的共享数据
        //注意,设置的共享数据需要放在使用请求转发的代码之前,不然另一个servlet无法获取之前设的请求域共享数据
        request.setAttribute("requestMsg","这是requestMsg共享的数据");

        request.setAttribute("msg","这是msg");


        //实现转发跳转至secondservlet
//        request.getRequestDispatcher("second").forward(request,response);

        //实现重定向至secondservlet
        response.sendRedirect("second");
*/
    }
}

在这里插入图片描述

在这里插入图片描述

添加字符集后修改如下:

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class FirstServlet extends HttpServlet {
    
    
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
    
        //请求
        //post方法的中文乱码问题
        request.setCharacterEncoding("utf-8");
        String username = request.getParameter("username");
        System.out.println("username="+username);
        String password = request.getParameter("password");
        System.out.println("password="+password);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
    
        System.out.println("触发了FirstServlet的doGet方法.......");

       /* //设置请求域的共享数据
        //注意,设置的共享数据需要放在使用请求转发的代码之前,不然另一个servlet无法获取之前设的请求域共享数据
        request.setAttribute("requestMsg","这是requestMsg共享的数据");

        request.setAttribute("msg","这是msg");


        //实现转发跳转至secondservlet
//        request.getRequestDispatcher("second").forward(request,response);

        //实现重定向至secondservlet
        response.sendRedirect("second");
*/
    }
}

在这里插入图片描述

7.2 响应乱码

分类:

①获得输出流直接写数据

写法如下:

response.addHeader("Content-Type","text/html;charset=utf-8);
//简写
response.setContentType("text/html;charset=utf-8");

不信?请看如下演示:

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

public class ResponseServlet extends HttpServlet {
    
    
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
    
        doGet(request,response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
    

        //添加响应头以规避中文乱码
//        response.setContentType("text/html;charset=utf-8");

        //输出响应数据流给客户端
        PrintWriter writer = response.getWriter();



        writer.print("<html>");
        writer.print("<head>");
        writer.print("</head>");
        writer.print("<body>");
        writer.print("<h1>success 成功</h1>");
        writer.print("</body>");


        writer.print("</html>");

    }
}

在这里插入图片描述
在这里插入图片描述
代码整体不变,仅添加一句代码如下所示:

		//添加响应头以规避中文乱码
        response.setContentType("text/html;charset=utf-8");

        //输出响应数据流给客户端
        PrintWriter writer = response.getWriter();

在这里插入图片描述

②转发

③重定向


八. 案例:连接数据库版的登录注册功能

案例:实现登录注册功能,要和数据库比对

8.1 注册功能

步骤:

①搭建环境

  1. 导入jar包(数据库驱动包、数据库连接池、DBUtils),放在WEB-INF下的lib目录下
    在这里插入图片描述

  2. 创建]DBCTools和BaseDao
    在这里插入图片描述

  3. 数据库连接池的配置文件(放到src目录下)
    在这里插入图片描述

  4. 在MySQL中创建users表格

  5. 创建实体类(User)

②编写注册功能

  1. 创建注册页面
  2. 创建UsersDAOlmpl
  3. 创建Registe’rServlet

这里提供部分代码:

package DAO;

import bean.users;

import java.sql.SQLException;

public class UsersDAOlmpl extends BaseDAOImpl implements UsersDAO {
    
    

    @Override
    public users findUserByUsername(String username) {
    
    
        String sql="select * from users where username=?";
        try {
    
    
            return getBean(users.class, sql, username);
        } catch (Exception e) {
    
    
           throw new RuntimeException(e);
        }
    }

    @Override
    public boolean addUser(users user) {
    
    
        String sql="insert into users values(null,?,?,?)";
        try {
    
    
            int len = update(sql, user.getUsername(), user.getPassword(), user.getEmail());
            if (len>0){
    
    
                return true;
            }
            return false;
        } catch (SQLException e) {
    
    
            throw new RuntimeException(e);
        }

    }
}

package DAO;

import bean.users;

public interface UsersDAO {
    
    

    //注册
    //根据用户名来查找相应的用户信息
    users findUserByUsername(String username); //登录

    //添加用户
    boolean addUser(users user);



}

8.2 登录功能

步骤:

  1. 创建登录页面
  2. 创建UsersDAOlmpl
  3. 创建LoginServlet

8.3 实现效果

①测试注册功能

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

②测试登录功能

在这里插入图片描述

在这里插入图片描述


九. 使用Beanutils优化代码

简介:

Beanutils是一个Java库,提供了一组实用类,用于处理JavaBeans。JavaBeans是遵循特定约定的Java类,例如具有默认构造函数和用于其属性的getter和setter方法。Beanutils提供了在JavaBeans之间复制属性、将属性值转换为字符串以及执行其他与JavaBeans相关的常见任务的方法。 它是Apache Commons项目的一部分,被广泛用于Java应用程序中。

功能:

自动将map集合中的数据映射到javaBean内

使用条件:

Map集合中的key值必须和javaBean中的属性名一致,不然不一致的那个key值映射不进去

使用步骤:

  1. 导入jar包

    ①复制下面的三个jar包,然后粘贴至要应用的本地动态web项目中Web/WEB-INF/lib下
    在这里插入图片描述
    在这里插入图片描述
    ②在lib目录中选中刚才的三个jar包,右击打开–> Add as Library --> Create Library里选相应的参数
    在这里插入图片描述
    在这里插入图片描述

  2. 直接使用Beanutils调用方法即可

案例如下:对上述连接数据库版的登录注册功能的案例中的注册功能使用Beanutils进行优化

代码演示如下:

import DAO.UsersDAOlmpl;
import bean.users;
import org.apache.commons.beanutils.BeanUtils;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;

public class RegistServlet extends HttpServlet {
    
    

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        super.doGet(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        //处理注册业务
      /*  String username = req.getParameter("username");
        String password = req.getParameter("password");
        String email = req.getParameter("email");
        //数据库中user表的uid字段是自增长列
        users user=new users(null,username,password,email);*/


        //使用BeanUtils,减少代码冗余,这样即使注册表单中有300个name属性值,我也不用一个个手动输入,然后再一一添加进user对象里,那样代码量太大
        Map<String, String[]> parameterMap = req.getParameterMap();//map集合中的key值是表单中的name属性值
        users user=new users();

        try {
    
    
            BeanUtils.populate(user,parameterMap);
        } catch (IllegalAccessException e) {
    
    
            e.printStackTrace();
        } catch (InvocationTargetException e) {
    
    
            e.printStackTrace();
        }


        UsersDAOlmpl dao=new UsersDAOlmpl();
        users userByUsername = dao.findUserByUsername(user.getUsername());

        if (userByUsername==null){
    
    
            dao.addUser(user);
            req.getRequestDispatcher("regist_success.html").forward(req,resp);
        }else {
    
    
            req.getRequestDispatcher("regist_error.html").forward(req,resp);
        }
    }
}


十. web项目的路径问题 ★

10.1 url(常用)

释义:

统一资源定位符(从整个网络环境中找一个资源),例如http://localhost:9999/day06_servlet_war_exploded/login.html

相对路径相对于ur1去作为参照物
绝对路径url就是绝对路径(从ip地址开始)

涉及到路径的位置有:

  • 网页中的超链接、表单、link、img、script…

  • 转发

  • 重定向

  • web.xml

注意:

在web项目内,路径前添加/,采用的是绝对路径

建议使用绝对路径,因为绝对路径一旦写死,不管该资源上有多少个上级目录,依然可以找到,若用相对路径,则要时刻注意该资源的上级目录的变化

/代表什么含义?

  1. 服务器解析

    从当前项目下开始找  例如http://localhost:9999/day06_servlet_war_exploded/
    

    ①web.xml

    例如:

     <!--  设置访问loginservlet的路径  /login  -->
    <servlet>
        <servlet-name>loginServlet</servlet-name>
        <servlet-class>t1.LoginServlet</servlet-class>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>loginServlet</servlet-name>
        <url-pattern>/login</url-pattern>
    </servlet-mapping>
    
    

    上述样例设置LoginServlet的访问路径是直接从项目下访问login

     <!--  设置访问loginservlet的路径  /abc/login  -->
    <servlet>
        <servlet-name>loginServlet</servlet-name>
        <servlet-class>t1.LoginServlet</servlet-class>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>loginServlet</servlet-name>
    	<url-pattern>/abc/login</url-pattern>
    </servlet-mapping>
    

    上述样例设置Loginservlet的访问路径是从项目下要先找到abc,然后从abc下找login就能找到LoginServlet

    ②转发
    例如:

    //建议使用绝对路径
    request.getRequestDispatcher("/second").forward(request,response);
    //http://localhost:9999/day06_servlet_war_exploded/second
    
    
  2. 浏览器解析

    从当前服务器下去找  例如:在 http://localhost:9999/ 下去找
    

    ①网页内所有的路径(超链接、表单、link、img、script…)

    ②重定向

    代码举例如下:

    response.sendRedirect("second");//不加/,默认前面是./second
    //重定向是在浏览器里解析,它是从当前服务器下去找,即http://localhost:9999/second去找,绝对找不到,遂报404
    
    /*
    
    解决方案:在second前面加上/day06_servlet_war_exploded/,这样就是在当前项目下去找second,一定能找到
    即从http://localhost:9999/day06_servlet_war_exploded/second去找
    
    但是这样写有一个弊端,若修改了上下文路径,代码里也得改,比较麻烦,扩展性不是很好
    
    推荐这样写,即动态的获取上下文路径+/second
    response.sendRedirect(request.getContextPath()+"/second");
    
    */
    
    
    

总结:

路径前加/,就是使用绝对路径 /的含义,由服务器解析就是当前项目下,由浏览器解析就是当前服务器下,网页内和重定向路径的/由浏览器解析。转发和web.ml中的/由服务器解析

10.2 uri(极少用)

释义:

统一资源标识符(从当前项目下找一个资源)

例如

/day06_servlet_war_exploded/login.html

在名为day06_servlet_war_exploded的war包(在服务器上部署的web项目)下去找login.html


猜你喜欢

转载自blog.csdn.net/siaok/article/details/130111639