1. Listener不熟悉
2. Servlet
Servlet: Server Applet,翻译为运行在服务端的Java小程序,是sun公司提供一套规范( 接口 ),用来定义我们的代码怎么写才能被tomcat识别。
本质:接口,一个类想要被tomcat正确识别,那么这个类就必须直接或间接的实现Servlet接口。
任务:接收请求,处理请求,返回响应。
作为Java程序员,我们可能已经习惯了使用IDE和Web框架进行开发,IDE帮我们做了编译、打包的工作,而Spring框架在背后帮我们实现了Servlet接口,并把Servlet注册到了Web容器,这样我们可能很少有机会接触到一些底层本质的东西,比如怎么开发一个Servlet?如何编译Servlet?如何在Web容器中跑起来?
今天我们就抛弃IDE、拒绝框架,自己纯手工编写一个Servlet,并在Tomcat中运行起来。一方面进一步加深对Servlet的理解;另一方面,还可以熟悉一下Tomcat的基本功能使用。
主要的步骤有:
1.下载并安装Tomcat。
2.编写一个继承HttpServlet的Java类。
3.将Java类文件编译成Class文件。
4.建立Web应用的目录结构,并配置 web.xml
。
5.部署Web应用。
6.启动Tomcat。
7.浏览器访问验证结果。
8.查看Tomcat日志。
下面你可以跟我一起一步步操作来完成整个过程。Servlet 3.0规范支持用注解的方式来部署Servlet,不需要在 web.xml
里配置,最后我会演示怎么用注解的方式来部署Servlet。
1. 下载并安装Tomcat
最新版本的Tomcat可以直接在 官网 上下载,根据你的操作系统下载相应的版本,下载完成后直接解压,解压后的目录结构如下。
2. 编写一个继承HttpServlet的Java类
javax.servlet
包提供了实现Servlet接口的GenericServlet抽象类。这是一个比较方便的类,可以通过扩展它来创建Servlet。但是大多数的Servlet都在HTTP环境中处理请求,因此Servlet规范还提供了HttpServlet来扩展GenericServlet并且加入了HTTP特性。我们通过继承HttpServlet类来实现自己的Servlet只需要重写两个方法:doGet和doPost。
因此今天我们创建一个Java类去继承HttpServlet类,并重写doGet和doPost方法。首先新建一个名为 MyServlet.java
的文件,敲入下面这些代码:
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("MyServlet 在处理get()请求...");
PrintWriter out = response.getWriter();
response.setContentType("text/html;charset=utf-8");
out.println("<strong>My Servlet!</strong><br>");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("MyServlet 在处理post()请求...");
PrintWriter out = response.getWriter();
response.setContentType("text/html;charset=utf-8");
out.println("<strong>My Servlet!</strong><br>");
}
}
这个Servlet完成的功能很简单,分别在doGet和doPost方法体里返回一段简单的HTML。
3. 将Java文件编译成Class文件
下一步我们需要把 MyServlet.java
文件编译成Class文件。你需要先安装JDK,这里我使用的是JDK 11。接着你需要把Tomcat lib目录下的 servlet-api.jar
拷贝到当前目录下,这是因为 servlet-api.jar
中定义了Servlet接口,而我们的Servlet类实现了Servlet接口,因此编译Servlet类需要这个JAR包。接着我们执行编译命令:
javac -cp ./servlet-api.jar MyServlet.java
编译成功后,你会在当前目录下找到一个叫 MyServlet.class
的文件。
4. 建立Web应用的目录结构
我们在上一期学到,Servlet是放到Web应用部署到Tomcat的,而Web应用具有一定的目录结构,所有我们按照要求建立Web应用文件夹,名字叫MyWebApp,然后在这个目录下建立子文件夹,像下面这样:
MyWebApp/WEB-INF/web.xml
MyWebApp/WEB-INF/classes/MyServlet.class
然后在 web.xml
中配置Servlet,内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0"
metadata-complete="true">
<description> Servlet Example. </description>
<display-name> MyServlet Example </display-name>
<request-character-encoding>UTF-8</request-character-encoding>
<servlet>
<servlet-name>myServlet</servlet-name>
<servlet-class>MyServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>myServlet</servlet-name>
<url-pattern>/myservlet</url-pattern>
</servlet-mapping>
</web-app>
你可以看到在 web.xml
配置了Servlet的名字和具体的类,以及这个Servlet对应的URL路径。请你注意, servlet和servlet-mapping这两个标签里的servlet-name要保持一致。
5. 部署Web应用
Tomcat应用的部署非常简单,将这个目录MyWebApp拷贝到Tomcat的安装目录下的webapps目录即可。
6. 启动Tomcat
找到Tomcat安装目录下的bin目录,根据操作系统的不同,执行相应的启动脚本。如果是Windows系统,执行 startup.bat
.;如果是Linux系统,则执行 startup.sh
。
7. 浏览访问验证结果
在浏览器里访问这个URL: http://localhost:8080/MyWebApp/myservlet
,你会看到:
My Servlet!
这里需要注意,访问URL路径中的MyWebApp是Web应用的名字, myservlet
是在 web.xml
里配置的Servlet的路径。
8. 查看Tomcat日志
打开Tomcat的日志目录,也就是Tomcat安装目录下的logs目录。Tomcat的日志信息分为两类 :一是运行日志,它主要记录运行过程中的一些信息,尤其是一些异常错误日志信息 ;二是访问日志,它记录访问的时间、IP地址、访问的路径等相关信息。
这里简要介绍各个文件的含义。
catalina.***.log
主要是记录Tomcat启动过程的信息,在这个文件可以看到启动的JVM参数以及操作系统等日志信息。
catalina.out
catalina.out
是Tomcat的标准输出(stdout)和标准错误(stderr),这是在Tomcat的启动脚本里指定的,如果没有修改的话stdout和stderr会重定向到这里。所以在这个文件里可以看到我们在 MyServlet.java
程序里打印出来的信息:
MyServlet在处理get请求…
localhost.**.log
主要记录Web应用在初始化过程中遇到的未处理的异常,会被Tomcat捕获而输出这个日志文件。
localhost_access_log.**.txt
存放访问Tomcat的请求日志,包括IP地址以及请求的路径、时间、请求协议以及状态码等信息。
manager.***.log/host-manager.***.log
存放Tomcat自带的Manager项目的日志信息。
用注解的方式部署Servlet
为了演示用注解的方式来部署Servlet,我们首先修改Java代码,给Servlet类加上 @WebServlet 注解,修改后的代码如下。
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/myAnnotationServlet")
public class AnnotationServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("AnnotationServlet 在处理get请求...");
PrintWriter out = response.getWriter();
response.setContentType("text/html; charset=utf-8");
out.println("<strong>Annotation Servlet!</strong><br>");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("AnnotationServlet 在处理post请求...");
PrintWriter out = response.getWriter();
response.setContentType("text/html; charset=utf-8");
out.println("<strong>Annotation Servlet!</strong><br>");
}
}
这段代码里最关键的就是这个注解,它表明两层意思:第一层意思是AnnotationServlet这个Java类是一个Servlet,第二层意思是这个Servlet对应的URL路径是myAnnotationServlet。
@WebServlet("/myAnnotationServlet")
创建好Java类以后,同样经过编译,并放到MyWebApp的class目录下。这里要注意的是,你 需要删除原来的web.xml,因为我们不需要 web.xml
来配置Servlet了。然后重启Tomcat,接下来我们验证一下这个新的AnnotationServlet有没有部署成功。在浏览器里输入: http://localhost:8080/MyWebApp/myAnnotationServlet
,得到结果:
Annotation Servlet!
这说明我们的AnnotationServlet部署成功了。可以通过注解完成 web.xml
所有的配置功能,包括Servlet初始化参数以及配置Filter和Listener等。
3. Fliter
介绍
当用户访问服务器资源时,过滤器将请求拦截下来,完成一些通用的功能,比如:登录校验、统一编码处理、敏感字符处理等
执行流程
1. 客户端向服务器发起访问资源的请求
2. Filter将请求拦截住,开始处理访问资源之前的逻辑
3. Filter决定是否要放行访问请求,如果放行,请求继续向后运行
4. 请求访问到相关资源,然后服务器给出响应
5. Filter将响应拦截住,开始处理访问资源之后的逻辑
6. 服务器将响应返回给浏览器
过滤器实现访问校验
下面使用过滤器是请求的访问校验,在实现之前先考虑两个问题
-
所有的请求,拦截到了之后,都需要校验令牌吗?
-
拦截到请求后,在满足什么条件下才可以放行?
创建Filter
创建
com.itheima.filter.LoginCheckFilter
编写过滤逻辑
package com.itheima.filter;
import com.google.gson.Gson;
import com.itheima.util.JwtUtils;
import com.itheima.vo.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebFilter("/*")
@Slf4j
public class LoginCheckFilter implements Filter {
@Autowired
private Gson gson;
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//1. 将请求和响应强制转换为HTTP的
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
//2. 获取请求uri
String uri = request.getRequestURI();
log.info("请求路径{}", uri);
//3. 判断请求uri是否为/login,如果包含,说明是登录操作,放行
if (uri.equals("/login")) {
filterChain.doFilter(servletRequest, servletResponse);//放行请求
return;//结束判断
}
//4. 获取请求头中的令牌(token)解析,如果解析失败,返回错误结果
String token = request.getHeader("token");
try {
JwtUtils.parseJWT(token);
}catch (Exception e){
log.info("token错误");
//返回错误消息
String json = gson.toJson(Result.error("NOT_LOGIN"));
response.setContentType("application/json;charset=utf-8");
response.getWriter().write(json);
return;//结束判断
}
//5.放行
filterChain.doFilter(request, response);
}
}
开启Filter支持
在启动类上添加
@ServletComponentScan
注解
4. 拦截器
拦截器
拦截器是Spring提供的一种技术,它的功能似于过滤器,它会在进入controller之前,离开controller之后以及响应离开服务时进行拦截。
拦截器实现访问校验
1.创建Interceptor
创建
com.itheima.interceptor.LoginCheckInterceptor
编写过滤逻辑
package com.itheima.interceptor;
import com.google.gson.Gson;
import com.itheima.util.JwtUtils;
import com.itheima.vo.Result;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
@Slf4j
public class LoginCheckInterceptor implements HandlerInterceptor {
@Autowired
private Gson gson;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//1. 获取请求头中的令牌(token)
String token = request.getHeader("token");
//2. 解析token,如果解析失败,返回错误结果(未登录)。
try {
Claims claims = JwtUtils.parseJWT(token);
}catch (Exception e){
log.info("token错误");
//返回错误消息
String json = gson.toJson(Result.error("NOT_LOGIN"));
response.setContentType("application/json;charset=utf-8");
response.getWriter().write(json);
return false;//结束判断
}
//3. 放行
return true;
}
}
2.配置Interceptor
5.过滤器VS拦截器
过滤器和拦截器实现的功能基本相似,不同点在于:
技术范围不同:过滤器需要JavaWeb技术,而拦截器属于Spring提供的技术
拦截范围不同:过滤器会拦截所有的资源,而拦截器只会拦截Spring环境中的资源
如果项目中同时出现了过滤器和拦截器,它们的执行位置如下