04-Servlet

nobility 发布于 2021-09-10 881 次阅读


Servlet

简单使用

Servlet本身是一组接口,在javax包中,创建实现该接口的Java类就具备了接收客户端请求并响应的功能,在客户端访问Tomcat时,会执行Servlet类中的service()方法,使用service()方法中的servletRequest对象和servletResponse对象进行客户端和服务端之间的交互

Tomcat采用Servlet类懒加载策略,访问的详细步骤如下:

  1. 未访问时并不会创建Servlet类对象,而是等到访问时才会创建Servlet对象,利用反射机制执行无参构造方法,之后再执行init()方法进行初始化(只会执行一次)
  2. 再执行service()方法与客户端进行交互(会执行多出)
  3. 再Tomcat服务器关闭时Servlet类对象会被销毁,在销毁前执行destroy()方法
package com;

import javax.servlet.*;
import java.io.IOException;
import java.io.PrintWriter;

public class MyServlet implements Servlet {
  public MyServlet() {
    System.out.println("MyServlet 实例被创建");
  }

  @Override
  public void init(ServletConfig servletConfig) throws ServletException {
    System.out.println("servlet 初始化");
  }

  @Override
  public ServletConfig getServletConfig() {
    return null;
  }

  @Override
  public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
    String id = servletRequest.getParameter("id");  //获取请求query参数
    System.out.println("server " + id + " : 你好,世界");  //服务端输出
    servletResponse.setContentType("text/html;charset=UTF-8");  //设置响应头的编码,在响应前设置
    PrintWriter writer = servletResponse.getWriter(); //获取打印输出流对象
    writer.write("client " + id + " 你好,世界");  //向客户端输出内容
  }

  @Override
  public String getServletInfo() {
    return null;
  }

  @Override
  public void destroy() {
    System.out.println("servlet 销毁");
  }
}

因为Servlet类在编译时会被打包到WEB-INF/classes目录中,所以无法直接访问,需要在web.xml文件中进一步进行配置,之后就可以通过浏览器访问了

<servlet> <!--用于servlet取别名,保护真正的servlet类-->
  <servlet-name>MyServlet</servlet-name>  <!--servlet类的别名,可任意取,一般使用简单类名-->
  <servlet-class>com.MyServlet</servlet-class>  <!--servlet类路径,即全类名-->
</servlet>
<servlet-mapping> <!--用于映射servlet类和URL之间的关系-->
  <servlet-name>MyServlet</servlet-name>  <!--servlet中定义的别名-->
  <url-pattern>/myservlet</url-pattern> <!--servlet映射的全URL路径-->
</servlet-mapping>

Servlet的其他方法

package com;

import javax.servlet.*;
import java.io.IOException;
import java.util.Enumeration;

public class MyServlet implements Servlet {
  private ServletConfig servletConfig;  //再init()方法中为配置对象赋值,getServletConfig()方法中返回

  @Override
  public void init(ServletConfig servletConfig) throws ServletException {
    this.servletConfig = servletConfig; //为servletConfig对象赋值
    System.out.println("*************************************单个servlet配置对象**********************************");
    String servletName = servletConfig.getServletName();
    System.out.println("servletConfig.getServletName() ==> " + servletName);  //返回该Servlet的全类名
    /* 获取参数 */
    String username = servletConfig.getInitParameter("username"); //获取web.xml中配置的init-param标签中的参数
    System.out.println("servletConfig.getInitParameter() ==> " + username); //返回param-name为username的param-value中的值
    Enumeration<String> enumeration = servletConfig.getInitParameterNames(); //获取全部配置参数的param-name中的值
    System.out.print("servletConfig.getInitParameterNames() ==> ");
    while (enumeration.hasMoreElements()) { //Enumeration类与Set类似,但可以直接遍历
      String paramName = enumeration.nextElement();
      String initParameter = servletConfig.getInitParameter(paramName); //使用getInitParameter()方法获取param-value中的值
      System.out.print(paramName + " : " + initParameter + "、 ");
    }
    System.out.println();
    System.out.println("*************************************全局Context配置对象**********************************");
    ServletContext servletContext = servletConfig.getServletContext();  //获取全局的servletContext对象
    String contextPath = servletContext.getContextPath();//获取项目部署路径,即前缀URL,若是 / 则返回空字符串
    System.out.println("servletContext.getContextPath() ==> " + contextPath);
    String serverInfo = servletContext.getServerInfo(); //获取服务器名和版本信息
    System.out.println("servletContext.getServerInfo() ==> " + serverInfo);
    /* 获取参数 */
    String globalUsername = servletContext.getInitParameter("globalUsername"); //获取web.xml中配置的context-param标签中的参数
    System.out.println("servletContext.getInitParameter() ==> " + globalUsername); //返回param-name为username的param-value中的值
    Enumeration<String> globalEnumeration = servletContext.getInitParameterNames(); //获取全部配置参数的param-name中的值
    System.out.print("servletContext.getInitParameterNames() ==> ");
    while (globalEnumeration.hasMoreElements()) { //Enumeration类与Set类似,但可以直接遍历
      String paramName = globalEnumeration.nextElement();
      String initParameter = servletContext.getInitParameter(paramName); //使用getInitParameter()方法获取param-value中的值
      System.out.print(paramName + " : " + initParameter + "、 ");
    }
    System.out.println();
  }

  @Override
  public ServletConfig getServletConfig() { //返回当前Servlet配置对象,用于其他方法中获取配置对象
    return this.servletConfig;
  }

  @Override
  public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
    servletResponse.getWriter().write(" hello world");
  }

  @Override
  public String getServletInfo() {  //获取当前Servlet的信息,不需要可返回空字符串
    return "";
  }

  @Override
  public void destroy() {
  }
}

web.xml中添加初始化配置参数和上下文配置参数

<context-param><!--配置全局context的参数-->
  <param-name>globalUsername</param-name>
  <param-value>全局张三</param-value>
</context-param>
<context-param>
  <param-name>globalPassword</param-name>
  <param-value>全局123</param-value>
</context-param>

<servlet> <!--用于servlet取别名,保护真正的servlet类-->
  <servlet-name>MyServlet</servlet-name>  <!--servlet类的别名,可任意取,一般使用简单类名-->
  <servlet-class>com.MyServlet</servlet-class>  <!--servlet类路径,即全类名-->
  <init-param>	<!--配置Servlet类初始化时的参数-->
    <param-name>username</param-name>
    <param-value>张三</param-value>
  </init-param>
  <init-param>
    <param-name>password</param-name>
    <param-value>123</param-value>
  </init-param>
</servlet>
<servlet-mapping> <!--用于映射servlet类和URL之间的关系-->
  <servlet-name>MyServlet</servlet-name>  <!--servlet中定义的别名-->
  <url-pattern>/myservlet</url-pattern> <!--servlet映射的全URL路径-->
</servlet-mapping>

Servlet的通常用法

  • 使用web.xml配置URL路径的方式过于繁琐,所以提供了@WebServlet()注解配置URL路径的方式,注解方式提供配置参数过于繁琐,但是大多数情况下也不需要参数
  • 通常都只需要service()方法处理即可,service()方法中的ServletRequest类对象也无法直接获取请求方法,需要向下转型为HttpServletRequest对象才行,所以通常使用Servlet时使用的是继承HttpServlet类,选择性的复写方法
  • HttpServlet是对Servlet的进一步封装,请求和响应对象是HttpServletRequestHttpServletResponse对象,进一步封装了请求响应对象
package com;

import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/myservlet")
public class MyServlet extends HttpServlet {
//  @Override
//  protected void service(HttpServletRequest request, HttpServletResponse response) throws IOException {
//    //处理任意请求,使用该方法后doPost()和doGet()方法将不会执行
//    response.getWriter().write(" service: hello world!!!");
//    response.getWriter().write(request.getMethod());
//  }

  protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
    //处理post请求
    response.getWriter().write(" Post: hello world");
  }

  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
    //处理get请求
    response.getWriter().write(" Get: hello world");
  }
}

以下是测试用的index.jsp,注意要加项目前缀

<form action="/javaWeb/myservlet" method="get">
    <input type="submit" value="get提交"/>
</form>
<form action="/javaWeb/myservlet" method="post">
    <input type="submit" value="post提交"/>
</form>

动态路由模拟

@WebServlet("/myservlet/*") //使用星号通配符
public class MyServlet extends HttpServlet {
  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
    String url = request.getRequestURL().toString();  //获取请求URL地址
    String urlDecode = URLDecoder.decode(url, "utf-8"); //对URL百分号转义进行解码
    String parameter = urlDecode.substring(urlDecode.lastIndexOf("/") + 1); //截取最后一个反斜杠后的参数
    response.setContentType("text/html;charset=utf-8");
    response.getWriter().write(parameter);
  }
}

请求参数(解决中文乱码)

对于Post请求,在解析参数前需要添加request.setCharacterEncoding("UTF-8");设置,否则中文会乱码,因为默认请求体编码是ISO-8859-1

对于Get请求,Tomcat8及以上版本Get请求参数默认就是UTF-8,Tomcat7需要在Tomcat的server.xml配置文件中找到这项配置:<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />,添加一个属性字段URLEncoding="UTF-8",改为这样<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" URLEncoding="UTF-8" />即可

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
  //访问 http://localhost:8080/javaWeb/myservlet?id=1&id=2
  //request.setCharacterEncoding("UTF-8");	//对于post请求来说需要加上该设置否则中文会乱码
  PrintWriter out = response.getWriter();
  String id = request.getParameter("id"); //若有多个只会返回第一个
  out.println(id);
  String[] ids = request.getParameterValues("id");  //返回所有该参数的值
  out.println(Arrays.toString(ids));
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
  PrintWriter out = response.getWriter();
  Cookie[] cookies = request.getCookies() == null ? new Cookie[]{} : request.getCookies();
  //获取所有cookie,若该返回null则替换为空Cookie数组,避免空指针异常
  for (Cookie cookie : cookies) { //遍历所有cookie
    out.println(cookie.getName() + " : " + cookie.getValue());
  }

  Cookie cookie = new Cookie("key", "value"); //创建cookie对象
  cookie.setMaxAge(30); //设置cookie失效,单位秒,不设置默认是session级别的
  cookie.setHttpOnly(true); //设置httpOnly,默认是false
  response.addCookie(cookie); //将cookie对象添加到响应对象中
}

请求转发和重定向

在请求对象上设置的自定义属性仅本次请求有效

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException, ServletException {
  request.setAttribute("key", "value");  //设置自定义属性,属性值可以是任意数据类型
  String key = (String) request.getAttribute("key");  //获取自定义属性,返回Object,需强制类型转化
  request.getRequestDispatcher(request.getContextPath() + "/index.jsp").forward(request, response);  //请求转发,使用forward方法将请求响应对象转发
  response.sendRedirect(request.getContextPath() + "/index.jsp");  //请求重定向
  //通常会使用request.getContextPath()进行项目名的拼接
}
请求转发 响应重定向
使用request对象的方法 使用response对象方法
一次请求,服务器内转发 两次请求,客户端重新请求
一次请求,所以可使用setAttribute()getAttribute()方法挂自定义属性
getAttribute()方法返回Object对象,需要强制类型转化)
两次请求,所以setAttribute()getAttribute()方法挂自定义属性不会传递到重定向的URL而返回null
getAttribute()方法返回Object对象,需要强制类型转化)
地址栏不会改变 地址栏会改变

Session

在session对象上设置的自定义属性仅本次会话有效

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
  HttpSession session = request.getSession(); //获取session对象
  session.setMaxInactiveInterval(10); //session为操作失效时间间隔,单位秒,默认30分钟
  session.setAttribute("key", "value");  //与request.setAttribute()用法一致
  //session.invalidate();	//使当前session失效
  String key = (String) session.getAttribute("key");  //与request.getAttribute()用法一致
}

ServletContext

在ServletContext对象上设置的自定义属性在整个程序运行中都有效

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
  ServletContext servletContext = request.getServletContext();	//获取ServletContext对象
  servletContext.setAttribute("key", "value");	//与request.setAttribute()用法一致
  String key = (String) servletContext.getAttribute("key");	//与request.getAttribute()用法一致
}

Servlet预加载

Servlet预加载通常用于数据库导入等费时操作,一般不需要对应URL,只是用于初始化,所以配置URL就不要与其他配置冲突即可

web.xml配置方式:

<servlet> <!--用于servlet取别名,保护真正的servlet类-->
  <servlet-name>MyServlet</servlet-name>  <!--servlet类的别名,可任意取,一般使用简单类名-->
  <servlet-class>com.MyServlet</servlet-class>  <!--servlet类路径,即全类名-->
  <load-on-startup>0</load-on-startup>  <!--打破servlet懒加载,使用预预加载,值越大越先加载-->
</servlet>
<servlet-mapping> <!--用于映射servlet类和URL之间的关系-->
  <servlet-name>MyServlet</servlet-name>  <!--servlet中定义的别名-->
  <url-pattern>/myservlet</url-pattern> <!--servlet映射的全URL路径-->
</servlet-mapping>

注解配置方式:

@WebServlet(urlPatterns = "/myservlet",loadOnStartup = 0)

web.xml中的其他配置

<welcome-file-list> <!--配置当URL以 / 结尾,则自动按顺序配匹下面文件名-->
  <welcome-file>index.html</welcome-file> <!--默认就是这些配置,一般不用配置-->
  <welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<error-page>	<!--线上环境下需要配置404和500等错误页面,以免暴露出程序的漏洞-->
  <error-code>404</error-code>
  <location>/error/404.html</location>
</error-page>
<error-page>
  <error-code>500</error-code>
  <location>/error/500.html</location>
</error-page>
此作者没有提供个人介绍
最后更新于 2021-09-10