• Servlet


    Servlet
    Servlet 是一个用于扩展服务器端功能的服务器端组件技术
    运行在服务器端,所以调用执行是由服务器负责
    web.xml 中需要进行配置,就是将 servlet 和一个请求地址建立对应关系,当浏览器对地址
    发起请求时,服务器则按照规则调用 serlvet 类中的方法
    服务器端的组件技术
    直接或者间接的实现 Servlet 接口
    用于扩展服务器端功能,可以实现动态网页的开发
    Servlet 接口
    1 、定义类实现 Servlet 接口
    1. public class HelloServlet implements Servlet {
    2. //在Servlet类实例化后自动执行的方法,这个方法在servlet对象的整个生命周期中运行且只运
    3. 行一次。主要用于实现Servlet的初始化操作,例如读取配置文件中当前Servlet的初始化配置信息。
    4. 当服务器调用该方法时会将当前servlet的初始化配置存储在cnfig对象中
    5. public void init(ServletConfig config) throws ServletException {
    6. this.config=config; //缓存config对象
    7. }
    8. //用于供其它位置获取config对象时进行调用
    9. private ServletConfig config;
    10. public ServletConfig getServletConfig() {
    11. return config;
    12. }
    13. //在init方法执行后,服务器调用service方法用于生成针对客户端的响应信息。服务器采用多
    14. 线程的方式运行service方法,一个客户端请求对应一个线程。服务器调用service方法时会传入2个参
    15. 数对象,req用于封装客户端的请求信息,resp用于封装服务器的响应信息。Servlet默认采用单实例
    16. 多线程的方式对客户端浏览器请求提供服务,service执行完成后不会自动销毁,而是常驻内存
    17. public void service(ServletRequest req, ServletResponse resp) throws
    18. ServletException, IOException {
    19. }
    20. //一般供获取当前servlet对象的说明信息
    21. public String getServletInfo() {
    22. return "当前servlet对象的说明信息";
    23. }
    24. //在servlet对象销毁之前执行,用于进行资源回收。一个servlet对象在整个生命周期运行且只
    25. 运行一次。servlet对象默认是常驻内存,只有在服务器关闭或者内存严重不足而当前Servlet对象被
    26. GC机制选中时才会被销毁
    27. public void destroy() {
    28. }
    29. }
    2 、在 web.xml 中针对 servlet 类进行配置,将 servlet 和一个或者多个地址建立对应关系
    1. "1.0" encoding="UTF-8"?>
    2. <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    3. xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    4. xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
    5. http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
    6. id="WebApp_ID" version="4.0"> xml文件头用于说明当前xml文件的语法规则
    7. <servlet> 将servlet类和一个名称建立对应关系
    8. <servlet-name>helloservlet-name>
    9. <servlet-class>com.yan.action.HelloServletservlet-class>
    10. servlet>
    11. <servlet-mapping> 将一个servlet名称和请求地址建立对应关系
    12. <servlet-name>helloservlet-name>
    13. <url-pattern>/hello.dourl-pattern>
    14. servlet-mapping>
    15. web-app>
    servlet 三生命周期
    init
    service
    单实例多线程的方式对外提供服务
    常驻内存
    detroy
    Servlet 开发
    直接实现 Servlet 接口
    抽象类 GenericServlet 用于非标准协议开发,例如游戏的服务器端
    抽象类 HttpServlet GenericServlet 的子类,用于 http 协议应用开发,例如网站
    HttpServlet 的运行原理
    1. public abstract class HttpServlet extends GenericServlet,针对Servlet接口的5种
    2. 方法都提供了默认实现
    3. //按照Servlet的运行规则,服务器会调用service方法对外提供服务。
    4. protected void service(HttpServletRequest req, HttpServletResponse resp)
    5. throws ServletException, IOException {
    6. String method = req.getMethod(); //获取请求方法,http协议定义了7种请求,
    7. 例如get/post/put/delete,...
    8. if (method.equals(METHOD_GET)) { //如果请求方法为GET,则调用doGet方法进
    9. 行处理
    10. doGet(req, resp);
    11. } else if (method.equals(METHOD_HEAD)) {
    12. doHead(req, resp);
    13. } else if (method.equals(METHOD_POST)) {
    14. doPost(req, resp);
    15. } else if (method.equals(METHOD_PUT)) {
    16. doPut(req, resp);
    17. } else if (method.equals(METHOD_DELETE)) {
    18. doDelete(req, resp);
    19. } else if (method.equals(METHOD_OPTIONS)) {
    20. doOptions(req,resp);
    21. } else if (method.equals(METHOD_TRACE)) {
    22. doTrace(req,resp);
    23. } else {
    24. String errMsg =
    25. lStrings.getString("http.method_not_implemented");
    26. Object[] errArgs = new Object[1];
    27. errArgs[0] = method;
    28. errMsg = MessageFormat.format(errMsg, errArgs);
    29. resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
    30. }
    31. }
    32. //针对不同的请求方法提供了对应的扩展点,运行子类进行覆盖定义
    33. protected void doGet(HttpServletRequest req, HttpServletResponse resp)
    34. throws ServletException, IOException {
    35. String protocol = req.getProtocol(); //获取请求协议,例如http
    36. String msg = lStrings.getString("http.method_get_not_supported");
    37. //根据参数获取默认的报错信息
    38. if (protocol.endsWith("1.1")) { 判断http协议的版本号,发送报错信息
    39. resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
    40. } else {
    41. resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
    42. }
    43. }
    Servlet 初始化配置
    在服务器调用 Servlet init 方法时会传入一个 ServletConfig 对象,其中包含当前 Servlet 的所有初始化配 置
    1. public void init(ServletConfig config) throws ServletException {}
    2. //GenericServlet中的方法实现
    3. public void init(ServletConfig config) throws ServletException {
    4. this.config = config; //缓存服务器提供的config对象
    5. this.init(); //提供扩展点
    6. }
    配置位于 web.xml 文件
    1. <servlet>
    2. <servlet-name>hello3servlet-name>
    3. <servlet-class>com.yan.action.Hello3Servletservlet-class>
    4. <init-param> 初始化参数,可以配置多个
    5. <param-name>rowsparam-name> 配置的参数名称
    6. <param-value>3param-value> 对应配置参数名称的配置值
    7. init-param>
    8. <init-param>
    9. <param-name>widthparam-name>
    10. <param-value>120param-value>
    11. init-param>
    12. servlet>
    servlet 中获取配置参数
    可以在 GenericServlet 提供的扩展点中获取配置参数
    1. @Override
    2. public void init() throws ServletException {
    3. //获取对应名称的初始化参数值,如果没有配置则返回为null
    4. //这个获取初始化参数的方法也可以在其它方法中进行调用,从而获取配置参数
    5. String rows = this.getServletConfig().getInitParameter("rows");
    6. System.out.println(rows);
    7. //获取当前servlet的配置名称,对应web.xml中的<servletname>hello3servlet-name>
    8. System.out.println(
    9. this.getServletConfig().getServletName()
    10. ):
    11. //获取所有当前servlet的初始化配置参数名称的迭代器,类似Iterator
    12. Enumeration<String> initParameterNames =
    13. this.getServletConfig().getInitParameterNames();
    14. while(initParameterNames.hasMoreElements()){
    15. String name=initParameterNames.nextElement(); //获取每个初始化配置
    16. 参数名称
    17. String value=this.getServletConfig().getInitParameter(name);//根
    18. 据配置参数名称读取对应的参数配置值
    19. System.out.println(name+"-->"+value);
    20. }
    21. //可以通过config对象获取当前应用的上下文
    22. final ServletContext context =
    23. this.getServletConfig().getServletContext();
    24. }
    其它配置
    接收客户端请求
    request 对象中封装的有客户端的请求参数信息
    getParamter(" 参数名称 "):String
    getParameterValues(" 参数名称 "):String[]
    getParameterMap():Map
    request 对象是 HttpServletRequest 接口类型,由服务器提供的实现,服务器在调用 service 方法时
    传入
    request 中的数据有 3 个不同的部分,请求头信息 header 【浏览器发送的请求信息】、请求参数
    parameter 【用户发送的相关参数信息】、属性信息 attribute 【一般用于编程中的数据传递】,前
    两个只是用于读取,并没有提供修改方法,属性信息提供了读写操作方法
    特殊方法
    getReader() 或者 getInputStream() 获取输入流,直接操作流
    请求头
    一般数据是传送给服务器使用的,不是给应用直接使用的。只有在特殊需求时使用,例如收集客户端所
    使用的浏览器信息【 User-Agent
    @Override
    public void init () throws ServletException {
    // 获取对应名称的初始化参数值,如果没有配置则返回为 null
    // 这个获取初始化参数的方法也可以在其它方法中进行调用,从而获取配置参数
    String rows = this . getServletConfig (). getInitParameter ( "rows" );
    System . out . println ( rows );
    // 获取当前 servlet 的配置名称,对应 web.xml 中的
    name>hello3
    System . out . println (
    this . getServletConfig (). getServletName ()
    ):
    // 获取所有当前 servlet 的初始化配置参数名称的迭代器,类似 Iterator
    Enumeration < String > initParameterNames =
    this . getServletConfig (). getInitParameterNames ();
    while ( initParameterNames . hasMoreElements ()){
    String name = initParameterNames . nextElement (); // 获取每个初始化配置
    参数名称
    String value = this . getServletConfig (). getInitParameter ( name ); //
    据配置参数名称读取对应的参数配置值
    System . out . println ( name + "-->" + value );
    }
    // 可以通过 config 对象获取当前应用的上下文
    final ServletContext context =
    this . getServletConfig (). getServletContext ();
    }
    public class AddServlet extends HttpServlet {
    @Override
    protected void doGet ( HttpServletRequest request , HttpServletResponse
    response ) throws ServletException , IOException {
    请求参数
    无论是 get 请求还是 post 请求,获取请求参数的方法一致,只是编程时需要注意对应的 doXxx 方法
    如果针对请求没有对应的 doXxx 方法,则会从父类中继承得到默认实现。实现是报错 405
    如果 get 请求和 post 请求处理逻辑一致,则需要自行编码实现
    获取请求参数数据的方法
    // 操作请求头参数
    final String host = request . getHeader ( "Host" ); // 根据名称获取请求头中指
    定的参数值
    System . out . println ( host ); //localhost:8080
    // 另外根据发送数据的类型 request.getIntHeader(" 名称 "):Int
    request.getDateHeader():long
    // 获取所有的请求头参数名称的枚举对象
    final Enumeration < String > headerNames = request . getHeaderNames ();
    while ( headerNames . hasMoreElements ()){
    String headerName = headerNames . nextElement ();
    String headerValue = request . getHeader ( headerName );
    System . out . println ( headerName + "-->" + headerValue );
    }
    String name = "Accept-Encoding" ; // 遍历一个名称对应的多个配置值的情形
    final Enumeration < String > enumeration = request . getHeaders ( "Accept
    Encoding" ); // 请求头中一个名称对应多个参数值
    while ( enumeration . hasMoreElements ()){
    String tmp = enumeration . nextElement ();
    System . out . println ( name + "<-->" + tmp );
    }
    }
    }
    public class AddServlet extends HttpServlet {
    @Override
    protected void doGet ( HttpServletRequest request , HttpServletResponse
    response ) throws ServletException , IOException {
    this . doPost ( request , response ); // get 请求转发给 doPost 进行处理
    }
    @Override
    protected void doPost ( HttpServletRequest req , HttpServletResponse
    resp ) throws ServletException , IOException {
    }
    }
    @Override
    protected void doPost ( HttpServletRequest request , HttpServletResponse
    resp ) throws ServletException , IOException {
    // 根据参数名称获取对应的参数值
    String username = request . getParameter ( "username" ); // 在网络数据传输中只有
    String 类型 , 其它类型需要自行编码实现数据类型转换
    // 因为 DateFormat 不是线程安全的,所以 DateFormat 不能定义为属性
    System . out . println ( username );
    // 获取用于遍历所有请求参数名称的迭代器对象
    针对一个名称传递多个数据的情形
    针对 checkbox 使用一个名称传递多个数据则必须使用 getParameterValues 获取数据,如果使用
    getParameter 则会有数据丢失
    常见问题 1 :类型转换
    数据传输都是 String 类型获取数据后需要进行类型转换,但是转换会有异常
    实际处理中有 2 种不同的处理方案:方案 1 记录日志并继续向上抛出;方案 2 采用默认值
    final Enumeration < String > parameterNames =
    request . getParameterNames ();
    while ( parameterNames . hasMoreElements ()){
    String parameterName = parameterNames . nextElement ();
    String parameterValue = request . getParameter ( parameterName );
    System . out . println ( parameterName + "-->" + parameterValue );
    }
    // 获取所有请求参数
    final Map < String , String [] > parameterMap =
    request . getParameterMap ();
    for ( String tmpName : parameterMap . keySet ()){
    String [] params = parameterMap . get ( tmpName );
    System . out . println ( tmpName + "<===>" + Arrays . toString ( params
    ));
    }
    // 针对 checkbox 获取数据的方法
    final String [] hobbies = request . getParameterValues ( "hobby" );
    for ( String tmp : hobbies )
    System . out . println ( tmp );
    }
    action = "add.do" method = "post" >
    用户名称: name = "username" />
    用户爱好: type = "checkbox" name = "hobby" value = " 篮球 " > 篮球
    type = "checkbox" name = "hobby" value = "111" > 足球
    type = "checkbox" name = "hobby" value = "222" checked > 乒乓球
    type = "checkbox" name = "hobby" value = "333" > 其它
    type = "submit" value = " 提交数据 " />
    // 针对 checkbox 获取数据的方法
    final String[] hobbies = request.getParameterValues("hobby");
    for(String tmp:hobbies)
    System.out.println(tmp);
    4 常见问题 2 :一个名字对应多个数据的存储和显示
    MySQL 中并不建议使用 enum 枚举类型和 set 集合类型
    多个选项的存储方案:
    具体入库数据
    一种直接存储选项的显示值,优势在于方便显示
    另外的存储方式存储的时字典表中对应的 id 值,优势在于方便修改,最大的缺陷在于额外查询
    请求参数的中文问题
    为了重现问题使用 tomcat7 插件
    1. <plugin>
    2. <groupId>org.apache.tomcat.mavengroupId>
    3. <artifactId>tomcat7-maven-pluginartifactId>
    4. <version>2.2version>
    5. <configuration>
    6. <path>/pppath> 上下文路径名称,也就是访问应用的路径为/pp
    7. <port>9090port> 端口号的配置,localhost:9090/pp/
    8. <uriEncoding>UTF-8uriEncoding> 默认的编码字符集
    9. configuration>
    10. plugin>
    因为在整个数据提交过程中会涉及多处编码字符集问题, 1 、提交所采用的编码字符集。
    charset="UTF - 8"> 2 、在互联网上传输数据默认编码字符集为 ISO-8859-1 3 、服务器接收数据后
    的编码处理
    请求参数的中文乱码问题有 3 种解决方案
    针对 get 请求,在具体应用中基本不可用
    针对 post 请求
    通用解决方案
    1. //根据参数名称获取对应的参数值
    2. String username=request.getParameter("username");//在网络数据传输中只有
    3. String类型,其它类型需要自行编码实现数据类型转换
    4. //使用ISO8859-1进行解码转换,转换为原始的字节数组
    5. byte[] arr=username.getBytes(StandardCharsets.ISO_8859_1);
    6. //再次使用UTF-8进行编码
    7. username=new String(arr, Charset.forName("UTF-8"));
    8. System.out.println(username);

    理论上来说采用通用解决方案可以处理 get post 的中文乱码问题,但是一般不采用这种方式,因为处理 比较繁琐。具体开发实践中注意一些小细节即可,不用通用解决方案
    一般不采用 get 提供中文,如果需要传递数据有限考虑采用代码值之类的方式。例如查询教学部的
    所有雇员,并不传递中文的【教学部】参数,而是传递教学部对应的编号值
    提交数据的 html 页面编码字符集统一为 UTF-8
    如果需要传递中文信息,可以采用
    提交数据, 在所有接收数据
    request.getPamrameter 之前先进行编码转换 request.setCharacterEncoding("UTF - 8") ;
  • 相关阅读:
    LFMCW雷达的距离分辨率
    -bash: jps: command not found
    数据结构笔记(王道考研) 第六章:图
    VUE3-博客全栈 08-前端
    AI学习笔记之六:无监督学习如何帮助人类挖掘数据金矿和防范网络欺诈
    .NET6发布项目到腾讯云Windows2012R全网最详细教程
    springboot整合全文搜索引擎Elasticsearch Spring Boot 28
    Python实现最小公约数和最小公倍数
    前端el-date-picker传递的日期格式不是自己想要的格式
    845. 八数码
  • 原文地址:https://blog.csdn.net/zjh0101/article/details/127854479