Servlet #
Servlet(Server Applet)是Java Servlet的简称,称为小服务程序或服务连接器,用Java编写的服务器端程序,具有独立于平台和协议的特性,主要功能在于交互式地浏览和生成数据,生成动态Web内容,它是由Sun公司(现在是Oracle公司)开发的,作为Java Servlet API的一部分,包含在Java EE(Enterprise Edition)规范中
狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,将Servlet理解为后者。Servlet运行于支持Java的应用服务器中。从原理上讲,Servlet可以响应任何类型的请求,但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器。
servlet-api #
servlet-api.jar并不包含在我们安装的JDK(Java SE)中,它是Java EE规范的一部分,通常作为Web容器(如Tomcat、Jetty、WebLogic等)的一部分提供。
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
如果您想要使用servlet-api.jar来编写servlet应用程序,您可以通过以下途径获取:
-
下载并安装一个Java Web容器,如Apache Tomcat。然后在
Tomcat安装目录下的lib文件夹中找到servlet-api.jar。 -
下载Java EE,其中包括Servlet API的实现,并在安装目录下的lib文件夹中找到
servlet-api.jar(Oracle已经不提供Java EE的下载了)
servlet-api.jar只是在编译的时候需要,运行的时候使用Web容器内置的即可,所以不必打包进项目依赖,以免发生冲突,使用maven可以声明scope为provided
注意:由于Java EE被Orcale捐献给Apache,并且在2021年更名为Jakarta EE,所以从 Jakarta EE 9 开始对应用的Servlet类名是:jakarta.servlet.Servlet,而之前是javax.servlet.Servlet,所以如果之前的项目还在使用javax.servlet.Servlet,那么你的项目无法直接部署到Tomcat10+版本上。你只能部署到Tomcat9-版本上。在Tomcat9以及Tomcat9之前的版本中还是能够识别javax.servlet这个包。
JavaWeb三大组件 #
Servlet程序、Filter过滤器、Listener监听器
Servlet的继承关系 #
javax.servlet.Servlet(接口):是所有Servlet类的父接口,它定义了Servlet必须实现的方法,包括init()、service()和destroy()等javax.servlet.ServletConfig(接口):代表着Servlet的配置信息,例如初始化参数、Servlet名称等。在Servlet的生命周期中,容器会调用其init()方法,并传递一个ServletConfig对象作为参数。javax.servlet.GenericServlet(抽象类):实现了Servlet、ServletConfig接口。它提供了一些通用的方法,例如getServletConfig()、getInitParameter()等。通过继承GenericServlet可以简化自定义Servlet的编程,因为只需要实现其中的service()方法即可。javax.servlet.http.HttpServlet(类):是GenericServlet的子类,并实现了HttpServletRequest和HttpServletResponse接口。它针对HTTP协议提供了更加方便的处理方式,例如doGet()、doPost()等方法。通过继承HttpServlet,开发人员可以方便地处理HTTP请求和响应
Servlet的常用方法 #
void init(ServletConfig config):由Servlet容器调用,用于初始化Servlet对象void service(ServletRequest req,ServletResponse res):由Servlet容器调用,用于处理客户端请求并响应void destory():由Servlet容器调用,释放Servlet对象所使用的资源ServletConfig getServletConfig():返回ServletConfig对象String getServletInfo():返回关于Servlet的信息,比如作者、版本、版权
service()、doGet()、doPost()的区别 #
service()、doGet()、doPost()均是Java Servlet中的方法,但是它们在处理请求时有所不同。
-
service()方法是Servlet执行请求的主要方法,当客户端发出请求时,Servlet容器将调用service()方法来处理请求并生成响应。这个方法根据具体的HTTP请求类型(GET、POST、PUT、DELETE等)来决定调用哪个子方法进行处理。 -
doGet()方法处理HTTP GET请求,通常用于获取信息或数据,并且参数会显示在URL中,它不会修改服务器上的任何数据。 -
doPost()方法处理HTTP POST请求,通常用于向服务器提交数据,并且请求参数在请求体内。它可以对服务器上的数据进行更改。
总结:service()方法是Servlet处理请求的主要方法,而doGet()和doPost()方法是service()方法的具体实现,用于处理GET和POST请求。同时出现的情况下,service()优先。
使用步骤 #
1、继承HttpServlet #
三种方式 #
1、定义一个类,去实现javax.servlet.Servlet接口
2、定义一个类,去继承javax.servlet.GenericServlet抽象类
3、定义一个类,去继承javax.servlet.http.HttpServlet抽象类(推荐)
//注意:如果一个Servlet类,三个方法一个都没有重写,请求时会报405
class MyServlet extends HttpServlet {
doGet() {}
doPost() {}
service() {}
}
2、配置Servlet程序访问地址 #
在web.xml文件中 #
<servlet>
<servlet-name>名称1</servlet-name>
<servlet-class>Servlet全类名(包+类)</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>名称1</servlet-name>
<url-pattern>/虚拟路径</url-pattern>
</servlet-mapping>
使用注解配置,不需要写xml文件 #
@WebServlet:属于类级别的注解,标注在继承了 HttpServlet 的类之上。常用的写法是将 Servlet 的相对请求路径(即 value)直接写在注解内- 属性
name-String:指定Servlet的名称,如果没有指定,默认是Servlet类的全类名value-String[]:完全等价于urlPatterns属性,二者不能同时指定urlPatterns-String[]:指定一组Servlet的匹配路径loadOnStartup-int:指定Servlet的加载顺序initParams-WebInitParam:指定Servlet的启动参数asyncSupported-boolean:Servlet是否支持异步操作模式description-String:Servlet的描述displayName-String:Servlet的显示名
在继承了Servlet类上方,添加注解,用于配置此Servlet的虚拟地址,三种方法
@WebServlet(value="/servletDemo1")
public MyServle extends HttpServlet{}
@WebServlet(value={"/servletDemo1","/servletDemo2"})
public MyServle extends HttpServlet{}
@WebServlet({"/servletDemo1","/servletDemo2"})
public MyServle extends HttpServlet{}
@WebServlet(urlPatterns="/servletDemo1")
public MyServle extends HttpServlet{}
3、访问 #
http://localhost:8080/项目虚拟目录/servlet虚拟路径
域对象 #
在JavaWeb中,Servlet中三大域对象分别是request,session,ServletContext,其只要是用来存放共享数据的。
之所以他们是域对象,原因是他们都内置了Map集合,都有setAttribute、getAttribute和removeAttribute方法用于操作值。
只要是域对象,都有这3个方法,可以实现数据共享,都是以key-value方式存放数据,key必须是String类型,value是Object类型
作用域 #
| 名称 | 对象类型 | 作用域 | 说明 |
|---|---|---|---|
| application | ServletContext | 在整个应用程序中有效 | 通过request.getServletContext()方法获取,可以在整个应用范围内共享数据 |
| session | HttpSession | 在当前会话中有效 | 通过request.getSession()获取,会话代表同一浏览器向服务器的多次请求和响应 |
| request | HttpServletRequest | 在当前请求中有效 | 在一次请求的范围内,可以共享资源 |
| page | PageContext | JSP的请求到响应中有效 | 作用范围是当前用户请求的JSP页面渲染时,一旦渲染结束响应即失效 |
Servlet生命周期 #
生命周期方法 #
servlet的生命周期是由三个方法体现的,称为生命周期方法:
init():初始化方法,只有在首次访问时调用service():执行方法,每次访问,都会调用destroy():销毁方法,只有Servlet销毁时调用(WEB工程停止时)
生命周期 #
1、当我们第一次访问servlet的时候,会创建servlet对象(调用构造器),调用servlet的init(),然后调用service()
2、当我们再一次访问servlet的时候,就不会调用init(),只会调用service()
3、当我们正常关闭服务器的时候,会调用servlet的destroy()
HttpServletRequest #
获取请求行的方法 #
String getMethod():获取请求方式String getContextPath():获取项目的虚拟目录String getServletPath():获取servlet的虚拟路径String getQueryString():获取get请求的请求参数String getRequestURI():获取请求的URI(资源路径)StringBuffer getRequestURL():获取请求的URL(绝对路径)String getProtocol():获取协议/版本号String getRemoteAddr():获取客户端的IP地址
获取路径参数 #
path?k1=v1&k2=v2获取这种形式的参数
String getParameter(String name):根据请求参数的名称获取值Enumeration<String> getParameterNames():获取所有的请求参数的名称String[] getParameterValues(String name):根据请求参数的名称获取值(多个)Map<String,String[]> getParameterMap():将所有请求参数的名称和值都封装到了map对象
获取请求头的方法 #
String getHeader(String name):根据请求头的名称获取请求头的值Enumeration<String> getHeaderNames():获取所有的请求头的名称Enumeration<String> getHeaders(String name):根据请求头的名称获取多个请求头的值int getIntHeader(String name):根据请求头的名称获取请求头的值(请求头值为int类型的时候)
获取请求体的方法 #
BufferedReader getReader():获取ReaderServletInputStream getInputStream():获取InputStream
HttpServletResponse #
设置响应状态码 #
void setStatus(int sc)
设置响应头 #
void setHeader(String name, String value)
设置响应体 #
打印流:PrintWriter getWriter()
字节流:ServletOutputStream getOutputStream()
解决中文乱码 #
//将tomcat写出的编码由ISO-8859-1变成UTF-8
response.setCharacterEncoding("utf-8");
//上述方法,只会将服务器写出的编码为utf-8,所以,需要服务器告诉浏览器使用utf-8来打开
//推荐!!!
response.setHeader("Content-Type", "text/html;charset=utf-8");
//或
response.setContentType("text/html;charset=utf-8");
响应Html文件 #
可以通过Response实例的getOutPutStream()向浏览器进行响应,例如响应html文件
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取请求体中的参数,和数据库进比较
String name = req.getParameter("name");
String psw = req.getParameter("psw");
SqlSession sqlSession = MyBatisUtil.getSqlSession();
UsersDao usersDao = sqlSession.getMapper(UsersDao.class);
Users byNameAndPsw = usersDao.findByNameAndPsw(name, psw);
//获取当前项目路径
String contextPath = req.getServletContext().getRealPath("/");
//登录成功、失败不同的html页面
String path = null;
if (byNameAndPsw == null){
path = "fail.html";
}else {
path = "success.html";
}
//获得对应页面的输入流
InputStream inputStream = new FileInputStream(contextPath + path);
//获得输出流
ServletOutputStream outputStream = resp.getOutputStream();
//将输入流的内容,写出到输出流
byte[] bytes = new byte[1024];
int len = 0;
while ( (len = inputStream.read(bytes)) >0 ){
outputStream.write(bytes,0,len);
}
//关闭资源
outputStream.flush();
outputStream.close();
inputStream.close();
}
同一Servlet处理不同的功能 #
1、通过注解或xml文件设置该url的多个URL,例如
@WebServlet(value={"goodsType/add","goodsType/change"})
2、通过request对象的getRequestURI()方法,获取该请求的URI(如shop/goodsType/add),然后通过字符串的分割,获取具体的请求
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//解析请求的方法
String uri = req.getRequestURI();
String resUri = uri.substring(uri.lastIndexOf("/") + 1);
//根据请求类型,使用反射获取并执行对应的方法
Method method = null;
try {
method = this.getClass().getMethod(resUri, HttpServletRequest.class, HttpServletResponse.class);
method.setAccessible(true);
method.invoke(this,req,resp);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
public void add(HttpServletRequest req, HttpServletResponse resp){
}
public void change(HttpServletRequest req, HttpServletResponse resp){
}
转发和重定向 #
无论转发还是重定向,始终都是请求一次,响应一次
转发 #
调用服务端的资源(静态、动态)
request.getRequestDispatcher("/path").forward(request,response);
-
属于一次请求
-
响应头没有Location
-
可以在request范围内共享数据
-
只可以使用本应用的资源
-
地址栏不会发生变化,就是请求时的地址
重定向 #
重新定位一下,通过响应头中的Location,告诉浏览器,需要重新请求一次
response.sendRedirect();
-
两次请求
-
第一次的请求中,响应头有Location
-
无法在request范围内共享数据,可以通过session或者application(ServletContext)达到数据的共享
-
可以调用应用之外的资源
-
地址栏会发生变化,就是最后请求地址