2、Servlet
Servlet
Servlet(Server Applet)是Java Servlet的简称,称为小服务程序或服务连接器,用Java编写的服务器端程序,具有独立于平台和协议的特性,主要功能在于交互式地浏览和生成数据,生成动态Web内容。
- 实现Servlet有三种方式:
- 实现javax.servlet.Servlet接口;
- 继承javax.servlet.GenericServlet类;
- 继承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呢?
-
通过注解的方式:@WebServlet("/访问路径")
-
通过配置文件web.xml的方式
-
浏览器要想访问Servlet类,需要在web.xml文件里面添加两个标签:
找到对象Servlet的标签 文件映射的标签
<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();
servlet的出生----------会触发 void init(ServletConfig)
- 服务器会在Servlet第一次被访问时创建Servlet,或者是在服务器启动时就创建Servlet。如果服务器时就创建Servlet,那么需要在web.xml中配置。也就是说默认情况下,servlet是在第一次访问时由服务器创建的。而且一个Servlet类型,服务器只创建一个实例对象。
在Servlet被创建后,服务器会马上调用Servlet的void init(ServletConfig)方法。请记住, Servlet出生后马上就会调用init()方法,而且一个Servlet的**一生这个方法只会被调用一次。
Servlet服务---------------void service(ServletRequest,ServletResponse)
- 当服务器每次接收请求时,都会去调用Servlet的service()方法来处理请求。服务器接收到一次请求就会调用一次service()方法。所以service()方法会被调用多次。所以我们把处理请求的代码放到service()方法中!
servlet的离去---------void destroy()
- Servlet是不会轻易的离去的,通常都是在服务器关闭时,Servlet 才会离去!在服务器关闭的时候会销毁Servlet,在销毁之前servlet调用destroy(),我们可以将释放资源等代码放在destroy()方法中。一个生命周期只会调用一次destro()方法。
- 【注意】:这个方法并不是用于销毁Servlet的,而是当Tomcat关闭时,会触发这个Servlet的这个方法。
Servlet不是线程安全的。若多个客户端同时访问 这个Servlet,即 多条线程同时访问这个Servlet,会造成线程安全问题。现在大家知道这个问题就行
单例,一个类只有一个对象;当然可能存在多个Servlet类!
线程不安全的,所以它的效率是高的!
Servlet接口相关的类型
三个不熟悉的类型:
- ServletRequest:service()方法的参数 ,表示请求对象,他封装了所以与请求相关的数据,由服务器创建的;
- ServletResponse: service()方法中的参数,表示响应对象,在service()方法中完成对客户端的响应需要使用这个对象;
- ServletConfig:init()方法参数,表示Servlet配置对象,对应得Servlcet的配置信息,在对应的web.xml文件中的
元素
ServletConfig对象
我们想要获取web.xml配置文件中的数据怎么办?
我们可以通过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:
- servletConfig-getServletContext();
- GenericServlet或者HTTPServlet对象调用getServletContext();
- httpSession---getServletContext();
- ServletContextEvent-getServletContext();
域对象
Javaweb有四大域对象:
- ServletContext
- HttpSession
- HttpRequest
- PageContext
- 大小关系: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请求有效。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!