三、Servlet程序
Sevlet技术
1、介绍
- Servlet是JavaEE规范之一,是一个接口
- Servlet是JavaWeb三大组件之一,分别是Servlet程序、Filter过滤器、Listener监听器
- Servlet是运行在服务器上的一个java小程序,可以接受客户端发送过来的请求,并且响应数据
2、手动实现Servlet程序
- 编写一个实现Servlet接口的类
- 实现service方法:处理请求并响应数据
- 在web.xml中配置servlet程序的访问地址
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>com.atguigu.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
-
servlet
- servlet-name:servlet的程序名,一般用类名
- servlet-class:servlet的全类名
-
servlet-mapping:给servlet程序配置访问地址
-
servlet-name:该地址匹配的servlet程序名
-
url-pattern:配置访问地址
/hello 表示地址为htpp://ip:port/工程路径/hello
-
注:访问地址必须加“ / ”、servlet-name必须对应
3、url到servlet程序的访问顺序
ip—端口号—程序名—资源名/hello—xml—匹配到url /hello—匹配的servlet程序HelloServlet—匹配到类com.atguigu.servlet.HelloServlet
4、Servlet生命周期
-
执行Servlet构造器
-
执行init初始化方法
第1、2步,在第一次访问的时候创建Servle程序时调用
-
执行service方法
第3步,每次访问都会调用
-
执行destroy销毁方法
第4步在web工程停止时调用
5、Get和Post处理
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("3. HelloServlet被访问了");
// 类型转换,因为它有getMethod()方法
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
// 获取请求方式
String method = httpServletRequest.getMethod();
if("POST".equals(method)){
doPost();
}else if("GET".equals(method)){
doGet();
}
}
public void doGet(){
System.out.println("GET请求");
}
public void doPost(){
System.out.println("POST请求");
}
6、HttpServlet
在实际开发中,一般使用继承HttpServlet类的方式实现Servlet程序
- 编写一个继承HttpServlet的类
- 根据业务需要重写**doGet()或doPost()**方法
- 到web.xml中配置Servlet程序的访问地址
在Idea中新建Servlet文件会自动创建继承HttpServlet的类
7、Servlet继承体系
ServletConfig类
ServletConfig是Servlet程序的配置信息类
作用:
- 可以获取Servlet程序的别名 servlet-name 的值
- 获取初始化参数 init-param
- 获取ServletContext对象
Servlet程序和ServletConfig对象都是由Tomcat负责创建
- Servlet程序默认是第一次访问时创建
- ServletConfig是每个Servlet程序创建时,就创建一个对应的ServletConfig对象
在xml的servlet标签中可以设置初始化参数init-param
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>com.atguigu.servlet.HelloServlet</servlet-class>
<!-- init-param是初始化参数,name是参数名,value是参数值-->
<init-param>
<param-name>url</param-name>
<param-value>jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8</param-value>
</init-param>
<init-param>
<param-name>username</param-name>
<param-value>root</param-value>
</init-param>
<init-param>
<param-name>password</param-name>
<param-value>123456</param-value>
</init-param>
</servlet>
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("2. init()");
System.out.println(servletConfig.getServletName());
System.out.println(servletConfig.getInitParameter("url"));
System.out.println(servletConfig.getInitParameter("username"));
System.out.println(servletConfig.getInitParameter("password"));
System.out.println(servletConfig.getServletContext());
}
ServletContext类
-
ServletContext是一个接口,表示Servlet上下文对象
-
一个web工程只有一个ServletContext对象实例
-
ServletContext对象是一个域对象(可以存取数据),这里的域是指存取数据的范围,是整个web工程
-
ServletContext对象在web工程启动时创建,停止时销毁
-
可以在Servlet程序中调用getServletConfig().getServletContext();获取,也可以直接调用getServletContext()获取
存数据 | 取数据 | 删除数据 | |
---|---|---|---|
Map | put( ) | get( ) | remove( ) |
域对象 | setAttribute( ) | getAttribute( ) | removeAttribute( ) |
作用:
-
获取web.xml中配置的上下文参数 context-param(属于整个web工程)
-
获取当前工程路径,格式: /工程路径
-
获取工程打包部署后在服务器硬盘上的绝对路径
-
像Map一样存取数据
-
获取web.xml中配置的上下文参数 context-param(属于整个web工程)
<context-param>
<param-name>username</param-name>
<param-value>root</param-value>
</context-param>
<context-param>
<param-name>password</param-name>
<param-value>123456</param-value>
</context-param>
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletContext servletContext = getServletConfig().getServletContext();
String username = servletContext.getInitParameter("username");
System.out.println("context-param参数username的值是:"+username);
String password = servletContext.getInitParameter("password");
System.out.println("context-param参数password的值是:"+password);
}
- 获取当前工程路径,格式: /工程路径
System.out.println("当前工程路径:"+servletContext.getContextPath());
-
获取工程打包部署后在服务器硬盘上的绝对路径
“ / ”表示打包后将工程的web目录解析为根目录,可以获取web目录下的任意绝对路径
System.out.println("当前工程绝对路径:"+servletContext.getRealPath("/"));
-
像Map一样存取数据
servletContext.setAttribute("test",123); servletContext.getContext("test");
HTTP协议
客户端和服务器通信发送数据时需要遵守的规则
GET请求
-
请求行
- 请求的方式 GET
- 请求的资源路径(+ ?+请求参数)
- 请求的版本协议号 HTTP/1.1
-
请求头
key:value 组成
GET /05_servlet/a.html HTTP/1.1
Host: localhost:8080
Connection: keep-alive
sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="98", "Google Chrome";v="98"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.82 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: JSESSIONID=3765909C328781ED1C9F3041383D21B1; Idea-57bd47a4=5b0d8787-d44e-4ebb-b51b-eaf23a520394
If-None-Match: W/"359-1644928208251"
If-Modified-Since: Tue, 15 Feb 2022 12:30:08 GMT
POST请求
-
请求行
- 请求的方式 POST
- 请求的资源路径(+ ?+请求参数)
- 请求的版本协议号 HTTP/1.1
-
请求头
key:value
空行
-
请求体——发送给服务器的数据
POST /05_servlet/hello3 HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Content-Length: 34
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://localhost:8080
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.82 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://localhost:8080/05_servlet/a.html
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: JSESSIONID=2FE995CA836DE7D806AB96F426096D81; Idea-57bd47a4=5b0d8787-d44e-4ebb-b51b-eaf23a520394
常用请求头
Accept:表示客户端可以接收的数据类型
Accept-Language:表示客户端可以接收的语言类型
User-Agent:表示客户端浏览器的信息
Host:表示请求时的服务器ip和端口号
常见请求
GET:
- form表单 method=get
- a标签
- link标签引入css
- script标签引入js
- img标签引入图片
- iframe引入html页面
- 浏览器地址栏输入地址访问
POST:
- form表单 method=post
HTTP响应报文格式
-
响应行
- 响应的协议/版本号
- 响应状态码
- 响应状态描述符
-
响应头
key:value
空行
-
响应体——服务器回传给客户端的数据
HTTP/1.1 200
Content-Length: 0
Date: Tue, 15 Feb 2022 15:02:37 GMT
Keep-Alive: timeout=20
Connection: keep-alive
常见状态码:
**200 OK:**表示从客户端发送给服务器的请求被正常处理并返回;
**301 Moved Permanently:**永久性重定向,表示请求的资源被分配了新的URL,之后应使用更改的URL;
**302 Found:**临时性重定向,表示请求的资源被分配了新的URL,希望本次访问使用新的URL;
**403 Forbidden:**服务器拒绝该次访问(访问权限出现问题)
**404 Not Found:**表示服务器上无法找到请求的资源,除此之外,也可以在服务器拒绝请求但不想给拒绝原因时使用;
**500 Inter Server Error:**表示服务器在执行请求时发生了错误,也有可能是web应用存在的bug或某些临时的错误时;
HttpServletRequest类
每当有请求到达Tomcat服务器,Tomcat服务器就会把请求过来的HTTP协议信息解析并封装到Request对象中,然后传递到service方法(doGet和doPost)中,我们可以通过HttpServletRequest对象,获取到请求的所有信息
常用方法
- getRequestURI():获取请求的资源路径
- getRequestURL():获取请求的统一资源定位符(绝对路径)
- getRemoteHost():获取客户端ip
- getHeader():获取请求头
- getParameter():获取请求的参数
- getParameterValues():获取请求的参数(多个值,比如checkbox)
- getMethod():获取请求的方式GET或POST
- setAttribute(key,value):设置域数据
- getAttribute(key):获取域数据
- getRequestDispatcher():获取请求转发对象
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("URI="+request.getRequestURI());
// URI=/06_servlet/api
System.out.println("URL="+request.getRequestURL());
// URL=http://localhost:8080/06_servlet/api
System.out.println("客户端ip地址="+request.getRemoteHost());
System.out.println("请求头的User-Agent="+request.getHeader("User-Agent"));
// 请求头的User-Agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.82 Safari/537.36
}
请求中文乱码解决
设置请求体的字符集为UTF-8,避免post请求的中文乱码问题
request.setCharacterEncoding("UTF-8");
注:在获取请求参数之前调用才有效
请求转发
服务器收到请求后,从一个资源跳到另一个资源的操作成为请求转发
请求转发不可以访问工程以外的资源,因为会将路径名开头默认解析为" /工程名 " + …
req.getRequestDispatcher("/xxx").forward(req,resp);
Base标签
在html的head标签中可以设置base标签
base标签可以设置当前页面中所有相对路径工作时,参照哪个路径来进行跳转
HttpServletResponse类
与HttpServletRequest一样,每当有请求到达,Tomcat服务器都会创建一个Response对象传递给Servlet程序去使用,HttpServletRequest表示请求信息,HttpServletResponse表示响应信息,如果需要设置给客户端响应的信息,可以通过HttpServletResponse来设置
两个输出流
字节流:getOutputStream(),常用于下载文件(传递二进制数据)
字符流:getWriter(),常用于回传字符串(常用)
二者同时只能使用一个
给客户端响应字符串
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
PrintWriter writer = response.getWriter();
writer.write("response's content");
}
响应中文乱码解决
方式一(不推荐)
设置服务器字符集为UTF-8
response.setCharacterEncoding("UTF-8");
通过响应头,设置浏览器也使用UTF-8字符集
response.setHeader("Content-Type","text/html;charset=UTF-8");
方式二(推荐)
同时设置服务器和客户端都使用UTF-8字符集,还设置了响应头
response.setContentType("text/html;charset=UTF-8");
请求重定向
方式一
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("访问到了Response1");
// 设置响应状态码302,表示重定向
response.setStatus(302);
// 设置响应头,说明新的地址位置
response.setHeader("Location","http://localhost:8080/06_servlet/response2");
}
方式二(推荐)
response.sendRedirect("http://localhost:8080/06_servlet/response2");
四、JSP
jsp是java server pages,Java的服务器页面,代替Servlet程序回传html页面的数据
jsp页面本质上是一个Servlet程序,当第一次访问jsp页面时,Tomcat服务器会把jsp页面翻译成为一个java源文件,并对他编译为.class字节码程序
jsp翻译出来的java类,继承了HttpServlet类,即为一个Servlet程序
jsp头部的page指令
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
jsp的page指令可以修改jsp页面中一些重要的属性,或者行为
-
language:表示jsp翻译后是什么语言文件,暂时只支持java
-
contentType:表示jsp返回的数据类型,也就是源码中response.setContentType( )的参数值
-
pageEncoding:表示jsp页面文件本身的字符集
-
import:导包、导类
以下两个属性是给out输出流使用
-
autoFlush:设置当out输出流缓冲区满了之后,是否自动自动刷新缓冲区,默认值是true
-
buffer:设置out缓冲区的大小,默认是8kb
-
errorPage:当jsp页面运行出错时,自动跳转到的页面
-
isErrorPage:设置当前jsp页面是否是错误信息页面,默认是false
-
session:设置访问当前jsp页面,是否会创建HTTPSession对象,默认是true
-
extends:设置jsp翻译出来的java类默认继承哪个类,
jsp常用脚本
1、声明脚本(极少使用)
格式: <%! java代码 %>
作用:可以给jsp翻译出来的java类定义属性和方法,还可以定义静态代码块和内部类
<%--1、声明类的属性--%>
<%! private int id;
private String name;
private static Map<String,Object> map;
%>
<%-- 2、声明静态代码块 --%>
<%!
static {
map = new HashMap<>();
map.put("key1","value1");
map.put("key2","value2");
}
%>
<%-- 3、声明类的方法--%>
<%!
public int abc(){
return 12;
}
%>
<%-- 4、声明内部类 --%>
<%!
public static class A{
private int id = 12;
private String abc = "abc";
}
%>
2、表达式脚本
格式: <%=表达式%>
作用:在jsp页面上输出数据
特点:
-
表达式脚本会被翻译到_jspService方法中,
-
会被翻译到out.print()输出到页面上
-
_jspService方法中的对象都可以直接使用
-
表达式不能以分号结束
<%=12%>
<%=12.12%>
<%="我是字符串"%>
<%=map%>
3、代码脚本
格式: <% java语句 %>
作用:在jsp页面中,用java语句编写需要的功能
特点:
- 代码脚本翻译之后都在_jspService()方法中
- 在_jspService()方法中的现有对象都可以直接使用,如request、response
- 多个代码脚本块可以组合拼接为一个完整的java语句
- 代码脚本可以和表达式脚本一起组合使用,在jsp页面上输出数据
jsp九大内置对象
在Tomcat翻译jsp页面为Servlet源代码后,内部提供的九大内置对象
- request ——请求对象
- response——响应对象
- pageContext——jsp上下文对象
- session——会话对象
- application——ServletContext对象
- config ——ServletConfig对象
- out——jsp输出流对象
- page——指向当前jsp的对象
- exception——异常对象
jsp四大域对象
九大内置对象中包括四个域对象
域对象是可以像Map一样存取数据的对象
- pageContext——(PageContextImpl类),在当前jsp页面范围内有效
- request——(HttpServletRequest类),在一次请求内有效
- session——(HttpSession类),在一次会话范围内有效(打开浏览器访问服务器直到关闭浏览器)
- application——(ServletContext类),整个web工程范围内都有效(只要web工程不停止,数据都在)
四个域对象功能一样,而存取数据的范围不同
存:setAttribute(key, value)
取:getAttribute(key)
使用的优先顺序是数据范围从小到大的顺序
pageContext——request——session——application
out和response区别
out.write(123);
response.getWriter().write(123);
当jsp页面中所有代码执行完毕后会做以下两个操作:
- 执行out.flush()操作,把out缓冲区中的数据追加写入到response缓冲区末尾
- 执行response的刷新操作,把全部数据写给客户端
由于jsp翻译之后,底层源代码都是使用out进行输出,所以在jsp中通常用out来输出,以免打乱页面输出内容的顺序
out.write()输出字符串
out.print()可以输出任意类型数据,(都转换为字符串后调用write输出)
结论:可以统一使用out.print()进行输出
常用标签
1、静态包含
-
格式
<%@ include file = " " %>
file属性指定要包含的jsp页面路径,地址中第一个斜杠 / 表示为http://ip:port/工程路径/ 映射到代码的web目录
-
特点
静态包含不会翻译被包含的jsp页面
其实是把被包含的jsp页面的代码拷贝到包含的位置执行输出
2、动态包含
-
格式
<jsp:include page = " "> </jsp:include >
page属性指定要包含的jsp页面路径
-
特点
-
动态包含会把被包含的jsp页面也翻译为java代码
-
底层代码使用如下代码去调用被包含的jsp页面执行输出
JspRuntimeLibrary.include(request, response, "/include/foot.jsp", out, false);
-
动态包含还可以传递参数
<jsp:include page="/include/foot.jsp"> <jsp:param name="name" value="root"/> </jsp:include>
-
3、请求转发
-
格式
<jsp:forward page=""></jsp:forward>
page属性指定请求转发的路径
练习
输出九九乘法表
<table style="border: 1px solid;margin: 0 auto;">
<%for (int i = 1; i <= 9; i++) {%>
<tr>
<%for (int j = 1; j <= i; j++) {%>
<td><%out.print(j+"*"+i+"="+j*i+"\t");%>
</td>
<%}%>
</tr>
<%}%>
</table>
请求转发练习
get请求Servlet,Servlet查询数据库并将结果集保存到request域,请求转发给jsp页面显示
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取请求的参数
// 发sql语句查询学生信息,用for循环模拟
List<Student> studentList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
int t = i+1;
studentList.add(new Student(t, "name"+t, 18+t, "phone"+t));
}
// 保存查询结果到request域
req.setAttribute("stuList",studentList);
// 请求转发到showStudent.jsp页面
req.getRequestDispatcher("/showStudent.jsp").forward(req,resp);
}
showStudent.jsp
<table>
<tr>
<th>编号</th>
<th>姓名</th>
<th>年龄</th>
<th>电话</th>
<th>操作</th>
</tr>
<%for(Student stu : studentList){%>
<tr>
<td><%=stu.getId()%></td>
<td><%=stu.getName()%></td>
<td><%=stu.getAge()%></td>
<td><%=stu.getPhone()%></td>
<td>修改/删除</td>
</tr>
<%}%>
</table>
五、Listener监听器
-
Listener监听器是JavaWeb的三大组件之一(Servlet程序、Filter过滤器、Listener监听器)
-
Listener是JavaEE的规范,就是接口
-
作用:监听某种事物的变化,通过回调函数,返回给客户(程序)去执行相应的处理
ServletContextListener监听器
-
ServletContextListener可以监听ServletContext对象的创建和销毁
-
ServletContext对象在web工程启动时创建,在web工程停止时销毁
在监听到创建和销毁后会分别调用相应方法进行反馈
两个回调方法分别为:
public interface ServletContextListener extends EventListener {
//在ServletContext对象创建之后马上调用,做初始化
default void contextInitialized(ServletContextEvent sce) {
}
//在ServletContext对象销毁之后调用
default void contextDestroyed(ServletContextEvent sce) {
}
}
使用监听器监听ServletContext对象的方法步骤:
- 编写一个类,实现ServletContextListener
- 实现两个回调方法
- 在web.xml中配置监听器
1、2
public class MyServletContextListenerImpl implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("ServletContext对象被创建了!");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("ServletContext对象被销毁了!");
}
}
3、web.xml下添加配置
<!-- 配置监听器 -->
<listener>
<listener-class>com.atguigu.listener.MyServletContextListenerImpl</listener-class>
</listener>