servlet技术
一、什么是servlet
- Servlet是javaEE规范之一
- 规范就是接口
- servlet就是javaweb三大组件之一
- 三大组件分别是:
- Servlet程序
- Filter过滤器
- Listener监听器
- Servlet是运行在服务器上的java小程序
- 用来处理客户端发过来的请求并响应数据给客户端
二、Servlet程序
- 编写一个类实现Servlet接口
- 实现service方法,处理请求并响应数据
- 在web.xml中配置地址
1、Servlet的生命周期
-
执行Servlet构造器方法
-
执行init初始化方法
第一、二步:开启tomcat之后第一次访问,tomcat创建Servlet程序时会调用
-
执行service方法
第三步:开启tomcat后,每次访问都会调用
-
执行destroy销毁方法
第四步:在关闭tomcat时会被调用
2、请求的分发处理
-
首先我们呢需要在webapp文件夹下创建一个test.html(访问时只需要在域名后加上test.html即可访问,不需要再web.xml中注册)
我们主要是用这个网页来访问我们的程序,进而在我们的程序中读取到我们读取的方法是哪种的(GET还是POST)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>test</title> </head> <body> <form action="http://localhost:8080/servlet03/demo1" method="get"> <input type="submit"> </form> </body> </html>
-
了解创建为什么我们在后来的学习中继承了HttpServlet后只需要重写doGet方法和doPost方法
@Override public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { //ServletRequest类没有getMethod方法所以我们需要向下转型为HttpServletRequest类 HttpServletRequest req1 = (HttpServletRequest) req; String method = req1.getMethod(); if (method.equals("GET")){ doGet(); }else{ doPost(); } System.out.println("hello servlet 被访问了"); } public void doPost(){ System.out.println("post请求"); } public void doGet(){ System.out.println("Get请求"); }
可以对比HttpServlet的类的service方法
- 会发现HttpServlet中req调用getMethod不需要向下转型(因为该类传入的参数是HttpServletRequest req,而我们的类是ServletRequest req)
- 那么我们是否可以也可以把ServletRequest req跟改成HttpServletRequest req呢?不能,因为我们的类是实现了Servlet,所有方法必须重写,方法的参数类型也必须和Servlet中的ServletRequest req一致,而HttpServlet类是继承GenericServlet 类,没有硬性要求必须重写,所以可以更改)
3、用IDEA直接创建Servlet程序
- 在servlet程序中直接配置doget方法和dopost方法
- 在web.xml文件中配置servlet部分,但是不会配置servlet-mapping方法
4、继承HttpServlet类
- 我们直接去实现Servlet类的话,会很麻烦
- 实际开发中我们一般直接继承HttpServlet类
- 这样我们只需要重写doGet和doPost方法即可
public class demo3 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("post方法");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("get方法");
}
}
三、ServletConfig类的使用
ServletConfig类:Servlet程序的配置信息类
Servlet程序和ServletConfig对象都是由Tomcat负责创建,我们负责使用。
Servlet程序默认是第一次访问时的创建,而Servlet是每一个Servlet程序创建时就创建一个对应得ServletConfig对象
1、ServletConfig类的三大作用
-
可以获取Servlet程序的别名servlet-name
-
可以获取在web.xml配置的初始化参数init-param
<servlet> <servlet-name>demo3</servlet-name> <servlet-class>com.wang.servlet.demo3</servlet-class> <init-param> <param-name>url</param-name> <param-value>jdbc:mysql//localhost:8080/test</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>demo3</servlet-name> <url-pattern>/demo3</url-pattern> </servlet-mapping>
-
获取Servlet-Context对象
2、Servletconfig在哪使用
-
Servletconfig是每个Servlet程序都有一个独立存在的。(数据不能共同)
-
ServletConfig一般在Servlet程序的初始化方法中使用
-
在直接实现Servlet的Servlet程序中
@Override public void init(ServletConfig config) throws ServletException { System.out.println("初始化方法"); //可以获取Servlet程序的别名servlet-name System.out.println("别名"+config.getServletName()); // 可以获取在web.xml配置的初始化参数init-param System.out.println("数据库"+config.getInitParameter("url")); // 获取Servlet-Context对象 System.out.println("ServiceContext"+config.getServletContext()); }
-
在继承HttpServlet程序中
@Override public void init(ServletConfig config) throws ServletException { //注意这句一定要加,否则会空指针异常 //因为在generalServlet中的构造方法也是init方法(里面主要是有一个保存config的操作),因为我们现在重写了这个init方法,所以generalServlet类中的构造方法就访问不到了所以我们需要用一个super.init(config)来继续调用这个init方法(空指针异常其实就是因为这个config本来会保存,可以调用到,但现在调用不到了。) super.init(config); System.out.println("初始化方法"); System.out.println("rename"+config.getServletName()); }
四、ServletContext类
- ServletContext是一个接口,表示Servlet上下文对象
- 一个web工程,只有一个ServletContext对象
因为GeneralServlet也实现了ServletConfig接口,所以我们写的类中也有getServletContext方法,
五、HTTP协议
1、GET请求
两部分组成:
1、请求行
2、请求头
- 请求行:
- 请求的方式:GET
- 请求的资源路径:/06_servlet/a.html
- 一般这里的模板是:请求的资源路径[+?+请求参数]
- 请求的协议版本号:HTTP/1.1
- 请求头:
- key-value: 请求是一堆键值对,每一对都代表不同的含义
- Accept:告诉服务器,客户端可以接受的数据类型
- Accept-Language:告诉服务器客户端的可接受语言
- 中文:zh-cn
- 英文:en-us
- User-Agent:人的代理即浏览器的信息
- Accept-Encoding:告诉服务器,客户端可以接受的数据编码(压缩)格式
- Host:服务器ip和需要访问的端口号
- Connection:告诉服务器请求连接怎么处理
- Keep-Alive:告诉服务器回传数据之后一段时间不要关闭,保持一段时间的连接
- Closed:断开连接
2、POST请求
-
隐藏域:
-
<form action="http://localhost:8080/servlet03/demo3" method="post"> //这两个hidden就是隐藏域,我们会在请求体中看到这两个数据 <input type="hidden" name="action" value="login"> <input type="hidden" name="username" value="rooot"> <input type="submit"> </form>
-
-
先行了解:POST请求和GET请求区别主要是POST请求多了一个请求体(用来存放传递的数据)
3、哪些时GET请求哪些时POST请求
- 一般情况下都是GET请求
- from标签中type=“hidden”是一种POST请求
4、响应的HTTP协议介绍
5、常用的响应码
6、MIME数据类型说明
7、谷歌查看原始HTTP协议
-
响应体的数据谷歌会单独放在一个repose中
-
General部分是全局请求信息
六、HttpServletRequest类
1、HttpServletRequest常用API
- uri和url区别:
- 都可以准确的定位一个东西,但url是uri子集
- uri主要是用一定的规则,比如身份证号码定位一个人一样定位一个资源(可以是相对路径,因为在这个工程中这个资源的相对路径是唯一对应的)
- url主要偏向于用绝对路径定位一个人,url=协议+主机+端口号+uri
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取请求的资源路径
System.out.println("URI-->>"+req.getRequestURI());
//获取请求的统一资源定位符
System.out.println("URL-->>"+req.getRequestURL());
//获取客户端的ip地址,由于本机测试很有可能出现0:0:0:0:0:0:0:1这种IPV6的ip地址(正常)
System.out.println("客户端IP地址-->>"+req.getRemoteHost());
//获取请求头
System.out.println("请求头-->>"+req.getHeader("user-agent"));
//获取请求的参数----getParamter()方法
//获取请求的多个参数----getParamterValues()方法
//获取请求的方式
System.out.println("请求方式-->>"+req.getMethod());
}
//获取请求的参数,获取请求转发对象单独重点讲解
- 结果
URI-->>/servlet03/demo2
URL-->>http://localhost:8080/servlet03/demo2
客户端IP地址-->>0:0:0:0:0:0:0:1
请求头-->>Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.104 Safari/537.36
请求方式-->>GET
2、在get请求中获取请求参数
- html界面模拟提交数据
<form action="http://localhost:8080/servlet03/demo2" method="get">get请求<br/>
用户名:<input type="text" name="username"><br/>
密码:<input type="password" name="password"><br/>
兴趣爱好:<input type="checkbox" name="hobby" value="cpp">C++
<input type="checkbox" name="hobby" value="java">java
<input type="checkbox" name="hobby" value="js">JavaScript<br/>
<input type="submit">
</form>
- Servlet程序模拟接受数据
//获取请求的参数----getParamter()方法
//获取请求的多个参数----getParamterValues()方法
System.out.println("输入的用户名"+req.getParameter("username"));
System.out.println("输入的密码"+req.getParameter("password"));
//这里列表不能直接打印出来,我们需要用到工具类
System.out.println("选择的爱好"+ Arrays.asList(req.getParameterValues("hobby")));
- 结果
输入的用户名wang
输入的密码123456
选择的爱好[cpp, java, js]
3、在post请求中获取请求参数
-
在html中把form的method改成post即可(在HttpServlet中重写的Service方法会自动根据请求的方式调用不同的方法如doGet方法和doPost方法)
-
Servlet程序不需要改(暂时)
-
传输数据是中文时会报错
-
req.setCharacterEncoding("UTF-8"); //一般来说都是要统一设置字符集编码为UTF-8的
-
4、请求转发
- 服务器收到请求之后,从一个资源跳转到另一个资源的操作叫做请求转发(注意:url不会发生改变)
-
html部分
<form action="http://localhost:8080/servlet04/enter"> 用户名:<input type="text" name="username" placeholder="用户名"><br/> 密码:<input type="password" name="password" placeholder="密码"><br/> 兴趣爱好:<input type="checkbox" name="hobby" value="cpp">C++ <input type="checkbox" name="hobby" value="java">java <input type="checkbox" name="hobby" value="js">JavaScript<br/> <input type="submit"> </form>
-
enter部分
@Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //检查参数 String username = req.getParameter("username"); System.out.println("enter检查参数-->>"+username); //在检查了参数之后就传一个已查到传到域对象处,后面的Servlet就调用这个域对象的键就可以知道这个参数是否经过了Servlet req.setAttribute("inspect_status","inspected"); //开始转发 /* * 这里的/代表http://ip:port/工程名 */ RequestDispatcher Dispatcher = req.getRequestDispatcher("/login"); Dispatcher.forward(req,resp); }
-
login部分
@Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //检查参数 String username = req.getParameter("username"); System.out.println("login检查参数-->>"+username); //检查域对象 Object respect_status = req.getAttribute("inspect_status"); System.out.println("login检查respect_status-->>"+respect_status); }
-
请转发的特点
-
共享request域中的数据,这也是为什么我们使用ServletContext类,我们这里将数据键值对放到req域中,之后的转发中通过forword(req,resp)又把这个req当作参数传给了转发对象
-
一般web-inf下文件时不能直接访问的,但是通过转发时可以是实现的
RequestDispatcher requestDispatcher = req.getRequestDispatcher("/WEB-INF/hello.html"); requestDispatcher.forward(req,resp);
-
5、base标签(html、jsp)
<!DOCTYPE html>
<html lang="zh_CN">
<head>
<meta charset="UTF-8">
<title>注意项界面</title>
<!-- 一般来说base标签下我们只需要放置自己这个文件的访问目录即可-->
<!-- 后面的资源路径可以省去,只显示一个目录路径即可-->
<base href="http://localhost:8080/servlet04/a/b/">
</head>
<body>
a下的b下的c.html
<a href="../../index.html">首页</a>
</body>
</html>
- 注意:a标签下的index.html路径时根据当前url来跳转的
- url是http://localhost:8080/servlet04/a/b/c.html时,…/…/index.html相对路径会跳转到http://localhost:8080/servlet04/index.html(这个时html之间跳转)
- url是http://localhost:8080/servlet04/demo3时,…/…/index.html相对路径会跳转到http://localhost:8080/index.html(这个是Servlet程序转发,我们会发现这个跳转肯定会发生错误,所以我们就需要base标签)
- base标签就是为了设置一个默认的参考相对路径的url,这样html就不会根据当前url来判断跳转路径了
6、路径中斜杆的含义
- “/“ 被浏览器解析,得到的地址是:http://ip:port
- 一般存在于html,jsp中
- “/” 被服务器解析,得到的地址是:http://ip:port/工程路径
- 一般存在于Servlet程序中,其中的各种API中
- 特殊情况:response.sentRediect("/");把斜杠发送给浏览器解析,得到http://ip:port/ (重定向)
七、HttpServletResponse类
1、两个响应流
- outputStream();字节流
- getWrite();字符流
- 两个响应流只能用一个
2、解决响应中文乱码
注意我们这里的方法都需要在获取流之前完成
- 我们需要做的:
- 把服务器输出格式调为UTF-8格式:resp.setCharsetEncoding(“UTF-8”)
- 把浏览器接受格式调为UTF-8格式:resp.setHeader(“Content-type”,“text/html;Charset=UTF-8”)
- 另一种方法:
- resp.setContentType(“text/html;charset=UTF-8”)
- 可以直接达到这两种代码的共同作用
3、请求重定向
-
两个步骤:
- 设置状态码为302:resp.setStatus(302)
- 设置重定向地址:resp.setHeader(“Location”,"/servlet04/demo6")
- 注意这里的重定向地址的斜杠:这里是直接把这个相对路径直接发送给浏览器,由浏览器来解析所以斜杠的意思是http://ip:port/,所以我们还需要加上工程目录
-
第二种方法:
- resp.sendRedirect("/servlet04/demo6")
- 这个方法可以直接一步到位
-
重定向特点
)
- 把浏览器接受格式调为UTF-8格式:resp.setHeader(“Content-type”,“text/html;Charset=UTF-8”)
- 另一种方法:
- resp.setContentType(“text/html;charset=UTF-8”)
- 可以直接达到这两种代码的共同作用
3、请求重定向
[外链图片转存中…(img-HwjzyycY-1615108468040)]
-
两个步骤:
- 设置状态码为302:resp.setStatus(302)
- 设置重定向地址:resp.setHeader(“Location”,"/servlet04/demo6")
- 注意这里的重定向地址的斜杠:这里是直接把这个相对路径直接发送给浏览器,由浏览器来解析所以斜杠的意思是http://ip:port/,所以我们还需要加上工程目录
-
第二种方法:
- resp.sendRedirect("/servlet04/demo6")
- 这个方法可以直接一步到位
-
重定向特点