Tomcat和Servlet应用配置及部分问题
持续更新中……
Tomcat
维基百科复制来的长篇大论:
Tomcat是由Apache软件基金会属下Jakarta项目开发的Servlet容器,按照Sun Microsystems提供的技术规范,实现了对Servlet和JavaServer Page(JSP)的支持,并提供了作为Web服务器的一些特有功能,如Tomcat管理和控制平台、安全局管理和Tomcat阀等。由于Tomcat本身也内含了HTTP服务器,因此也可以视作单独的Web服务器。但是,不能将Tomcat和Apache HTTP服务器混淆,Apache HTTP服务器是用C语言实现的HTTPWeb服务器;这两个HTTP web server不是捆绑在一起的。Apache Tomcat包含了配置管理工具,也可以通过编辑XML格式的配置文件来进行配置。
使用
Idea的下载安装及配置不再赘述。
Tomcat10.0.4下载地址:64位win.zip、32位win.zip、其他(官网)
下载的压缩包需要解压到纯英文目录,安装包同理安装在纯英文目录下。
如果没有配置过jdk和jre的目录,需要首先下载和配置jdk!!
双击Tomcat/bin目录下setclasspath.bat,会有cmd窗口一闪而过。
双击Tomcat/bin目录下startup.bat,cmd窗口中出现以下文字则服务器开启成功:
此时在浏览器中输入localhost:8000即可开启Tomcat的demo页面:
- 强行关闭,直接点x
- 双击打开bin目录下shutdown.bat
问题分析
控制台都是乱码看不懂
首先说明这是编码问题,在Tomcat\conf目录下找到logging.properties
更改UTF-8为GBK
打开Tomcat闪退
有两种情况:
- 环境变量没设置好,打开cmd输入java -version即可查看有无设置好jdk的环境变量,其次可能Tomcat的环境变量没设置好,试试以管理员身份打开setclasspath.bat。
- 端口占用
- 打开cmd输入netstat -aon|findstr “端口号” ,检测端口占用情况(可跳过
- 找到Tomcat\conf\server.xml,用文本编辑器打开,ctrl+f找到8080,改成其他端口,1~1023为系统端口不要设,改为1024~65535即可。
部署项目
部署项目的方式:
直接将项目放到webapps目录下即可。
- /hello:项目的访问路径–>虚拟目录
- 简化部署:将项目打城一个war包,再将war包放置到webapps目录下。
war包会自动解压缩
配置conf/server.xml文件
在<Host>标签体中配置
1
<Context docBase="D:\hello" path="/hehe" />d
- docBase:项目存放的路径
- path:虚拟目录
在conf\Catalina\localhost创建任意名称的xml文件.在文件中编写
1
<Context docBase="D:\hello" />
- 虚拟目录:xml文件的名称
动态项目和静态项目:
目录结构:
静态项目:
- 使用静态资源
java动态项目的目录结构:
- 项目根目录
- WEB-INF
- web.xml:该项目的核心配置文件
- classes目录: 放置字节码文件
- lib目录: 防止项目依赖的jar包
- WEB-INF
- 项目根目录
Tomcat集成到IDEA中
点击Run->Edit Configurations或Add Configurations
点击Configure…
选择Tomcat根目录
上面有
你可能需要点击
没有就不点
新建一个Module
cyan , 没有这个Web Application?
ctrl+shift+alt+/打开Maintenance,打开Registry,找到javaee.legacy.project.wizard
勾选后即可在新建Module中找到JavaEE的Web Application选项
在index.jsp中编写html代码(也可跳过),运行就可以了
打开浏览器,成功运行项目:
Servlet
简介:
维基百科复制来的长篇大论:
Servlet(Server Applet),全称Java Servlet。是用Java编写的服务器端程序。其主要功能在于交互式地浏览和修改数据,生成动态Web内容。狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet理解为后者。
Servlet运行于支持Java的应用服务器中。从实现上讲,Servlet可以响应任何类型的请求,但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器。
最早支持Servlet标准的是JavaSoft的Java Web Server。此后,一些其它的基于Java的Web服务器开始支持标准的Servlet。
概述:
Servlet(server applet)为运行在服务器端的小程序。
- Servlet本质上也是Java类,但要遵循Servlet规范进行编写,没有main()方法,它的创建、使用、销毁都由Servlet容器管理(如Tomcat)。
- Servlet是和HTTP协议紧密联系的,其可以处理HTTP协议相关的所有内容。这也是Servlet应用广泛的原因之一。
- Servlet容器有很多,如Tomcat,Jetty,WebLogic Server,web Sphere,JBoss等等。
Servlet的工作流程
- 通过请求头获知浏览器访问的是哪个主机
- 再通过请求行获取访问的是哪个一个web应用
- 再通过请求行中的请求路径获知访问的是哪个资源
- 通过获取的资源路径在配置中匹配到真实的路径,
- 服务器会创建servlet对象,(如果是第一次访问时,创建servlet实例,并调用init方法进行初始化操作)
- 调用service(request, response)方法来处理请求和响应的操作 7. 调用service完毕后返回服务器 由服务器讲response缓冲区的数据取出,以http响应的格式发送给浏览器
Servlet的实现
创建web项目
new project
继续next
改个项目名字
最终得到的项目结构如下:
实现Servlet
实现Servlet规范,即继承HttpServlet类,该类中已经完成了通信的规则,我们只需要进行业务的实现即可。
警告:请下载对应版本的tomcat,不然会导致404错误无法启动servlet项目。点击直达—>官方版本说明
版本表格
Servlet Spec | JSP Spec | EL Spec | WebSocket Spec | JASPIC Spec | Apache Tomcat version | Actual release revision | Supported Java Versions |
---|---|---|---|---|---|---|---|
4.0 | TBD (2.4?) | TBD (3.1?) | TBD (1.2?) | 1.1 | 9.0.x | 9.0.0.M9 (alpha) | 8 and later |
3.1 | 2.3 | 3.0 | 1.1 | 1.1 | 8.5.x | 8.5.4 | 7 and later |
3.1 | 2.3 | 3.0 | 1.1 | N/A | 8.0.x (superseded) | 8.0.35 (superseded) | 7 and later |
3.0 | 2.2 | 2.2 | 1.1 | N/A | 7.0.x | 7.0.70 | 6 and later |
(7 and later for WebSocket) | |||||||
2.5 | 2.1 | 2.1 | N/A | N/A | 6.0.x | 6.0.45 | 5 and later |
2.4 | 2.0 | N/A | N/A | N/A | 5.5.x (archived) | 5.5.36 (archived) | 1.4 and later |
2.3 | 1.2 | N/A | N/A | N/A | 4.1.x (archived) | 4.1.40 (archived) | 1.3 and later |
2.2 | 1.1 | N/A | N/A | N/A | 3.3.x (archived) | 3.3.2 (archived) | 1.1 and later |
重写Servlet方法
满足Servlet规范只是让我们的类能够满足接受请求的要求,接收到请求后需要对请求进行分析,以及进行业务逻辑处理,计算出结果,则需要添加代码,在规范中有一个叫做service的方法,专门用来做请求处理的操作,业务代码则可以卸载该方法中。
新建一个类
继承HttpServlet
1
2
3
4
5import javax.servlet.http.HttpServlet;
public class ServletDemo1 extends HttpServlet {
}重写service方法
1
2
3
4
5
6
7
8
9
10
11
12import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class HelloServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("hello servlet");// 打印内容到控制台
resp.getWriter().write("Hello world!");// 通过流输出数据到浏览器
}
设置注解
在完成好了一切代码的编写后,还需要向服务器说明,特定请求对应特定资源。
开发servlet项目,使用@WebServlet将一个继承于javax.servlet.http.HttpServlet的类顶i为Servlet组件。
在Servlet3.0中,可以使用@WebServlet注解将一个继承于javax.servlet.http.HttpServlet的类标注为可以处理用户请求的Servlet。
通过设置注解,指定访问路径
启动服务器
继承GenericServlet类,其他不变
实现接口Servlet
创建一个JavaEE项目
定义一个类,实现Servlet接口
实现接口中的抽象方法
配置Servlet
在service方法中加上:
1
System.out.println("hello servlet");
在WEB-INF下的web.xml中加入:
1
2
3
4
5
6
7
8
9<!-- 配置Servlet -->
<servlet>
<servlet-name>demo</servlet-name>
<servlet-class>setobox.me.servlet.ServletDemo1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>demo</servlet-name>
<url-pattern>/demo</url-pattern>
</servlet-mapping>ps: 一定要写在web-app标签中
运行项目
- 点击运行,浏览器地址栏中加/demo
其他配置
- 这个地方只保留斜杠可以缩短地址栏
Servlet的生命周期
Servlet没有main()方法,不能独立运行,它的运行完全由Servlet引擎来控制和调度。所谓生命周期,指的是servlet容器何时创建servlet实例、何时调用其方法进行请求的处理、何时销毁其实例的整个过程。
Servlet的生命周期,servlet类加载–>实例化–>服务–>销毁
实例和初始化时机
当请求到达容器时,容器查找该servlet对象是否存在,如果不存在,则会创建实例并进行初始化。
就绪/调用/服务阶段
有请求到达容器,容器调用servlet对象的service()方法,处理请求的方法在整个生命周期中可以被多次调用;HttpServlet的service()方法,会依据请求方式来调用doGet()或者doPost()方法。但是,这两个do方法默认情况下,会抛出异常,需要子类去override。
销毁时机
当容器关闭时(应用程序停止时),会将程序中的Servlet实例进行销毁。
上述的生命周期可以通过Servlet中的生命周期方法来观察。在Servlet中有三个生命周期方法,不由用户手动调用,而是在特定的时机有容器自动调用,观察这三个生命周期方法,即可观察到Servlet的生命周期。
三个方法:
在Servlet实例创建之后执行(证明该Servlet有实例创建了)
1 | public void init(ServletConfig config) throws ServletException { |
每次有请求到达某个Servlet方法时执行,用来处理请求(证明该Servlet进行服务了)
1 | protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { |
Servlet实例销毁时执行(证明该Servlet的实例被销毁了)
1 | public void destroy() { |
HttpServletRequest对象
简介:
作用:用来接收客户端发送过来的请求信息
HttpServletRequest是ServletRequest的子接口,ServletRequest只有一个子接口,就是HttpServletRequest。提问:既然只有一个子接口,为什么不将两个接口合并为一个?
答:
接受请求
常用方法
getRequestURL() | 获取客户端发出请求时的完整URL |
---|---|
getRequestURI( | 获取请求行中的资源名称部分(项目名称开始) |
getQueryString() | 获取请求行中的参数部分 |
getMethod() | 获取客户端请求方式 |
getProtocol() | 获取HTTP版本号 |
getContextPath() | 获取webapp名字 |
1 | // 获取客户端请求的完整URL (从http开始,到?前面结束) |
获取请求参数
| getParameter(name) | 获取指定名称的参数 |
|———————————|————–|
| getParameterValues(String name) | 获取指定名称参数的所有值 |
1 | // 获取指定名称的参数,返回字符串 |
请求乱码问题
Requests的编码方式为ISO-8859-1(此编码不支持中文),所以解码时会出现乱码。可以设置Requests中的编码方式,告诉服务器以何种方式来解码,也可以在接收到乱码数据后,通过相应的编码还原数据。
1 | request.setCharacterEncoding("UTF-8"); |
仅对post请求有效
1 | new String(request.getParameter(name).getBytes("ISO-8859-1"),"UTF-8"); |
Tomcat7及以下版本的GET请求会乱码,借助String方法,对任意请求都有效。但如果数据原本不乱吗,使用此方法会导致乱码。
请求转发
请求转发,是一种服务器的行为,当用户请求到达后,服务器进行转发,此时会将请求对象进行保存,地址栏中的URL地址不会改变,得到响应后,服务器端再将响应发送给客户端,从始至终只有一个请求发出。
实现方法如下:
1 | request.getRequestDispatcher(url).forward(request,response); |
特点:
- 服务端行为
- 地址栏不发生改变
request作用域
通过该对象可以在一个请求中传递数据,作用范围:在一次请求中有效(请求转发时有效)。
1 | // 设置域对象内容 |
HttpServletResponse对象
接收用request对象,输出用response对象。
响应数据
接收到客户端请求后,可以通过 HttpServletResponse 对象直接进行响应,响应时需要获取输出流。
有两种形式:
1 | // getWriter() 字符输出流 (输出字符串) |
1 | // getOutputStream() 字节输出流 (输出一切数据) |
响应回的数据到客户端被浏览器解析。
注意:两者不能同时使用
响应乱码问题
在响应中,如果我们响应的内容中含有中文,则有可能出现乱码。这是因为服务器响应的数据也会经过网络传输,服务器端有一种编码方式,在客户端也存在一种编码方式,当量古达使用的编码方式不同时则出现乱码。
解决方法:
1 | // 设置服务端的编码格式 |
重定向
重定向是由服务器指导的客户端的行为。客户端发出第一个请求,被服务器接收处理后,服务器会进行响应,在响应的同时,服务器会给客户端一个新的地址。
1 | // 重定向跳转到index.jsp |
请求转发与重定向的区别
请求转发 | 重定向 |
---|---|
一次请求,数据在request域中共享 | 两次请求,数据在request域中不共享 |
服务端行为 | 客户端行为 |
地址栏不发生变化 | 地址栏发生变化 |
绝对地址定位到站点后 | 绝对地址可写到http:// |
Cookie对象
Cookie可以将一些只需要保存在客户端,或只需要在客户端进行处理的出局,存储在本地,减少网络传输,提高网页处理的效率。如:记住密码等。
Cookie的创建和发送
1 | // 创建Cookie对象 |
Cookie的获取
getCookies()方法不能指定获取某个cookie,只能获取由全部cookie组成的一个数组。如果向获取某个cookie,则需要遍历数组,并用Cookie的方法获取。
1 | // 获取Cookie数组 |
Cookie的生存时间
通过设定Cookie的有效时间,可以减少一些隐私泄露的问题,默认情况下,Cookie在当前浏览器关闭时便失效。
生存时间取值有三种:
负整数(默认)
表示不存储该cookie,当前浏览器关闭时该cookie就会从内存中消失。
正整数
表示存储的秒数,cookie会保存到硬盘上。
零
可以删除已经存储的cookie。
1 | // 设置方法 |
Cookie注意事项
Cookie仅保存在当前浏览器中
Cookie不能存储中文
如果需要中文,则需要通过URLEncoder.encode编码和URLEncoder.decode解码
Cookie同名便会覆盖原有Cookie
浏览器中存储Cookie有限制,一般只存少量数据
Cookie存储路径
通过setPath()设置存储路径。
HttpSession对象
HttpSession是一个接口,servlet容器使用此接口创建HTTP客户端的HTTP服务器之间的会话,服务器会为每一个用户创建一个独立的会话。
常用方法:
1 | // 获取session对象 |
Session标识符
Session用sessionid标识每个会话。每当一次请求到达服务器,如果开启了会话,服务器第一步会查看客户端是否回传了一个名叫JSESSIONID的cookie,如果有,则认为是之前标识过的一次会话,返回该session对象,达成数据共享;如果没有,则认为这是一次新的会话,重新创建一个新的session对象,并标志此次会话。
Session域对象
Session用来表示一次会话,在一次会话中数据是可以共享的,这时session作为域对象存在,可以通过setAttribute(name,value)方法向域对象中添加数据,通过getAttribute(name)从域对象中获取数据,通过removeAttribute(name)从域对象中移除数据。
常用方法:
1 | // 获取session对象 |
- Session域 > request域
Session域对象的生命周期
Tomcat中session默认的存活时间为30min,当无操作时长达到session生存时间,会话便会结束。
自定义生命周期(单位:秒):
1 | // 获取session对象 |
ServletContext对象
ServletContext对象的获取
每一个 web 应用都有且仅有一个ServletContext 对象,又称 Application 对象,从名称中可知,该对象是与应用程 序相关的。在 WEB 容器启动的时候,会为每一个 WEB 应用程序创建一个对应的 ServletContext 对象。
作用:
- 第一、作为域对象用来共享数据,此时数据在整个应用程序中共享
- 第二、该对象中保存了当前应用程序相关信息。
1 | // 通过request对象获取 |
常用方法:
1 | // 1.获取当前服务器的版本信息 |
ServletContext域对象
ServletContext也可以当作域对象使用给,通过向ServletContext中存取数据,可以使整个应用程序共享某些数据。ServletContext中数据在服务器关闭后失效。
1 | // 获取ServletContext对象 |
Servlet的三大域对象
request域对象
在一次请求中有效。请求转发有效,重定向失效。
session域对象
在一次会话中有效。请求转发有效,重定向失效。
context域对象
在整个应用程序中有效。关闭服务器后失效。
待更新。。。
参考资料:
- 维基百科
- 黑马程序员JavaWeb视频
- b站Servlet教学视频