Servlet学习

本篇文章记录学习Servlet技术的内容。

什么是Servlet

  • Servlet是JavaEE的一个接口,是JavaWeb的三大组件之一。三大组件分别是:Servlet、Filter过滤器、Listener监听器。
  • 处理请求和发送响应的过程是由Servlet程序来完成的,并且Servlet是为了解决实现动态页面而衍生的东西。
  • Servlet是运行在服务器上的一个java程序,它可以接收客户端发送过来的请求,并发送响应数据给客户端

Tomcat和Servlet的关系

  • Tomcat是Web应用服务器,是一个Servlet/JSP容器。Tomcat作为Servlet容器,负责处理客户端发送的请求,将请求传给Servlet,并将Servlet的响应传回给客户端。
  • 从http协议中的请求和响应可以知道,浏览器发出的请求是一个请求文本,浏览器接收到的也是一个响应文本。

从上图可以看出是如何请求和响应的。

①:Tomcat接收客户端的http请求文本并解析,然后封装成HttpServletRequest类型的request对象,所有的http头数据都可以通过request对象调用对应方法查到。

②:Tomcat同时会将响应的信息封装成HttpServletResponse类型的response对象,通过设置response的属性可以控制输出到浏览器中的内容,然后将response交给Tomcat,Tomcat会将其变成响应文本的格式发送给浏览器。

Java Servlet API是Servlet容器(Tomcat)和Servlet之间的接口,它定义了Servlet的各种方法,还定义了Tomcat传送给Servlet的对象类,其中最重要的是ServletRequest和ServletResponse。在编写Servlet程序时,需要实现Servlet接口,实现里面的方法。

实现Servlet

  • 编写一个类去实现Servlet接口
  • 实现service方法,处理请求,并响应数据。
  • 在web.xml中配置servlet程序的访问地址。需要让浏览器知道发出的请求到达哪个servlet,也就是让tomcat将封装好的request找到对应的servlet让其使用。

实现的servlet类

1
2
3
4
5
6
7
public class HelloServlet implements Servlet{
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("HelloServlet被访问了");
}
//其它实现的方法省略
}

web.xml中需要配置以下四个信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<servlet>
<!--servlet-name给程序起一个别名-->
<servlet-name>HelloServlet</servlet-name>
<!--servlet的全类名-->
<servlet-class>servlet.HelloServlet</servlet-class>
</servlet>
<!--servlet-mapping标签给servlet程序配置访问地址-->
<servlet-mapping>
<!--告诉服务器,当前配置的地址给那个servlet程序使用,和上面的别名一致-->
<servlet-name>HelloServlet</servlet-name>
<!--配置访问地址
/ 斜杠表示地址为:http://ip:port/工程路径
/hello 表示地址为:http://ip:port/工程路径/hello
这个地址就表示访问到HelloServlet这个类了-->
<url-pattern>/hello</url-pattern>
</servlet-mapping>

url地址到Servlet程序的访问

Servlet的生命周期

  1. 执行Servlet构造器方法
  2. 执行init初始化方法

前两步是在第一次访问时创建Servlet程序时调用。

3.执行service方法(这一步每次访问都会调用)。

4.执行destroy销毁方法。(在web工程停止时调用)。

load-on-startup作用

  • load-on-startup 元素标记容器是否应该在web应用程序启动的时候就加载这个servlet,(实例化并调用其init()方法)。
  • 它的值必须是一个整数,表示servlet被加载的先后顺序。
  • 如果该元素的值为负数或者没有设置,则容器会当Servlet被请求时再加载
  • 如果值为正整数或者0时,表示容器在应用启动时就加载并初始化这个servlet,值越小,servlet的优先级越高,就越先被加载。值相同时,容器就会自己选择顺序来加载。
1
2
写在<servlet></servlet>
<load-on-startup>1</load-on-startup>

通过继承HttpServlet实现Servlet程序

一般不会直接实现Servlet类实现Servlet程序,而是去继承HttpServlet类。在HttpServlet继承类中,只需要根据自己的需要去重写doGet和doPost方法既可。

查看HttpServlet类的继承结构,发现它继承于GenericServlet类。而GenericServlet类实现了Servlet类,对很多方法做了空实现。并且持有一个ServletConfig类的引用,对ServletConfig的使用定义了一些方法。

Servlet接口内容

可以看到,接口内有Servlet生命周期的三个关键方法,init、service、destroy,还有一个重要的getServletConfig方法来获取ServletConfig对象,ServletConfig对象可以获取到Servlet的信息,ServletName、ServletContext、InitParameter、InitParameterNames,通过查看ServletConfig这个接口就可以知道。

ServletConfig接口又可以获取ServletContext对象,具体内容下面讲解。

GenericServlet类内容

这个类实现了Servlet和ServletConfig的9个方法。其中init方法有两个,一个是带参数ServletConfig的,一个是无参的方法。

1
2
3
4
5
6
7
8
9
10
private transient ServletConfig config;
public ServletConfig getServletConfig() {
return this.config;
}
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
public void init() throws ServletException {
}

通过这几个方法一起看,首先看init(ServletConfig config)方法,因为只有init(ServletConfig config)中带有ServletConfig对象,为了方便能够在其他地方也能直接使用ServletConfig对象,而不仅仅局限在init(ServletConfig config)方法中,所以创建一个私有的成员变量config,在init(ServletConfig config)方法中就将其赋值给config,然后通过getServletConfig()方法就能够获取ServletConfig对象了。而当想在init方法中做一些别的事情,就需要重写init(ServletConfig config)方法,这样的话在重写的方法中必须调用父类的init(ServletConfig config)方法,即super.init(ServletConfig config),否则GenericServlet类中的成员变量config会一直是null值,无法再得到ServletConfig对象。增加一个init()方法,那么需要在init内初始化别的数据时,就只需要重写init()方法,不需要覆盖init(ServletConfig config)了,仍然可以得到config对象。

1
public abstract void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;

这是GenericServlet类中的service方法,是一个抽象方法,那么也就是还有一个子类继承它,即HttpServlet类。这个类实现了service方法的各种细节,通过类名也可知道和http协议有关系了。

HttpServlet类内容

包含各种常量,比如GET和POST请求,还有各种请求的处理方法。

service(ServletRequest req, ServletResponse res)方法

1
2
3
4
5
6
7
8
9
10
11
12
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
HttpServletRequest request;
HttpServletResponse response;
try {
request = (HttpServletRequest)req;
response = (HttpServletResponse)res;
} catch (ClassCastException var6) {
throw new ServletException("non-HTTP request or response");
}

this.service(request, response);
}

该方法只是把ServletRequest和ServletResponse这两个对象强转为HttpServletRequest和HttpServletResponse对象。之后,在调用service(HttpServletRequest req, HttpServletResponse resp)方法,这个方法就是判断浏览器过来的请求方式是哪种,每种的处理方式不一样,我们常用的就是get,post,并且,我们处理的方式可能有很多的内容,所以,在该方法内会将get,post等其他5种请求方式提取出来,变成单个的方法,然后我们需要编写servlet时,就可以直接重写doGet或者doPost方法就行了,而不是重写service方法,更加有针对性。

所以,以后编写servlet程序时,只需要继承于HttpServlet类,只要重写两个方法,doGet()和doPost()。

ServletConfig类

是servlet程序的配置信息类,由Tomcat负责创建,为每一个Servlet程序都创建一个ServletConfig对象。

三大作用

  1. 可以获取Servlet程序的别名servlet-name的值。(getServletName())
  2. 获取初始化参数init-param(只是该Servlet下的初始化参数)。(getInitParameter())
  3. 获取ServletContext对象。(getServletContext())
1
2
3
4
5
6
7
8
9
10
11
12
13
<servlet>
<!--servlet-name给程序起一个别名-->
<servlet-name>HelloServlet</servlet-name>
<!--servlet的全类名-->
<servlet-class>servlet.HelloServlet</servlet-class>
<!--配置参数-->
<init-param>
<!--参数名-->
<param-name>username</param-name>
<!--参数值-->
<param-value>root</param-value>
</init-param>
</servlet>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletConfig servletConfig = getServletConfig();
System.out.println("servlet2的get请求");
//获取Servlet程序别名
System.out.println(servletConfig.getServletName());
//获取初始化参数init-param
System.out.println(servletConfig.getInitParameter("username"));
System.out.println(servletConfig.getInitParameter("url"));
//获取ServletContext对象
System.out.println(servletConfig.getServletContext());
Enumeration<String> initParameterNames = servletConfig.getInitParameterNames();
while(initParameterNames.hasMoreElements()){
String name = initParameterNames.nextElement();
System.out.println(name+":"+servletConfig.getInitParameter(name));
}
}

ServletContext类

  • ServletContext 是一个接口,它表示 Servlet 上下文对象
  • 一个 web 工程,只有一个 ServletContext 对象实例。
  • ServletContext 对象是一个域对象。
  • ServletContext 是在 web 工程部署启动的时候创建。在 web 工程停止的时候销毁。

什么是域对象?

域对象,是可以像 Map 一样存取数据的对象,叫域对象。

这里的域指的是存取数据的操作范围,整个 web 工程。

四大作用

  1. 获取 web.xml 中配置的上下文参数 context-param.全局初始化参数,每个Servlet都可以得到该值(getServletContext())
  2. 获取当前的工程路径,格式: /工程路径。(getContextPath())
  3. 获取工程部署后在服务器硬盘上的绝对路径。(getRealPath())
  4. 存取数据。(setAttribute(),getAttribute())
1
2
3
4
5
6
7
8
9
10
11
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletConfig servletConfig = getServletConfig();
ServletContext servletContext = servletConfig.getServletContext();
//获取web.xml配置的上下文参数context-param
String username = servletContext.getInitParameter("username");
System.out.println("context参数username:"+username);
//获取当前工程路径
System.out.println("当前工程路径为"+servletContext.getContextPath());
//获取工程部署后在硬盘上的绝对路径
System.out.println("绝对路径为"+servletContext.getRealPath("/"));
}

HttpServletRequest类

每次只要有请求进入 Tomcat 服务器,Tomcat 服务器就会把请求过来的 HTTP 协议信息解析好封装到 Request 对象中。 然后传递到 service 方法(doGet 和 doPost)中给我们使用。我们可以通过 HttpServletRequest 对象,获取到所有请求的 信息。

常用api

1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取请求的资源路径
System.out.println("URI-->"+req.getRequestURI());
//获取请求的绝对路径
System.out.println("URL-->"+req.getRequestURL());
//获取客户端ip地址
System.out.println("ip-->"+req.getRemoteAddr()+" port-->"+req.getServerPort());
//获取请求头
System.out.println("header-->"+req.getHeader("User-Agent"));
//获取请求方式
System.out.println("method-->"+req.getMethod());
}

获取请求参数

1
2
3
4
5
6
7
8
9
10
<body>
<form action="http://localhost:8080/2_Servlet/param" method="post">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
兴趣爱好:<input type="checkbox" name="hobby" value="java">Java
<input type="checkbox" name="hobby" value="C++">C++
<input type="checkbox" name="hobby" value="C#">C#<br>
<input type="submit" value="提交吧">
</form>
</body>
1
2
3
4
5
6
7
8
9
10
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("******GET******");
//获取请求参数
System.out.println(request.getParameter("username"));
System.out.println(request.getParameter("password"));
//只能获取到第一个
System.out.println(request.getParameter("hobby"));
//获取多个值
System.out.println(Arrays.asList(request.getParameterValues("hobby")));
}

post请求中同样,但是会出现中文乱码,需要设置请求体的字符集为utf8.

1
request.setCharacterEncoding("utf-8");

请求转发

请求转发是指,服务器收到请求后,从一个资源跳转到另一个资源的操作叫请求转发。

  1. 浏览器地址栏没有发生变化
  2. 是同一次请求
  3. 共享Request域中的数据
  4. 可以转发到WEB-INF下
  5. 不可以访问工程以外的资源
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//servlet1中的get方法
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
System.out.println("发送的"+username);
request.setAttribute("password","123456");
//务必以"/"开头,表示地址为hhtp://ip:port/工程名/
RequestDispatcher requestDispatcher = request.getRequestDispatcher("/servlet2");
requestDispatcher.forward(request,response);
}
//servlet2中的get方法
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
System.out.println("转发收到的"+username);
Object password = request.getAttribute("password");
System.out.println(password);
}
//浏览器地址栏
http://localhost:8080/2_Servlet/servlet1?username=root

base标签

可以设置当前页面的相对路径工作时,参照哪个相对路径进行跳转。

1
<base href="http://localhost:8080/07_servlet/a/b/">

Web中”/“的意义

HttpServletResponse类

HttpServletResponse 类和 HttpServletRequest 类一样。每次请求进来,Tomcat 服务器都会创建一个 Response 对象传 递给 Servlet 程序去使用。HttpServletRequest 表示请求过来的信息,HttpServletResponse 表示所有响应的信息, 我们如果需要设置返回给客户端的信息,都可以通过 HttpServletResponse 对象来进行设置。

两个输出流

字节流:getOutputStream()

字符流:getWriter()

两个流同时只能使用一个

向客户端回传数据

1
2
3
4
5
6
7
8
9
10
11
12
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println(resp.getCharacterEncoding());//默认ISO-8859-1
// //设置服务器字符集
// resp.setCharacterEncoding("UTF-8");
// //通过响应头,设置浏览器也使用utf8
// resp.setHeader("Content-Type","text/html;charset-UTF-8");
//也可以直接设置Content-Type,但要在获取流之前设置
resp.setContentType("text/html;charset-UTF-8");//推荐使用, 它会同时设置服务器和客户端都使用 UTF-8 字符集,还设置了响应头
PrintWriter writer = resp.getWriter();
writer.write("响应内容");
}

请求重定向

是指客户端给服务器发请求,然后服务器告诉客户端说,我给你一些地址。你去新地址访问。叫请求 重定向(因为之前的地址可能已经被废弃)。

浏览器地址栏会发生变化

两次请求

不能共享request域中的数据

不能访问到WEB-INF下的资源

可以访问工程外的资源,比如百度

1
2
3
4
5
6
7
8
9
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("经过了response1");
// //设置响应码302,表示重定向
// response.setStatus(302);
// //设置响应头,说明新的地址在哪里
// response.setHeader("Location","http://localhost:8080/2_Servlet/response2");
response.sendRedirect("/2_Servlet/response2");//推荐使用
//也可以重定向到其他资源,比如百度
}
1
2
3
4
5
6
7
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("response2's results");
response.getWriter().write("response2's results");
}
//结果
经过了response1
response2's results
Author

叶润繁

Posted on

2022-01-08

Licensed under