目录

Servlet原理及其使用

什么是Servlet

是JAVA的基于WEB服务器的完成动态网页开发的一种技术

Servlet的使用

编写一个Servlet,使用继承HttpServlet的方式

package com.it.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.*;
import java.io.IOException;
public class MyFirstServlet extends HttpServlet { @Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取表单提交的参数,填写的参数是表单对应数据的name属性
String username = req.getParameter("username");
String password = req.getParameter("password"); if(username.equals("dqx") && password.equals("123")) {
// 转发到/success.jsp
req.getRequestDispatcher("/success.jsp").forward(req,resp);
}else {
// 在request作用域中存入一个 key value
req.setAttribute("msg", "用户名或密码错误");
req.getRequestDispatcher("/login.jsp").forward(req, resp);
}
}
}

配置web.xml

    <servlet>
<servlet-name>loginServlet</servlet-name>
<servlet-class>com.it.servlet.MyFirstServlet</servlet-class>
<!-- 配置在服务器启动时加载Servlet -->
<load-on-startup>0</load-on-startup>
</servlet> <servlet-mapping>
<servlet-name>loginServlet</servlet-name>
<!-- 处理的请求 -->
<url-pattern>/doLogin</url-pattern>
</servlet-mapping>

很简单的几个JSP文件

  • login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<%
String path = request.getContextPath();
String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/";
%>
<base href="<%=basePath%>">
</head>
<body>
<form action="doLogin" method="post">
<%
String msg = (String) request.getAttribute("msg");
System.out.println("---------");
if(msg != null) {
out.print(msg);
}
%>
<hr>
用户名:<input type="text" name="username"> <br>
密码:<input type="password" name="password"> <br>
<input type="submit" value="登录">
</form>
</body>
</html>
  • success.jsp
<h1>登录成功</h1>

小提示,如果继承HttpServlet发现找不到类的时候,引入一个jar包添加到类路径(必须放在WEB-INF目录下)



——————————————————————————————————

Servlet读取配置(自身Servlet配置、全局配置)

为什么需要读取配置?

答:因为有些配置是固定的(例如编码),许多地方都要使用到,而如果硬编码的话,一旦编码改变,则需要更改多处,影响维护。

获得Servlet自己的配置信息

  • 在web.xml中配置参数
    <servlet>
<servlet-name>myConfigServlet</servlet-name>
<servlet-class>com.it.servlet.MyConfigServlet</servlet-class>
<!-- 配置Servlet配置参数 -->
<init-param>
<param-name>charset</param-name>
<param-value>UTF-8</param-value>
</init-param>
</servlet>
  • 在Servlet中获取配置信息(可以在构造函数、init()方法、等等地方都能获取到)
public class MyConfigServlet extends HttpServlet {
private String charset;
@Override
public void init() throws ServletException {
// 获得Servlet参数,这两种方式是一样的
charset = this.getInitParameter("charset");
charset = this.getServletConfig().getInitParameter("charset");
}

获得全局配置信息

  • 在web.xml中配置全局参数
    <context-param>
<param-name>globalCharset</param-name>
<param-value>GBK</param-value>
</context-param>
  • 在Servlet中获取
public class MyConfigServlet extends HttpServlet {
private String globalCharset;
@Override
public void init() throws ServletException {
// 获得全局配置参数
globalCharset = this.getServletContext().getInitParameter("globalCharset");
System.out.println(globalCharset);
}

解决HTTP请求当中的乱码问题(get、post、response)

response响应乱码

  • response响应乱码出现的原因?

    答:由于浏览器不知道服务器响应的内容是什么编码的,浏览器默认使用GBK编码,而如果服务器响应的数据是不是GBK编码的时候,浏览器就会乱码
  • 解决方法

    答:在响应中告知浏览器以什么编码来解析数据
// 注意,这里是因为演示,所以硬编码了,实际编码应该从配置中读取
// 这句话就是告知浏览器接收到响应数据时以何种类型处理,以何种编码解析
resp.setContentType("text/html;charset=UTF-8");

post请求时接收参数乱码

  • post请求出现乱码的原因

    答:浏览器进行post请求时(表单提交),浏览器的页面编码与服务器接收处理参数的编码不一致时,就会出现乱码。
  • 解决方法,设置request处理请求参数的编码
request.setCharacterEncoding("UTF-8");

get请求时,接收浏览器的url中的参数时乱码

  • get请求出现乱码的原因

    答:浏览器进行get请求(参数是在URI上)时,浏览器的页面编码(例如浏览器该页面是UTF-8编码)与服务器接收处理URI参数的编码(tomcat默认是IOS-8895-1)不一致时,就会出现乱码。
  • 解决方案有2种
    • 通过修改tomcat服务器中的conf目录中的server.xml文件,添加URIEncoding配置
        <Connector port="8080" protocol="HTTP/1.1"
    connectionTimeout="20000"
    redirectPort="8443" URIEncoding="utf-8" />
    • 在接受参数后,将数据转换为与页面提交的数据一致的编码
    byte[] bytes = username.getBytes("ISO-8859-1");
    String newUsername = new String(bytes,"UTF-8");

Servlet的注解支持(代替xml配置servlet方式)

可以在Servlet类上使用注解的方式代替在web.xml中的配置

import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet; @WebServlet(loadOnStartup = 0,
initParams = {@WebInitParam(name = "charset",value = "UTF-8"),@WebInitParam(name = "key2",value = "value2")},
urlPatterns = "/AnnoServlet")
public class AnnoServlet extends HttpServlet {
}

Servlet的原理

Servlet的响应原理

  • 浏览器发送请求到服务器
  • 服务器通过web.xml中配置的servlet-mapping找到对应的映射
  • 通过匹配到映射找到对应的servlet,通过反射创建其对象
  • 调用该对象中的service方法处理请求
  • 处理请求后使用将响应结果传输给浏览器

Servlet的生命周期

  • 类加载(Servlet加载)
  • 实例化(利用反射创建对象,单例)
  • init初始化
  • service 处理请求
  • destroy 销毁

Servlet的加载时机

  • 在Servlet第一次处理请求时加载(默认)
  • 在服务器启动时就进行加载,在web.xml中对应的servlet标签中添加load-on-startup标签(一般值为0-10),优先级从大到小

Servlet的API关系

  • javax.servlet.Servlet接口
  • javax.servlet.GenericServlet 抽象类,实现了Servlet接口
  • javax.servlet.http.HttpServlet 普通类,继承了GenericServlet抽象类

    所以,想要编写一个Servlet必须要实现或者间接实现Servlet接口,一般使用最多的就是继承HttpServlet
  • 常用方法
// Servlet接口中的方法
void service(ServletRequest var1, ServletResponse var2);//核心方法,用于处理浏览器发送的请求
ServletConfig getServletConfig(); //用于获取Servlet的配置信息

为什么继承了HTTPServlet不重写Service访问时会出现405报错?

答:因为继承了HTTPServlet方法没有重写service()方法或doGet()、doPost()方法

HTTPServlet为什么要这么处理?

答:因为本身我们开发者编写一个Servlet类就是为了处理请求,而既没有service()方法处理请求,也没有doGet()、doPost(),那这个Servlet存在的意义就缺失了。所以HttpServlet处理时就直接报错,是为了告知调用者重写service()或者doGet()、doPost()等方法

为什么service()方法是处理请求的,但是重写doGet、doPost()等方法也有效?
  • HttpServlet中通过获得请求的方式(get、post)后,还是会调用doGet、doPost等方法
  • 如果继承了HttpServlet的类重写了doGet方法,那么最后HttpServlet中的service方法在执行时判断了请求的方式是get请求后,会执行子类重写的doGet方法(多态)。

Servlet运行的Web项目中的4种路径问题

1、绝对路径(访问任何资源)

// 格式: 协议://IP地址:端口号/资源路径
<a href="http://127.0.0.1:8080/myProject1/path1/update.jsp">同一个项目下同一个文件夹中的文件update.jsp</a> <br/>
<a href="http://127.0.0.1:8080/myProject1/path1/subpath/delete.jsp">同一个项目下同子文件夹中的文件delete.jsp</a> <br/>
<a href="http://127.0.0.1:8080/myProject1/path2/add.jsp">同一个项目下同级文件夹中的文件add.jsp</a> <br/>
<a href="http://127.0.0.1:8080/myProject1/index.jsp">同一个项目下父级文件夹中的文件index.jsp</a> <br/>
<a href="http://127.0.0.1:8080/myProject1/servlet/DoLogin2">访问servlet的路径</a> <br/>
<a href="http://127.0.0.1:8080/myProject1/index.jsp">同一个服务器中不同项目中的文件servlet01/index.jsp</a> <br/>
<a href="http://192.168.121.33:8080/demo/a.jsp">同一个局域网中不同服务器中的项目</a> <br/>
<a href="http://www.baidu.com">外网服务器的项目</a> <br/>

2、根路径(访问当前服务器的任何资源)

提示:只能访问当前服务器的任何资源

<a href="/webdemo01/path1/update.jsp">同一个项目下同一个文件夹中的文件update.jsp</a> <br/>
<a href="/webdemo01/path1/subpath/delete.jsp">同一个项目下同子文件夹中的文件delete.jsp</a> <br/>
<a href="/webdemo01/path2/add.jsp">同一个项目下同级文件夹中的文件add.jsp</a> <br/>
<a href="/webdemo01/index.jsp">同一个项目下父级文件夹中的文件index.jsp</a> <br/>
<a href="/webdemo01/servlet/DoLogin2">访问servlet的路径</a> <br/>
<a href="/webdemo01/index.jsp">同一个服务器中不同项目中的文件servlet01/index.jsp</a> <br/>
<%--不行--%>
<a href="/demo/a.jsp">同一个局域网中不同服务器中的项目</a> <br/>

3、相对路径(访问当前服务器的资源)、不推荐

<a href="update.jsp">同一个项目下同一个文件夹中的文件update.jsp</a> <br/>
<a href="subpath/delete.jsp">同一个项目下同子文件夹中的文件delete.jsp</a> <br/>
<a href="../path2/add.jsp">同一个项目下同级文件夹中的文件add.jsp</a> <br/>
<a href="../index.jsp">同一个项目下父级文件夹中的文件index.jsp</a> <br/>
<a href="../servlet/DoLogin2">访问servlet的路径</a> <br/>
<a href="../../myProject2/index.jsp">同一个服务器中不同项目中的文件

4、相对路径(基于base标签,访问当前项目,其实实质还是绝对路径或根路径)

<head>
<%
// 这是绝对路径的base标签
String path = request.getContextPath();
String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/"; // 根路径的base标签
String genBasePath = request.getContextPath() + "/";
%>
<base href="<%=genBasePath%>">
</head>
<body>
<%-- 注意,这里不要在写一个/了,不然就变成根路径与base标签无关了了 --%>
<a href="index.jsp">访问index.jsp</a>
</body>

使用idea配置模板

不同路径的使用场景

  • 路径适用于jsp页面中的 a、form、src、link等等
  • 访问其他服务器时:绝对路径
  • 访问自己服务器其他项目时: 根路径
  • 访问当前项目:base路径

转发与重定向的原理及区别

原理不同

  • 转发,指的是在服务器跳转

    服务器在内部接收到第一个请求时,转发到其他servlet或页面处理,然后返回数据给用户。

    这个过程用户是不可见的,所以转发时,浏览器只会向服务器发出一个请求

  • 重定向,客户端跳转

    服务器在运行到重定向的语句时,将重定向请求发回给浏览器,其状态码为302(并携带者需要重定向的资源路径)

    浏览器在接收到这个302的重定向响应后,用其中的资源路径地址再次向服务器发送一个请求

    所以这个过程中涉及到了2个请求。所以重定向是用户可见的。

语法实现方式不同

// 转发(这里使用的是相对路径)
req.getRequestDispatcher("index.jsp").forward(req, resp); // 重定向(这里使用的是相对路径)
resp.sendRedirect("index.jsp");

获取request作用域数据问题

转发能够获取request作用域中的数据,因为其转发的原理就是至始至终都只有一个请求,而request作用域的范围就是一个请求当中,所以能够获取

重定向不能获取request中的数据,因为重定向时,涉及到了2个请求,所以第二个请求已经不在第一个请求的作用范围内了,所以无法获取。

跳转页面后,网址的显示不同

转发跳转后,由于是服务器内部跳转,浏览器无法察觉,所以还是显示的浏览器访问的资源路径。

重定向跳转后,由于是客户端跳转,跳转对浏览器是可见的,所以显示的路径是重定向的路径。

书写代码时,使用“ / ”的意义不同

转发时候的字符串时候由于路径最前面会加上/项目名,所以写 / 代表着当前项目,这也意味着。转发只能转发到当前的项目资源。

支持书写的路径不同

  • 绝对路径
  • 转发:不支持
  • 重定向:支持
  • 相对路径(不推荐)
  • 转发:支持
  • 重定向:支持
  • 根路径
  • 转发:支持(当前项目)
  • 重定向:支持(代表当前服务器,与在jsp中使用根路径是一样的)

使用表单时重复提交问题

转发:会出现表单重复提交的问题,在提交表单时使用转发,在转发后的页面,如果刷新网页,就会重复提交表单。(所以提交表单推荐使用重定向)

重定向:不会出现重复提交表单的问题

效率问题

转发:效率更高(服务器只用处理一个请求)

重定向:效率低,因为浏览器发送了2个请求,服务器响应2个请求

是否经过配置的过滤器

转发:不会,因为是在服务器内部,请求刚进来的时候就已经经过了过滤器,不会再重复执行过滤器

重定向:会,因为是2个请求。

Request请求与Response响应的更多方法

Request的更多方法

/* 获取请求头信息******/
Enumeration headerNames = req.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
String headerValue = req.getHeader(headerName);
System.out.println(headerName + "----" + headerValue);
}
System.out.println("-----------------------------------------"); /*****获得URL地址信息******/
// 获得协议:HTTP
String scheme = req.getScheme();
System.out.println(scheme); // 获得服务器IP : 127.0.0.1
String serverName = req.getServerName();
System.out.println(serverName); // 获得端口 : 8080
int serverPort = req.getServerPort();
System.out.println(serverPort); // 获得项目名 /servlet01
String contextPath = req.getContextPath();
System.out.println(contextPath); // 获得请求的服务路径,用根路径表示: /info
String servletPath = req.getServletPath();
System.out.println(servletPath); // 请求方式:GET
String method = req.getMethod();
System.out.println(method); // http://localhost:8080/servlet01/info
String s = req.getRequestURL().toString();
System.out.println(s); // :/servlet01/info
String requestURI = req.getRequestURI();
System.out.println(requestURI); /*****获取数据******/
// 获取表单提交的数据
String username = req.getParameter("username"); // 获取request作用域中的数据
Object user = req.getAttribute("user"); // 获取多个name相同的参数
String[] hobbies = req.getParameterValues("hobby"); /*****获得浏览器信息和服务器信息******/
// 获得远程访问服务器的客户端IP
String remoteAddr = req.getRemoteAddr();
System.out.println("访问者的IP为:" + remoteAddr); // 获得远程访问服务器的主机名
String remoteHost = req.getRemoteHost();
System.out.println(remoteHost); // 获得远程访问服务器的端口
int remotePort = req.getRemotePort();
System.out.println(remotePort); // 以下为获取本地
String localAddr = req.getLocalAddr();
String localName = req.getLocalName();
int localPort = req.getLocalPort(); /*****获取其他对象******(重点)*/
// 获得session对象
HttpSession session = req.getSession();
// 获取请求Cookie
Cookie[] cookies = req.getCookies();
// 获得Servlet全局对象
ServletContext servletContext = req.getServletContext();
ServletContext servletContext1 = this.getServletContext();

Response的更多方法

/******response的更多方法*******/
// 设置contentType解决响应乱码
resp.setContentType("text/html;charset=utf-8");
// 设置响应编码
resp.setContentType("UTF-8"); // 设置文本长度(文件下载时用到)
resp.setContentLength(1024); // 添加响应头信息,重复的头信息不会覆盖
resp.addHeader("head1","value1");
resp.addHeader("head1","value1"); // 设置响应头信息,name一致会覆盖,这就是与addHeader的区别
resp.setHeader("head2","hah");
resp.setHeader("head2","hah2"); // 添加Cookie
resp.addCookie(new Cookie("cookie1","value1"));

小提示,如何在项目中访问到Servlet

其实每编写一个Servlet并使用web.xml配置好、或者使用注解的方式配置了一个Servlet时。

就相当于在Web根路径下放入了这个Servlet。



JAVAWEB - Servlet原理及其使用>从零开始学JAVA系列的更多相关文章

  1. JSP的执行原理、JSP的内置对象、四大作用域解析、MVC模式理解>从零开始学JAVA系列

    目录 JSP的执行原理.JSP的内置对象.四大作用域解析.MVC模式理解 JSP的执行原理 这里拿一个小例子来解析JSP是如何被访问到的 首先将该项目部署到tomcat,并且通过tomcat启动 通过 ...

  2. JAVAWEB过滤器、监听器的作用及使用>从零开始学JAVA系列

    目录 JAVAWEB过滤器.拦截器的作用及使用 过滤器Filter 什么是过滤器 为什么要使用过滤器(过滤器所能解决的问题) 配置一个过滤器完成编码的过滤 编写一个EncodingFilter(名称自 ...

  3. Session与Cookie的原理以及使用小案例>从零开始学JAVA系列

    目录 Session与Cookie的原理以及使用小案例 Cookie和Session所解决的问题 Session与Cookie的原理 Cookie的原理 Cookie的失效时机 小提示 Session ...

  4. JAVAWEB的基本入门(JSP、Tomcat)>从零开始学JAVA系列

    目录 JAVAWEB的基本入门(JSP.Tomcat) 使用idea创建web项目的两种方式 1.直接创建一个web项目(这样创建好的项目可以直接运行) 2.创建一个普通的java项目并配置web模块 ...

  5. spring框架的学习->从零开始学JAVA系列

    目录 Spring框架的学习 框架的概念 框架的使用 Spring框架的引入 概念 作用 内容 SpringIOC的学习 概念 作用 基本使用流程 SpringIOC创建对象的三种方式 通过构造器方式 ...

  6. 冒泡排序、选择排序、直接插入排序、快速排序、折半查找>从零开始学JAVA系列

    目录 冒泡排序.选择排序.直接插入排序 冒泡排序 选择排序 选择排序与冒泡排序的注意事项 小案例,使用选择排序完成对对象的排序 直接插入排序(插入排序) 快速排序(比较排序中效率最高的一种排序) 折半 ...

  7. JAVA数组的基础入门>从零开始学java系列

    目录 JAVA数组的基础入门 什么是数组,什么情况下使用数组 数组的创建方式 获取数组的数据 数组的内存模型 为什么数组查询修改快,而增删慢? 查询快的原因 增删慢的原因 数组的两种遍历方式以及区别 ...

  8. JAVA虚拟机的组成>从零开始学java系列

    目录 JAVA虚拟机的组成 什么是虚拟机? JAVA虚拟机的组成部分 堆区(堆内存) 方法区 虚拟机栈 本地方法栈 程序计数器 字符串常量池 JAVA虚拟机的组成 什么是虚拟机? 虚拟机是运行在隔离环 ...

  9. IDEA使用Tomcat时控制台乱码的解决方案>从零开始学JAVA系列

    IDEA使用Tomcat时控制台乱码的解决方案 解决方案1,修改启动时虚拟机参数 解决方案2,修改idea的设置 解决方案3,修改idea配置文件 在最后添加一行 '-Dfile.encoding=U ...

随机推荐

  1. VsCode中代码折叠快捷键

    ctrl+K  ctrl+[  折叠本级 ctrl+K  ctrl+]  取消折叠本级 ctrl+K  ctrl+0  折叠全部 ctrl+K  ctrl+J  取消折叠全部

  2. 【Spring Cloud & Alibaba 实战 | 总结篇】Spring Cloud Gateway + Spring Security OAuth2 + JWT 实现微服务统一认证授权和鉴权

    一. 前言 hi,大家好~ 好久没更文了,期间主要致力于项目的功能升级和问题修复中,经过一年时间的打磨,[有来]终于迎来v2.0版本,相较于v1.x版本主要完善了OAuth2认证授权.鉴权的逻辑,结合 ...

  3. js笔记20

    1.DOM零级事件元素绑定多个click,最后只执行最后一个click    DOM二级事件绑定多个click,都要执行 注意当绑定的多个事件名,函数名,事件发生阶段三者完全一样时,才执行最后一个 第 ...

  4. 33、jQuery介绍

    33.1.jQuery是什么: (1)jQuery由John Resig创建,至今已吸引了来自世界各地的众多 javascript 高手加入其team. (2)jQuery是继prototype之后又 ...

  5. 29、html介绍

    29.1.前端概述: 1.html.css.js之间的关系: html是页面布局,css是页面渲染,js是让页面动起来(让页面和用户进行交互): 2.浏览器访问web站点获取html页面图示: 3.h ...

  6. 精尽Spring Boot源码分析 - 日志系统

    该系列文章是笔者在学习 Spring Boot 过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring Boot 源码分析 GitHub 地址 进行阅读 Sprin ...

  7. Flask(1)- 简介

    背景 为啥要学,很久之前就学过点,没写文章 最近因为要写机器人工具,其实就是简单的纯服务端工具 反正 flask 也挺简单,一天快速过完 概念会直接搬教程的,实操自己敲一遍再总结 参考教程 https ...

  8. 暑假自学java第五天

    关于测试类的问题: 单独创建一个包存放测试类,如com.test 首先要构建路径添加测试类的相关类库,方法是项目右键,buld path->config buld path->librar ...

  9. shell 中的for循环

    第一类:数字性循环 #!/bin/bash for((i=1;i<=10;i++)); do echo $(expr $i \* 3 + 1); done #!/bin/bash for i i ...

  10. 「SPOJ 3105」Power Modulo Inverted

    「SPOJ 3105」Power Modulo Inverted 传送门 题目大意: 求关于 \(x\) 的方程 \[a^x \equiv b \;(\mathrm{mod}\; p) \] 的最小自 ...