2、Servlet

Servlet

Servlet(Server Applet)是Java Servlet的简称,称为小服务程序或服务连接器,用Java编写的服务器端程序,具有独立于平台和协议的特性,主要功能在于交互式地浏览和生成数据,生成动态Web内容。

  1. 实现Servlet有三种方式:
    1. 实现javax.servlet.Servlet接口;
    2. 继承javax.servlet.GenericServlet类;
    3. 继承javax.servlet.http.HttpServlet类;
  • 实现Servlet接口

    • 要成为一个servlet,然后交给servlet容器去管理和调用,就必须有某种约定。tomcat和开发者之间要有一个约定:

      tomcat应该确切的知道开发者写的servlet程序一定具有什么方法,这样tomcat才知道调用哪些方法

      开发者也需要知道,tomcat到底会调用我们写的servlet的哪些方法

      java采用接口的方式进行约定,所以定义了一个接口和一系例方法,tomcat和开发者之间都必须遵守这个接口。

  • 继承GenericServlet抽象父类

    • 因为直接Servlet的方式,需要开发者实现这个接口的每个方法,但其实有一些方法,开发都并不一定用得到。这时为何不提供一个类去将这些比较少用的方法先实现一次,然后留下开发者一定要实现的service(),保证service()一定会被开发者重写。

      servlet规范的定义者已经考虑到这个问题,所以提供一个通用的抽像父类GenericServlet,该父类除了service()没有实现外,其它方法都帮你实现,留下service()为abstract,使得开发者必定要实现这个抽象方法

  • 继承HttpServlet抽象父类

    • 如果处理的是http的请求,继承HttpServlet应该是你首选的方式,以后开发也是90%以上机会你是继承它。

      1 这个类提供很关于处理http请求的方法给你使用

      2 这个类提供了不同的方法来处理get和post的请求

      如果是继承HttpServlet,那么没必要去重写serice(),HttpServlet已经帮我们重写了该方法,内部大致的实现思路是:

      serive(){
       String method = req.getMethod();
       if("GET".equalsIgnoreCase(method)){
          doGet();
       }else if("Post".equalsIgnoreCase(method)){
          doPost();
       }
      }

      对于开发者来说,只需要重写doGet和doPost就可以了。

三者的关系:

​ 这三者的关系是Servlet是接口,GenericServlet是接口的实现类,HTTP Servlet继承于Generic Servlet。

【备注】

Servlet中的方法大多数不由我们来调用,而是由Tomcat来调用。并且Servlet的对象也不由我们来创建,由Tomcat来创建!我们程序员需要做的 还是按照自己的业务需求来编写Servlet。

如何来访问一个servlet呢?

若是一个网页html,咱们就可以直接通过这个html的网址来访问。但是 这个Servlet是一个类,如何通过浏览器来访问呢?如何才能通过浏览器访问一个类Servlet呢?

  1. 通过注解的方式:@WebServlet("/访问路径")

  2. 通过配置文件web.xml的方式

    1. 浏览器要想访问Servlet类,需要在web.xml文件里面添加两个标签:

      1. 找到对象Servlet的标签
      2. 文件映射的标签
      <web>
      <servlet>
      <servlet-name>fisrt</servlet-name>(唯一标识的名称)
      (对应得servlet的类)
      <servlet-mapping>com.servlet.FirstServlet</servlet-mapping>
      </servlet>
      <serlvet-mapping>
      <servlet-name>fisrt</servlet-name>(跟servlet标签的name保持一致)
      <url-pattern>/fisrtServlet</url-pattern>(浏览器访问的路径,需要加斜杆)
      </serlvet-mapping>
      </web>

Servlet的生命周期

所谓的生命周期就是从出生,服务到死亡的过程。Servlet也是如此!

与生命周期相关的方法有:

​ void init(ServletConfig);

​ void service(ServletRequest,ServletResponse);

​ void destroy();

  1. servlet的出生----------会触发 void init(ServletConfig)

    1. 服务器会在Servlet第一次被访问时创建Servlet,或者是在服务器启动时就创建Servlet。如果服务器时就创建Servlet,那么需要在web.xml中配置。也就是说默认情况下,servlet是在第一次访问时由服务器创建的。而且一个Servlet类型,服务器只创建一个实例对象

    在Servlet被创建后,服务器会马上调用Servlet的void init(ServletConfig)方法。请记住, Servlet出生后马上就会调用init()方法,而且一个Servlet的**一生这个方法只会被调用一次。

  2. Servlet服务---------------void service(ServletRequest,ServletResponse)

    1. 当服务器每次接收请求时,都会去调用Servlet的service()方法来处理请求。服务器接收到一次请求就会调用一次service()方法。所以service()方法会被调用多次。所以我们把处理请求的代码放到service()方法中!
  3. servlet的离去---------void destroy()

    1. Servlet是不会轻易的离去的,通常都是在服务器关闭时,Servlet 才会离去!在服务器关闭的时候会销毁Servlet,在销毁之前servlet调用destroy(),我们可以将释放资源等代码放在destroy()方法中。一个生命周期只会调用一次destro()方法。
    2. 【注意】这个方法并不是用于销毁Servlet的,而是当Tomcat关闭时,会触发这个Servlet的这个方法。
  4. Servlet不是线程安全的。若多个客户端同时访问 这个Servlet,即 多条线程同时访问这个Servlet,会造成线程安全问题。现在大家知道这个问题就行

  5. 单例,一个类只有一个对象;当然可能存在多个Servlet类!

线程不安全的,所以它的效率是高的!

Servlet接口相关的类型

三个不熟悉的类型:

  1. ServletRequest:service()方法的参数 ,表示请求对象,他封装了所以与请求相关的数据,由服务器创建的;
  2. ServletResponse: service()方法中的参数,表示响应对象,在service()方法中完成对客户端的响应需要使用这个对象;
  3. ServletConfig:init()方法参数,表示Servlet配置对象,对应得Servlcet的配置信息,在对应的web.xml文件中的元素

ServletConfig对象

我们想要获取web.xml配置文件中的数据怎么办?

我们可以通过ServletConfig对应的元素,一个ServletConfig对应一个元素。

可以通过servletConfig.getServletName()方法来获取!

ServletConfig对象是由服务器创建的,然后传递给Servlet的init()方法,你可以在init()方法中使用它!

String getServletName():获取Servlet在web.xml文件中的配置名称,即指定的名称;(用到不多)

ServletContext getServletContext():用来获取ServletContext对象,ServletContext会在后面讲解;

String getInitParameter(String name):用来获取在web.xml中配置的初始化参数,通过参数名来获取参数值;

Enumeration getInitParameterNames():用来获取在web.xml中配置的所有初始化参数名称;(返回值是一个迭代器(集合),很少使用了)

还可以在元素中配置参数:

<servlet>
  <servlet-name>One</servlet-name>
  <servlet-class>com.xyd.servlet.OneServlet</servlet-class>
  <init-param>
    <param-name>username</param-name>
  <param-value>scott</param-value>
  </init-param>
  <init-param>
  <param-name>password</param-name>
  <param-value>tiger</param-value>
  </init-param> ------ 配置了两个初始化参数
</servlet>

获取参数:

// 获取 初始化的参数
String username = servletConfig.getInitParameter("username");
String password = servletConfig.getInitParameter("password");
System.out.println("username:"+username);
System.out.println("password:"+password);
//获取初始化参数
Enumeration<String> initParameterNames = servletConfig.getInitParameterNames();
while (initParameterNames.hasMoreElements()) {
String nextElement = initParameterNames.nextElement();
System.out.println(nextElement + " : " +     servletConfig.getInitParameter(nextElement));
}

Generic Servlet抽象父类

有两个init方法(方法重载)。

有一个获取ServletConfig 对象的方法。

服务方法: service(ServletRequestreq, ServletResponse res)。

【备注】GenericServlet还实现了ServletConfig接口,拥有与ServletConfig同名且功能相同的getInitParameter()、getServletContext()等ServletConfig的方法。

实现了ServletConfig接口

GenericServlet还实现了ServletConfig接口,所以可以直接调用getInitParameter()、getServletContext()等ServletConfig的方法。

HttpServlet抽象类

HttpServlet类是GenericServlet的子类,它提供了对HTTP请求的特殊支持能够非常方便的操作HTTP请求和返回中的一些参数,所以通常我们都会通过继承HttpServlet来完成自定义的Servlet。这些方法中,咱们需要重点关注的是 doGet和 doPost方法。还有一点需要注意,虽然这个类中 没有抽象方法,但HttpServlet是抽象类,只能被继承 使用。一般定义自己Servlet都是继承于HttpServlet,具体代码是这样的,只需重写 doGet 和 doPost 方法即可

HttpServlet重写了Servlet的生命周期方法service,这个生命周期方法最终实现是调用HttpServlet自己的service方法,而这个service具体的实现又是通过方法doGet(对应get请求) 和 doPost(对应post请求)。所以,当继承于HttpServlet来定义自己的MyServlet的时候,只需要重写doGet(对应get请求) 或者 doPost(对应post请求) 即可。

HttpServlet中的service():

具体的HttpServlet的流程

所以,为了既可以通过get 又可以通过post请求访问某个Servlet,一般同时给这个Servlet添加doGet 和 doPost 方法,

测试1:

只实现了doPost方法,即只允许客户端通过post方式访问。

在浏览器中直接访问这个Servlet,此请求方式为get请求,而服务器端的Servlet没有重写doGet,所以会报出 405

写个界面,通过post请求服务器端的这个Servlet。查看打印结果

Servlet细节

1.Servlet与线程安全(先背下来)

因为一个类型的Servlet只有一个实例对象,那么就有可能会现时出一个Servlet同时处理多个请求(即 多条线程同时访问这个Servlet),那么Servlet是否为线程安全的呢?答案是:“不是线程安全的”。这说明Servlet的工作效率很高,但也存在线程安全问题!所以我们不应该在Servlet中随便创建成员变量,因为可能会存在一个线程对这个成员变量进行写操作,另一个线程对这个成员变量进行读操作。

解决:

不要在Servlet中创建成员!创建局部变量即可!

可以创建无状态成员!(意义不大)

可以创建有状态的成员,但状态必须为只读的!(意义不大)

2.让服务器在启动时就创建Servlet

默认情况下,服务器会在某个Servlet第一次收到请求时创建它。也可以在web.xml中对Servlet进行配置,使服务器启动时就创建Servlet。

<servlet>
<servlet-name>hello1</servlet-name>
<servlet-class>com.servlet.Hello1Servlet</servlet-class>
<load-on-startup>0</load-on-startup>
------->在<servlet>中配置<load-on-startup>,其中给出一个非负整数!
       
</servlet>
<servlet-mapping>
<servlet-name>hello1</servlet-name>
<url-pattern>/hello1</url-pattern>
</servlet-mapping>
  • - 是servlet-mapping的子元素,用来指定URL。**必须以 “/” 开头**。 - 可以给出多个 - 通配符 * - 比如 /fisrt/* ,带有 项目/fisrt/xx 都能访问到Servlet - 不能夹在中间 比如:/first/*/aa,这样是错误的 - 以 .do的 - 在/.do这样写可以 - 在/*.do这样写不行

web.xml是继承了tomcat的web.xml(conf/web.xml)文件的。

以下代码是启动服务器的时候,会自动加载的内容:

<welcome-file-list>
      <welcome-file>index.html</welcome-file>
      <welcome-file>index.htm</welcome-file>
      <welcome-file>index.jsp</welcome-file>
</welcome-file-list>

ServletContext对象

服务器会对,每个应用创建一个ServletContext对象,一个项目只有一个servletContext对象。

可以在N多个servlet中获取这个唯一的对象,使得他可以在多个servlet中传递数据。

与天地同寿!!当服务器启动的时候就创建,当服务器关闭时就会死去!

获取servletContext:

  1. servletConfig-getServletContext();
  2. GenericServlet或者HTTPServlet对象调用getServletContext();
  3. httpSession---getServletContext();
  4. ServletContextEvent-getServletContext();

域对象

Javaweb有四大域对象:

  1. ServletContext
  2. HttpSession
  3. HttpRequest
  4. PageContext
  5. 大小关系:ServletContext > HttpSession > HttpRequest > PageContext

域对象必须有要存数据功能

域对象必须要有取数据功能

域对象内部其实有一个Map

方法:

  • setAttribute(String key,Object value);设置一个属性(多个同名的属性,在前面的会被覆盖)
  • getAttribute(key)获取一个属性
  • removeAttribute(key)移除一个属性
  • getAttributeNames():获取所有域属性的名称

ServletRequest对象

response和request 是服务器创建的两个对象,在服务器端的Servlet中可以通过request得到客户端请求的数据,可以通过response向客户端返回数据。

HTTP/1.1 200 OK:响应协议为HTTP1.1,状态码为200(表示请求成功),OK是对状态码的解释;

状态码(响应码)

以2开头都表示成功,

以1开头的一般表示 服务器端正在处理请求过程中...,

以3开头的都需要转移访问地址

以4开头一般是客户端错误,

以5开头一般是服务器端的错误。

Server: Apache-Coyote/1.1:服务器的版本信息;

Content-Type: text/html;charset=UTF-8:响应体的类型 和 使用的编码为UTF-8;

响应正文的MIME类型,例如image/jpeg表示响应正文为jpg图片 image/gif表示响应正文为gif图片,例如text/html;charset=utf-8表示响应正文为html,并且编码为utf-8编码(注:若返回内容为text,则需要指定编码。此处编码为urt-8,那这个页面中可以有中文;若编码为ISO-8859-1,则这个页面中不能出现中文 ,否则乱码)。浏览器会通过这一信息来显示响应数据。

Content-Length: 724:响应体为724字节;

Set-Cookie: JSESSIONID=C97E2B4C55553EAB46079A4F263435A4; Path=/hello:响应给客户端的Cookie;(Cookie是后面需要学习的一个重要的知识)

Date: Wed, 25 Sep 2016 04:15:03 GMT:响应的时间,这可能会有8小时的时区差;

request获取请求参数:

请求服务器的方式分为 GET请求和POST请求.

GET请求和POST请求的区别:

GET请求:

请求参数会在浏览器的地址栏中显示,所以不安全;

请求参数长度限制长度在1K之内;

GET请求没有请求体,无法通过request.setCharacterEncoding()来设置参数的编码;

POST请求:

请求参数不会显示浏览器的地址栏,相对安全;请求参数长度没有限制;

可以通过request.setCharacterEncoding()来设置参数的编码;

  • String getParameter(String name)指定名称获取参数

  • String[] getParameterValues(String name)指定名称返回一个数组(用在复选框,和返回多个值的)

  • Enumeration getParameterNames()获取所有请求参数名称(key),然后通过key得到对应的value。Enumeration枚举。

  • Map getParameterMap():获取所有请求的参数,封装到Map中,key作为参数,value为参数值。(value为一个数组,因为可能存在参数名相同的多组key-value)

  • //post才有的setCharacterEncoding(),解决乱码问题
    req.setCharacterEncoding("utf-8");
    //获取表单数据
    String username = req.getParameter("username");
    String password = req.getParameter("password");
    //获取一个数组
    String[] hobby = req.getParameterValues("hobby");
    String sex = req.getParameter("sex");
    String address = req.getParameter("address");
    System.out.println(username+"--"+password+"--"+sex+"--"+address);
    for (String string : hobby) {
    System.out.println(string);
    }

解决乱码问题:req.setCharacterEncoding("utf-8");只对post请求有效。

posted @   站着说话不腰疼  阅读(68)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
点击右上角即可分享
微信分享提示