一文读懂之Servlet

1. 概念

Servlet,全称Server Applet服务器端小程序,Servlet指的是必需运行在服务器端的Java小程序,狭义上讲,Servlet指的是javaEE提供的一个接口;广义上讲,所有实现了Servlet接口的java类,都可以被称为Servlet.在这里插入图片描述

2. 继承关系

|-Servlet ->顶级接口
|- GenericServlet–>直接实现Servlet接口的抽象类
|-HttpServlet–>继承GenericServlet的抽象类.

3. 定义并使用Servlet

3.1 继承GenericServlet,重写service方法
public class HelloServlet extends GenericServlet {
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("HelloServlet执行了...");
    }
}
3.2 提供配置,指明Servlet和路径之间的映射关系.
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <!--配置Servlet-->
    <servlet>
        <servlet-name>HelloServlet</servlet-name>
        <servlet-class>com.bupt.HelloServlet</servlet-class>
    </servlet>

    <!--配置Servlet和路径之间的映射关系-->
    <servlet-mapping>
        <servlet-name>HelloServlet</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>
</web-app>

4. Servlet的访问流程在这里插入图片描述

  • 请求到来时,tomcat会先进行URL解析,得到访问资源(/hello),去项目的web.xml中进行配置,如果配置成功,则会定位到一个Servlet的名字(HelloServlet),通过名称,找到该Servlet的全限定路径,反射创建对象,接着调用service方法,代码变执行了.
  • 如果在本项目的web.xml中没有匹配到访问路径,此时,该路径还回去Tomcat的conf目录下的web.xml中继续匹配,在Tomcat中,默认提供了两个Servlet,分别是Default和JspServlet,配置信息如下:
<servlet>
    <servlet-name>default</servlet-name>
    <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
    <init-param>
        <param-name>debug</param-name>
        <param-value>0</param-value>
    </init-param>
    <init-param>
        <param-name>listings</param-name>
        <param-value>false</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>
<servlet>
    <servlet-name>jsp</servlet-name>
    <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
    <init-param>
        <param-name>fork</param-name>
        <param-value>false</param-value>
    </init-param>
    <init-param>
        <param-name>xpoweredBy</param-name>
        <param-value>false</param-value>
    </init-param>
    <load-on-startup>3</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>jsp</servlet-name>
    <url-pattern>*.jsp</url-pattern>
    <url-pattern>*.jspx</url-pattern>
</servlet-mapping>

5.url-pattern的配置形式

  • /路径,精准匹配
  • *.xxx,模糊匹配,固定目录匹配
  • /目录/*,模糊匹配,固定目录匹配
  • /,匹配除了.jsp/.jspx以外的其他路径
  • /*,匹配所有的访问路径,包括jsp
<servlet-mapping>
    <servlet-name>HelloServlet</servlet-name>
    <url-pattern>/hello</url-pattern><!--精确匹配/hello-->
    <url-pattern>*.css</url-pattern><!--匹配以.css结尾的所有路径-->
    <url-pattern>/bupt/*</url-pattern><!--匹配/bupt/下的所有路径-->
    <url-pattern>/</url-pattern><!--除了.jsp的路径-->
    <url-pattern>/*</url-pattern><!--所有路径-->
</servlet-mapping>

6.Servlet的生命周期

生命周期指的是一个对象从创建到消亡的过程,就Servlet而言,生命周期可以被分为四个阶段,分别是:

  • 实例化->创建对象
  • 初始化->调用init方法
  • 服务->调用service方法
  • 销毁->调用destroy方法
public class DemoServlet extends HttpServlet {
    public DemoServlet() {
        System.out.println("[1] 实例化");
    }

    @Override
    public void init() throws ServletException {
        System.out.println("[2] 初始化");
    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("[3] 提供服务...");
    }

    @Override
    public void destroy() {
        System.out.println("[4] 销毁");
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <servlet>
        <servlet-name>Demo</servlet-name>
        <servlet-class>com.bupt.DemoServlet</servlet-class>
        <load-on-startup>10</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>Demo</servlet-name>
        <url-pattern>/demo</url-pattern>
    </servlet-mapping>
</web-app>

默认情况下,Servlet就是一种懒汉式单例,第一次使用时进行实例化和初始化,可以在web.xml配置文件中的< servlet>标签中加入< load-on-startup>修改为饿汉式单例.<load-on-startup.要配置一个正整数,数字越小越先被加载.
其中Servlet默认的default的级别为1,jsp为3

7.登录功能的实现

HTML+Servlet+MyBatis+MySQL:
获取请求实体:

// 获取前台传递的用户名和密码
String username = req.getParameter("username");
String password = req.getParameter("password");

发送响应信息(输出流)

// 向页面输出信息, 响应
resp.getWriter().print("<font color='red' size='5'>failed!</font>");
resp.getWriter().print("<br />");
resp.getWriter().print("please <a href='index.html'>try again</a>!");

8.Servlet对象

8.1请求对象

是service方法的第一个参数,HttpServletRequest,是一个接口,继承了父接口ServletRequest,该对象由Servlet容器创建(Tomcat),然后传递给service方法使用,用于为Servlet提供客户端发送请求的信息.

8.1.1获取请求行信息
/**
 * 获取请求行信息
 *
 * @param req
 */
private void getReqLine(HttpServletRequest req) {
    System.out.println("协议版本号: " + req.getProtocol());
    System.out.println("请求方式: " + req.getMethod());
    System.out.println("url: " + req.getRequestURL());
    System.out.println("uri: " + req.getRequestURI());
    System.out.println("path: " + req.getServletPath());
    System.out.println("协议名: " + req.getScheme());
}
8.1.2获取请求头信息
/**
 * 获取请求头信息
 * @param req
 */
private void getReqHeader(HttpServletRequest req) {
    String userAgent = req.getHeader("User-Agent");
    System.out.println("userAgent: " + userAgent);
    Enumeration<String> names = req.getHeaderNames();
    while(names.hasMoreElements()) {
        String name = names.nextElement();
        System.out.println(name + ": " + req.getHeader(name));
    }
}
8.1.3获取请求实体
/**
 * 获取请求实体
 *
 * @param req
 */
private void getReqEntity(HttpServletRequest req) {
    String username = req.getParameter("username");
    String[] hobs = req.getParameterValues("hob");
    System.out.println("username = " + username);
    System.out.println("hobs = " + Arrays.toString(hobs));
}
8.1.4获取其他信息
/**
 * 获取请求其他信息
 *
 * @param req
 */
private void getReqOther(HttpServletRequest req) {
    System.out.println("服务器IP: " + req.getLocalAddr());
    System.out.println("服务器端口号: " + req.getLocalPort());
    System.out.println("客户端IP: " + req.getRemoteAddr());
    System.out.println("客户端端口号: " + req.getRemotePort());
    System.out.println("上下文(项目)路径: " + req.getContextPath());
    System.out.println("真实路径: " + req.getRealPath("/upload"));
}
8.2响应对象

响应对象是service方法的第二个参数, HttpServletResponse, 是一个接口, 继承了父接口ServletResponse. 该对象由Servlet容器创建, 传递service方法使用, 用来帮助程序员对客户端的请求做出响应. 主要针对响应头和响应实体进行操作.

public class RespServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 设置响应头信息
        // Content-Type设置响应内容的格式
        // resp.setHeader("Content-Type", "text/html;charset=UTF-8");
        resp.setContentType("text/html;charset=UTF-8");
        resp.setHeader("demo", "mouse"); // 同名会覆盖
        resp.addHeader("demo", "keyboard"); // 同名不会覆盖
        resp.addHeader("demo", "screen");
        // 设置响应实体
        resp.getWriter().print("<h2>哈哈哈!</h2>");
    }
}

9.乱码问题和页面跳转

9.1乱码问题
9.1.1乱码及常见编码方式

乱码问题的根本原因是编码格式不统一造成的. 常见的编码方式:

  • ASCII, 美国, 最基本的编码格式, 其他编码方式都包含ASCII;
  • ISO-8859-1, 西欧编码方式, 不支持中文;
  • GBK/GB2312/GB18030, 汉字编码方式, 不支持其他国家的语言;
  • UTF-8, 万国码, 基本上支持所有国家的语言.
9.1.2前端页面乱码
<!--告知浏览器使用哪种编码方式解析内容-->
<meta charset="UTF-8"> HTML4
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">HTML5
9.1.3post请求乱码

接收参数前设置编码格式为UTF-8

// 解决post请求乱码问题, 设置接收参数时使用的编码方式为UTF-8
req.setCharacterEncoding("UTF-8");
String username = req.getParameter("username");
9.1.4get请求乱码

需要手动转换和tomcat的版本有关(7及以前的版本)

String username = req.getParameter("username");
// 解决get乱码, 需要先使用ISO-8859-1解码为字节数组, 然后再重新使用UTF-8编码为字符串
byte[] bytes = username.getBytes("ISO-8859-1");
username = new String(bytes, "UTF-8");
9.1.5 响应乱码
// 设置响应头, 解决响应乱码
resp.setContentType("text/html;charset=UTF-8");
// 响应到客户端浏览器
resp.getWriter().print(username);
9.2页面跳转

页面跳转指的是从一个位置跳转到另一个位置, 常用于Servlet之间的调用, 或者页面的显示. 在Servlet中, 有两种跳转方式: 请求转换和响应重定向.

9.2.1 请求转发

不管跳转几个位置, 客户端只发送一次请求, 所有的跳转都是借助转发器实现的.

public class Demo1Servlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("第一个Servlet被调用...");
        System.out.println("demo1: " + req.getParameter("name"));
        // 请求转发的实现, 通过请求对象实现的
        req.getRequestDispatcher("demo2").forward(req, resp);
    }
}
9.2.2 响应重定向

重定向时多次请求, 都是由客户端发起的.

public class Test1Servlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("[重定向] 第一个Servlet被调用...");
        System.out.println("test1: " + req.getParameter("name"));
        // 重定向到第二个servlet
        resp.sendRedirect("test2?name=" + req.getParameter("name"));
    }
}
9.2.3请求转发和响应重定向的区别

在这里插入图片描述

10.ServletConfig和ServletContext

10.1 ServletConfig

ServletConfig, 是一个接口, 由Servlet容器创建该对象, 在Servlet初始化阶段, 用于为Servlet提供配置信息. 配置信息可以在web.xml中提供.在这里插入图片描述

10.1.1提供配置信息

web.xml中配置Servlet的配置信息, 实现软编码.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <servlet>
        <servlet-name>demo</servlet-name>
        <servlet-class>com.bjsxt.servlet.DemoServlet</servlet-class>
        <!--初始化参数-->
        <init-param>
            <param-name>name</param-name>
            <param-value>zhangsan</param-value>
        </init-param>
        <init-param>
            <param-name>gender</param-name>
            <param-value></param-value>
        </init-param>
    </servlet>

    <servlet-mapping>
        <servlet-name>demo</servlet-name>
        <url-pattern>/demo</url-pattern>
    </servlet-mapping>
</web-app>
10.1.2获取配置信息
public class DemoServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 获取ServletConfig对象
        ServletConfig config = getServletConfig();
        // 获取初始化参数
        String name = config.getInitParameter("name");
        String gender = config.getInitParameter("gender");
        System.out.println("name = " + name);
        System.out.println("gender = " + gender);
    }
}
10.2 ServletContext

ServletContext, 是一个接口, 表示整个web应用. 一个应用程序有且仅有一个ServletContext对象. 通常来讲, ServletContext也被称之为Application. 使用ServletContext可以实现以下操作: 读取全局初始化参数, 可以加载web项目的静态资源, 可以获取资源的真实路径等操作.

2.1 如何获取ServletContext对象
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    // 获取ServletContext对象
    ServletContext c1 = getServletContext();
    // 先获取ServletConfig, 然后获取ServletContext
    ServletContext c2 = getServletConfig().getServletContext();
    // 可以通过请求对象获取ServletContext
    ServletContext c3 = req.getServletContext();
    System.out.println(c1 == c2);
    System.out.println(c2.hashCode());
    System.out.println(c3.hashCode());
}
10.2.2 读取全局配置参数

全局配置参数是独立于Servlet的, 所有的Servlet都可以使用全局初始化参数.

<!--全局初始化参数-->
<context-param>
    <param-name>name</param-name>
    <param-value>张无忌</param-value>
</context-param>
// 读取全局初始化参数
String name = c2.getInitParameter("name");
System.out.println("name = " + name);
10.2.3 加载静态资源
// 加载静态资源, 指的是web目录下的内容, getResourceAsStream
InputStream is = c1.getResourceAsStream("WEB-INF/web.xml");
BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
String line;
while((line = br.readLine()) != null) {
    System.out.println(line);
}
br.close();
10.2.4获取真实路径
// 获取目录的真实路径, 不存在则创建
String upload = c3.getRealPath("/upload");
System.out.println("upload = " + upload);
File file = new File(upload);
if(!file.exists()) {
    file.mkdirs();
}

11.路径问题

路径问题解决定位问题, 相对路径(从当前位置出发进行定位), 绝对路径(从指定位置出发进行定位), 例如:

  • a/aa/aa.html 相对路径
  • E:/demo/a/aa/aa.html 绝对路径
    web项目中, 还有两位位置比较重要:
  • 服务器根路径, 指的是部署项目的位置, 访问时: http://localhost:8080/
  • 项目根路径, 指的是项目所在目录, 访问时: http://localhost:8080/demo/
  • 当context路径为/时, 服务器根路径和项目根路径重合.
11.1.前台页面路径问题

前台页面中, /表示服务器根路径, 定位时建议使用绝对路径.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>AAAA</h1>
<hr>
<a href="">当前位置</a>
<a href="/">服务器根路径</a>
<hr>
<ul>
    <li>相对路径
        <ul>
            <li><a href="../../../../b/bb/bbb/bbb.html">bbb.html</a></li>
        </ul>
    </li>
    <li>绝对路径
        <ul>
            <li><a href="/b/bb/bbb/bbb.html">bbb.html</a></li>
        </ul>
    </li>
</ul>
</body>
</html>
10.2后台Servlet相关的路径问题

后台Servlet路径问题应该区别对待, 建议使用绝对路径定位.

  • 请求转发时: /表示项目根路径
  • 响应重定向时: /表示服务器根路径
public class DemoServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("DemoServlet.service");
        // 跳转到bbb.html
        // 相对路径, 基于web.xml中配置的url-pattern
        req.getRequestDispatcher("../b/bb/bbb/bbb.html").forward(req, resp);
        // 绝对路径, 通过/定位, /表示项目根路径
        req.getRequestDispatcher("/b/bb/bbb/bbb.html").forward(req, resp);
    }
}
public class Demo2Servlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("Demo2Servlet.service");
        // 重定向到bbb.html
        // 相对路径
        resp.sendRedirect("../b/bb/bbb/bbb.html");
        // 绝对路径, /定位, /表示的时服务器根路径
        resp.sendRedirect(req.getContextPath() + "/b/bb/bbb/bbb.html");
    }
}

12.常见错误:

12.1

java.lang.NoClassDefFoundError: Could not initialize class

在这里插入图片描述
12.2 eclipse将项目部署到Tomcat下出现HTTP Status 404
解决方法
12.3 servlet映射–eclipse中自己手动创建的xml文件为什么不生效
解决方法
12.4 500之后404
解决方法

发布了219 篇原创文章 · 获赞 352 · 访问量 21万+

猜你喜欢

转载自blog.csdn.net/qq_42859864/article/details/103945873