目录
Tomcat是HTTP的服务器,用于接收HTTP 请求,分析请求得出响应,然后将响应返回给浏览器
/webapps目录内,存放Tomcat服务器内的所有web程序,其内的Root目录是默认的web应用程序,默认访问应用程序使用的URL为http://127.0.0.1:8080
配置Tomcat的服务端口
默认端口是8080,可以进行修改
启动Tomcat
双击bin目录下的startup
出现这个表示成功启动,乱码原因:Tomcat使用UTF-8编码,而windows使用GBK
浏览器输入http://127.0.0.1:8080/,就可以看到Tomcat的欢迎界面
静态文档指的是在用户浏览的过程中,web呈现的页面不会有任何的改变,是固定死的;而动态文档是可以动态生成的
在Root目录下,创建一个.html的文件
浏览器输入http://127.0.0.1:8080/hello.html,就可以在浏览器查看这个文件
理论上其他用户就可以访问这个网址,看到这个界面;但是当前我们自己的IP是私有IP地址,不可以在网络上传播,需要借助外网IP
更多情况下,我们的网站内容是可变的,需要实现动态页面
这就要使用到我们Tomcat提供的API,也即是servlet
Maven 是一个项目管理工具,可以对 Java 项目进行自动化的构建和依赖管理;我们的代码要依赖很多第三方库,Maven可以帮助我们自动管理依赖
使用idea创建maven项目
例如配置mysql的相关依赖,打开https://mvnrepository.com/,maven中央仓库,选择mysql的版本,复制maven内容
拷贝maven的内容到pom.xml中
如果没有自动从中央仓库下载依赖,可以手动刷新,启动下载
Tomcat 作为服务器,负责接收客户请求,并做出响应
Tomcat把接收到的请求传送给Servlet,并将 Servlet 的响应传送回给客户;而Servlet是一种运行在支持Java语言的服务器上的组件
创建Servlet的流程
1、创建maven程序
2、引入servlet的依赖
Apache Tomcat® - Which Version Do I Want?
根据Tomcat的版本,从maven中央仓库选择对应servlet的版本,拷贝maven内容到代码中,idea从中央仓库下载依赖
3、 创建目录结构
我们需要在当前的目录结构上新增些结构
web.xml中拷贝以下内容
- web-app PUBLIC
- "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
- "http://java.sun.com/dtd/web-app_2_3.dtd">
-
- <web-app>
- <display-name> Archetype Created Web Applicationdisplay-name>
- web-app>
4、编写servlet代码
- @WebServlet("/hi")//当url路径包含hi目录时,才会触发这个类
- public class Hello extends HttpServlet {
- //继承HttpServlet类,这个类包含的方法之一就是doGet(),表示服务器在接收到Get请求之后,执行的操作
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- //HttpServletRequest:请求类 HttpServletResponse:响应类
- //Tomcat服务器的控制台的内容
- System.out.println("服务器hello");
- //2、当服务器接收到请求之后,做出的响应
- resp.getWriter().write("hello");
- }
- }
5、打包,将这个maven程序打包,部署到Tomcat服务器上
在pom.xml中做如下修改
- <packaging>warpackaging>
- <build>
- <finalName>
- hellotest
- finalName>
- build>
打包支持war包和jar包,war包可以说是服务于Tomcat的,不仅包含.class文件,还包含css,js和其他配置文件;而jar包 包含.class文件
双击maven操作界面的packet,进行打包
6、将包部署到Tomcat服务器上
将war包拷贝到Tomcat服务器上的webapps目录里
启动Tomcat服务器
7、检验是否成功
URL包含两级路径,输入http://127.0.0.1:8080/hellotest/hi
页面呈现
这里的第一级路径(context path)是"/hellotest",就是打包时,pom.xml中的名称
第二级目录(Servlet path)是"/hi",就是java中的@WebServlet("/hi")语句指定的路径,这个语句将URL中的/hi目录和java类关联,只要发送的请求包含/hi目录,才会被这个类接收请求,从而发送响应
idea的一个插件,实现将maven程序自动打包并配置到Tomcat服务器中。(不是真的会打一个war
包,拷贝到webapps目录内,smart Tomcat简化这个过程,直接让Tomcat服务器从指定路径访问了程序,实现了逻辑上的配置)
1、404:表示资源不存在
2、405:表示方法不支持
我们以上重写的是doGet方法,也就是Get请求
一般执行Get请求的操作:
一般执行Post请求的操作:
- @WebServlet("/hi")//当url路径包含hi目录时,才会触发这个类
- public class Hello extends HttpServlet {
-
- @Override
- protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- //HttpServletRequest:请求类 HttpServletResponse:响应类
- //Tomcat服务器的控制台的内容
- System.out.println("服务器hello");
- //2、当服务器接收到请求之后,做出的响应
- resp.getWriter().write("hello");
- }
- }
当前是Post方法,我们直接输入http://localhost:8080/hello/hi,执行的是Get方法,方法不匹配
3、 500:服务器出现异常
- String ret=null;
- resp.getWriter().write(ret.length());
比如以上这种,空指针异常
4、无法访问此网站
Tomcat服务器启动问题
servlet 3.0 API由以下4个包组成:
1、javax.servlet包:定义了开发独立于协议的服务器的小程序的接口和类
2、javax.servlet.http包:定义了开发采用HTTP通信的服务器的小程序的接口和类
3、javax.servlet.annotation包:定义了9个注解类型和两个枚举类型
4、javax.servlet.descriptor包:定义了以编程方式访问Web英语程序配置信息的类型
这个包定义了独立于任何协议的一般Servlet接口和类
狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet理解为后者
接口 | 说明 |
Servlet | 所有Servlet的根接口 |
ServletRequest | 客户请求的接口 |
ServletRespons | 服务器响应的接口 |
ServletContextListener | 监听web应用程序的监听器接口 |
ServletContextAttributedListener | 监听web应用程序属性的监听器接口 |
RequestDispatcher | 将请求转发给其他资源 |
ServletConfig | Servlet类使用的过滤器配置对象 |
常用的类和接口:
1、Servlet 接口:Servlet API中的核心接口,每一个Servlet 程序必须直接或者间接的使用这个接口
我们介绍它的3个方法:
2、ServletConfig 接口:为用户提供了Servlet 的配置信息
3、GenericServlet 类: 这个抽象类实现了ServletConfig 接口和Servlet接口,提供了Servlet接口中处理 service()外的所有方法,同时也增加了几个日志操作的方法,可以提供扩展这个类并实现 service()方法来创建任何类型的 servlet
介绍几个重要的类和接口
1、HttpServlet 类
这个抽象类用于实现针对 HTTP协议 的servlet,扩展了GenericServlet类,实现了service ()方法,而service()是用来处理客户请求的
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { super.service(req, resp); }
查看service()方法的源码,这个方法可以处理GET,POST,PUT等请求,根据方法名执行对应的doXXX方法
- protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- String method = req.getMethod();
- long lastModified;
- if (method.equals("GET")) {
- lastModified = this.getLastModified(req);
- if (lastModified == -1L) {
- this.doGet(req, resp);
- } else {
- long ifModifiedSince = req.getDateHeader("If-Modified-Since");
- if (ifModifiedSince < lastModified) {
- this.maybeSetLastModified(resp, lastModified);
- this.doGet(req, resp);
- } else {
- resp.setStatus(304);
- }
- }
- } else if (method.equals("HEAD")) {
- lastModified = this.getLastModified(req);
- this.maybeSetLastModified(resp, lastModified);
- this.doHead(req, resp);
- } else if (method.equals("POST")) {
- this.doPost(req, resp);
- } else if (method.equals("PUT")) {
- this.doPut(req, resp);
- } else if (method.equals("DELETE")) {
- this.doDelete(req, resp);
- } else if (method.equals("OPTIONS")) {
- this.doOptions(req, resp);
- } else if (method.equals("TRACE")) {
- this.doTrace(req, resp);
- } else {
- String errMsg = lStrings.getString("http.method_not_implemented");
- Object[] errArgs = new Object[]{method};
- errMsg = MessageFormat.format(errMsg, errArgs);
- resp.sendError(501, errMsg);
- }
-
- }
HttpServlet类是抽象类,可以通过继承这个类,重写service()方法,它会自动判断请求类型,从而调用对应方法,但是根据请求计算响应,逻辑还是doGet()方法等实现
所以我们直接重写doGet()方法处理GET请求,重写doPost()方法,处理Post请求
- @WebServlet("/ok")//当url中第二级路径是/ok,才会触发到这个类,才可以处理请求
- public class ServletAPI extends HttpServlet {
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- //重写doGet方法,用于处理HTTP协议的GET请求
- //1、接收请求
- //2、根据请求计算响应
- int a =10;
- //3、将响应返回给服务器
- resp.getWriter().write(a+"ok");
- }
- }
这里看似是ServletAPI类扩展了HttpServlet类,实际上是 ServletAPI类 实现了Servlet接口,(因为HttpServlet这个抽象类继承自GenericServlet 类,GenericServlet类实现了ServletConfig 接口和Servlet接口)
所以是:ServletAPI类实现了Servlet接口,重写了doGet()方法
Tomcat将请求传递给ServletAPI类,Tomcat调用Servlet接口的service()方法,service()方法内部调用doGet()方法,调用的是ServletAPI 类重写的doGet()方法,就涉及到了多态
2、HttpServeltRequest接口
扩展了ServletRequest接口,并提供了一些方法获取到HTTP请求的内容
3、HttpServeltResponse接口
扩展了ServletResponse接口,并提供了一些方法针对HTTP协议返回响应
Servlet作为一个在容器中运行的组件,有一个创建到销毁的过程,这个过程被称作Servlet的生命周期
接收请求
1、Tomcat初始化
Tomcat会在指定的目录中找到要加载资源的 servlet
2、Tomcat部署的是war包,存入的是.class文件和其他的css,js等文件,那么对于.class文件,就需要根据反射机制为需要使用的类创建实例化对象,这个对象本质上实现了Servlet 这个根接口
3、对于servlet 接口的实例,Tomcat要调用 servlet 接口的init()方法,完成类的初始化并准备提供服务
4、如果是启动HTTP的服务器,那么Tomcat创建TCP 的socket,监听8080端口,等待客户端连接;这个等待接收的过程,是基于多线程实现的
5、如果以上监听8080端口的环节正常结束,那么会给每一个servlet实例调用destory()方法,关闭相关资源;这里的正常结束,比如被别的线程结束,但是如果直接关闭了Tomcat,就可能来不及调用destory()
处理请求
1、根据socket对象的信息(字符串),构造基于HTTP协议的请求,并创建一个空的HTTP响应
2、根据HTTP请求中的信息,找到URL,也即是所请求访问资源的地址
如果这个地址在我们电脑存在,那么表示请求访问的资源是静态资源,直接将对应内容输出即可;
否则,对应的就是动态页面;Tomcat调用Servlet 接口 的service()方法,service()方法执行被重写的doXXX()方法,产生响应。涉及到了多态
我们将一个Servlet 类的实例,经历init(),service(),destory()三个状态的时间,称之为 Servlet的生命周期;对于一个类,init()仅且执行多次,service()可能执行多次,destory()最多执行一次(可能被异常关闭)
一个servlet 程序也可以部署静态文件,放在webapp目录即可
浏览器使用Tomcat加载demo.html这个静态页面,然后这个静态页面发送Post请求,被服务器接收,做出响应
出现上述的乱码,是因为编码方式的问题
idea默认使用UTF-8编码,windows默认使用GBK编码,需要统一编码
就是设置header的content-type属性
- @WebServlet("/method")
- public class ServletText extends HttpServlet {
- @Override
- protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- resp.setContentType("text/html;charset=utf-8");//指定响应的格式
- resp.getWriter().write("响应 ok");
- }
- }
支持很多方法,获取到请求的信息
- @WebServlet("/request")
- public class HttpServletRequestApi extends HttpServlet {
- @Override
- protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- StringBuilder builder = new StringBuilder();
- builder.append(req.getProtocol());//返回请求所使用的协议名称和版本
- builder.append('\n');
- builder.append(req.getMethod());//返回请求使用的方法
- builder.append('\n');
- builder.append(req.getRequestURI());//获取请求的资源路径
- builder.append('\n');
- builder.append(req.getRequestURL().toString());//获取请求的统一资源定位符(绝对路径)
- builder.append('\n');
- builder.append(req.getContextPath());//获取指示请求上下文的路径
- builder.append('\n');
- builder.append(req.getQueryString());//获取QueryString字段内容
- builder.append('\n');
- builder.append(req.getCharacterEncoding());//获取body的字符编码
- builder.append("\n");
- Enumeration<String> headerNames = req.getHeaderNames();//返回所有请求头的头名 也就是key
- while (headerNames.hasMoreElements()) {
- String des = headerNames.nextElement();//key
- builder.append(des).append(" ");
- builder.append(req.getHeader(des));//根据key值得到对应的value
- builder.append('\n');
- }
-
- //读取body内容,返回一个字节流
- ServletInputStream in = req.getInputStream();
- int len = 0;
- byte[] elem = new byte[1024];
- while ((len = in.read(elem)) != -1) {
- builder.append(new String(elem, 0, len, StandardCharsets.UTF_8));
- }
- resp.getWriter().write(builder.toString());
- }
- }
创建请求
运行结果
URI: 统一资源标识符。用来唯一标识资源,是一种语义上的抽象概念。
URL: 统一资源定位符。用来定位唯一的资源, 必须提供足够的定位信息。
URN: 统一资源名称。定义了资源的身份(命名)。
简单比喻 - URI唯一标识一个人(例如身份证), URL定义了如何访问到这个人(例如家庭地址),URN用名字标识一个人(假设所有人名字都不一样的情况下)
URL和URN都是URI的子集
post请求的数据格式:
body的数据组织形式Content-Type有三种:
1、application/x-www-form-urlencoded; 就是键值对的形式,和url的queryString格式相同;也是默认的格式userid=123&pass=908
2、application/json {userid:123,pass:908}
3、multipart/from-data:文件上传,下载的格式
对于json格式的body,可以使用第三方库:jackson来获取数据内容
- <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
- <script >
- //创建post请求 body部分设置为json格式
- $.ajax({
- type: 'post',
- url:"jsontext",
- contentType:'application/json',
- data:JSON.stringify({ //将js数据的对象 转化为一个json格式的字符串
- name:'123',password:'456'//这个是一个js对象
-
- }),
- success: function(body){
- console.log(body);
- }
- });
抓包观察,body部分的数据格式
- class type{
- public int name;
- public int password;
- //属性名要匹配
- }
- @WebServlet("/jsontext")
- public class json extends HttpServlet {
- //1、创建jackson的对象
- // ObjectMapper类是Jackson库的主要类。它提供一些功能将转换成Java对象匹配JSON结构
- ObjectMapper objectMapper = new ObjectMapper();
-
- @Override
- protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- //2、读取body内容,解析成为对应对象
- type ret = objectMapper.readValue(req.getInputStream(), type.class);//转化为了Type对象
- //根据type.class,也就是反射,json的name 和password,匹配到type的两个属性
-
- resp.getWriter().write("name :" + ret.name + "password :" + ret.password);
- }
- }
获取get请求中jqueryString的内容
- @WebServlet("/QueryString")
- public class QueryString extends HttpServlet {
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- resp.setContentType("text/html;charset=utf-8");
- String name= req.getParameter("name");
- //获取参数的值,如果不存在返回null,也就是jqueryString中name=123,这里就返回123
- String password= req.getParameter("password");
- resp.getWriter().write("name: "+name+ " password: "+password);
- //结果:name=123 password=456
- }
- }
- @WebServlet("/demo")
- public class read extends HttpServlet {
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- resp.setHeader("Refresh", "1");//设置一个Header内的键值对,如果name已经存在,进行覆盖
- //结果:每隔一秒,刷新页面
- resp.getWriter().write("time"+ System.currentTimeMillis());//按照文本格式,将数据写回客户端
-
- resp.setStatus(302);//设置状态码
- resp.addHeader("location","https://www.sogou.com");//设置一个Header内的键值对,如果name已经存在,不进行覆盖
- //结果:重定位
- //重定位,可以简化
- resp.sendRedirect("https://www.sogou.com");
- }
- }