tomcat servlet入门
tomcat下载地址:https://tomcat.apache.org/download-80.cgi
解压到你的目录即完成安装
⽂件夹 | 说明 | 备注 |
bin |
该⽬录下存放的是⼆进制可执
⾏⽂件
|
startup.bat启动Tomcat、
shutdown.bat停⽌Tomcat
|
conf |
这是⼀个⾮常重要的⽬录,这
个⽬录下有两个最为重要的⽂
件server.xml和web.xml
|
server.xml:配置整个服务器信
息。例如修改端⼝号,编码格
式等。
web.xml:项⽬部署描述符⽂
件,这个⽂件中注册了很多
MIME类型,即⽂档类型。
|
lib |
Tomcat的类库,⾥⾯存放
Tomcat运⾏所需要的jar⽂件。
|
|
logs |
存放⽇志⽂件,记录了Tomcat
启动和关闭的信息,如果启动
Tomcat时有错误,异常也会记
录在⽇志⽂件中。
|
|
temp |
Tomcat的临时⽂件,这个⽬录
下的东⻄在停⽌Tomcat后删
除。
|
|
webapps |
存放web项⽬的⽬录,其中每个
⽂件夹都是⼀个项⽬;其中
ROOT是⼀个特殊的项⽬,在地
址栏中没有给出项⽬⽬录时,
对应的就是ROOT项⽬。
|
|
work |
运⾏时⽣成的⽂件,最终运⾏
的⽂件都在这⾥。
|
当客户端⽤户访问⼀个JSP⽂件
时,Tomcat会通过JSP⽣成
Java⽂件,然后再编译Java⽂
件⽣成class⽂件,⽣成的java
和class⽂件都会存放到这个⽬
录下。
|
修改端口号:
Tomcat默认端⼝号为8080,可以通过conf/server.xml⽂件修改
手动新建myweb 项目,结构如下

WEB-INF/web.xml
<?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_3_1.xsd" version="3.1" metadata-complete="true"> <display-name>Welcome to Tomcat</display-name> <description> Welcome to Tomcat </description> </web-app>
hello.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>这是第一个页面</title> </head> <body> 这是 我的第一个web页面 </body> </html>
手动创建:
1.
View Code
View Code
View Code
View Code
View Code
View Code
View Code
View Code
注意:-Dfile.encoding=UTF-8 的添加会解决控制台乱码问题,如果解决不了,请参考文章最后的链接
注意 tomcat 启动失败可能是由于端口被占用。访闻地址及结果如下:
添加java代码:

/** * @authour cyf * 2023/7/5 20:43 * 直接继承 Servlet 实现接口 */ public class MyServlet1 implements Servlet { @Override public void init(ServletConfig servletConfig) throws ServletException {} @Override public ServletConfig getServletConfig() { return null; } @Override public String getServletInfo() { return null; } @Override public void destroy() {} @Override public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { System.out.println("访问了服务器 MyServlet1------>"); } }

/** * @authour cyf * 2023/7/5 22:04 * 继承 GenericServlet 重写 service 方法 */ public class MyServlet2 extends GenericServlet { @Override public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { System.out.println("访问了服务器 MyServlet2------>"); } }

/** * @authour cyf * 2023/7/5 22:04 * 继承 HttpServlet 重写所需方法 */ public class MyServlet3 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // super.doGet(req, resp); System.out.println("访问了服务器 MyServlet3 doGet()------>"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // super.doPost(req, resp); System.out.println("访问了服务器 MyServlet3 doPost()------>"); } }
MyServlet4 比较重要:
MyServlet4 将请求转发到 MyServlet5,MyServlet5再 重定向到 MyServlet6,

/** * @authour cyf * 2023/7/6 8:17 * 使用 @WebServlet 注解,不用在web.xml 里配 * * 线程安全问题:Servlet在访问之后,会执⾏实例化操作,创建⼀个Servlet对象。⽽我们Tomcat容器可以同时多个线程并发访问同⼀个Servlet, * 如果在⽅法中对成员变量做修改操作,就会有线程安全的问题。 * 1.synchronized 将存在线程安全问题的代码放到同步代码块中 * 2.实现SingleThreadModel接⼝:servlet实现SingleThreadModel接⼝后,每个线程都会创建servlet实例,这样每个客户端请求就不存在共享资源的问题, * 但是servlet响应客户端请求的效率太低,所以已经淘汰。 * 3.尽可能使⽤局部变量(推荐) */ @WebServlet(name = "MyServlet4", value = "/MyServlet4") public class MyServlet4 extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("MyServlet4 ----->"); /** * tomcat8以上,只需要添加request.setCharacterEncoding("utf-8"); 即可解决post乱码问题,get已经没有乱码问题 */ request.setCharacterEncoding("utf-8"); getRequestLine(request); getRequestHead(request); getRequestBody(request); /**请求转发 * 1.浏览器地址栏路径不发⽣变化 * 2.只能转发到当前服务器内部资源中。 * 3.转发是⼀次请求 */ request.setAttribute("forword", "shardData");//设置共享数据 request.getRequestDispatcher("/myServlet5").forward(request, response); //获取应用范围的作用域,进行数据共享 // ServletContext servletContext = request.getServletContext(); // servletContext.setAttribute(); } /** * 在Tomcat7及以下版本,客户端以UTF-8的编码传输数据到服务器端,⽽服务器端的request对象使 * ⽤的是ISO8859-1这个字符编码来接收数据,服务器和客户端沟通的编码不⼀致因此才会产⽣中⽂乱 * 码的 * Tomcat8的版本中get⽅式不会出现乱码了,因为服务器对url的编码格式可以进⾏⾃动转换。 */ private String transactionStr(String str) { try { return new String(str.getBytes(StandardCharsets.ISO_8859_1),"UTF-8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } //修改 Arrays.toString() 源码 private String transactionStrs(String[] a) { if (a == null) return "null"; int iMax = a.length - 1; if (iMax == -1) return "[]"; StringBuilder b = new StringBuilder(); b.append('['); for (int i = 0; ; i++) { b.append(transactionStr(a[i])); if (i == iMax) return b.append(']').toString(); b.append(", "); } } /** * 获取请求体,有 post方式 和 通用方式(推荐) * * @param request */ private void getRequestBody(HttpServletRequest request) throws IOException { //针对post方法的两种请求体的读取方式 /* if (request.getMethod().equalsIgnoreCase("post")) { System.out.println("打印post 请求体---->"); //1.通过BufferedReader 读取 BufferedReader reader = request.getReader(); String str; while ( (str = reader.readLine()) != null){ System.out.println(str); } //2.通过ServletInputStream 读取 // ServletInputStream inputStream = request.getInputStream(); // InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8"); // char buf[] = new char[10]; // while (inputStreamReader.read(buf) != -1){ // System.out.print(new String(buf)); // } } */ System.out.println("通用参数获取1-----------》"); Enumeration<String> parameterNames = request.getParameterNames(); while (parameterNames.hasMoreElements()) { String name = parameterNames.nextElement(); String value = request.getParameter(name); // System.out.println(name + ":" + transactionStr(value)); System.out.println(name + ":" + value); } System.out.println("通用参数获取2-----------》"); Map<String, String[]> parameterMap = request.getParameterMap(); Iterator<Map.Entry<String, String[]>> iterator = parameterMap.entrySet().iterator(); parameterMap.entrySet().stream().forEach(e -> { String key = e.getKey(); String[] value = e.getValue(); //System.out.println(key + ":" + transactionStrs(value)); System.out.println(key + ":" + Arrays.toString(value)); }); System.out.println("通过getParameterValues()获取value数组-----------》"); String[] usernames = request.getParameterValues("username"); // System.out.println(transactionStrs(usernames)); System.out.println(Arrays.toString(usernames)); } /** * 获取 Request 请求头 * 下⾯是⼀些最常⻅的请求头: * Accept:浏览器可接受的MIME类型。 * Accept - Charset:浏览器可接受的字符集。 * Accept - Encoding:浏览器能够进⾏解码的数据编码⽅式,⽐如gzip。Servlet能够向⽀持gzip的浏览器返回经gzip编码的HTML⻚⾯。许多情形下这可以减少5到10倍的下载时间。 * Accept - Language:浏览器所希望的语⾔种类,当服务器能够提供⼀种以上的语⾔版本时要⽤到。 * Authorization:授权信息,通常出现在对服务器发送的WWW - Authenticate头的应答中。 * Connection:表示是否需要持久连接。如果Servlet看到这⾥的值为“Keep - Alive”,或者看到请求使 * ⽤的是HTTP 1.1(HTTP 1.1默认进⾏持久连接),它就可以利⽤持久连接的优点,当⻚⾯包含多个元 * 素时(例如Applet,图⽚),显著地减少下载所需要的时间。要实现这⼀点,Servlet需要在应答中发 * 送⼀个Content - Length头,最简单的实现⽅法是:先把内容写⼊ByteArrayOutputStream,然后在 * 正式写出内容之前计算它的⼤⼩。 * Content - Length:表示请求消息正⽂的⻓度。 * Cookie:这是最重要的请求头信息之⼀,参⻅后⾯《Cookie处理》⼀章中的讨论。 * From:请求发送者的email地址,由⼀些特殊的Web客户程序使⽤,浏览器不会⽤到它。 * Host:初始URL中的主机和端⼝。 * If - Modified - Since:只有当所请求的内容在指定的⽇期之后⼜经过修改才返回它,否则返回304“Not Modified”应答。 * Pragma:指定“no - cache”值表示服务器必须返回⼀个刷新后的⽂档,即使它是代理服务器⽽且已经有了⻚⾯的本地拷⻉。 * Referer:包含⼀个URL,⽤户从该URL代表的⻚⾯出发访问当前请求的⻚⾯。 * User - Agent:浏览器类型,如果Servlet返回的内容与浏览器类型有关则该值⾮常有⽤。 * UA - Pixels,UA - Color,UA - OS,UA - CPU:由某些版本的IE浏览器所发送的⾮标准的请求头,表示屏幕⼤⼩、颜⾊深度、操作系统和CPU类型。 * https://blog.csdn.net/kfanning/article/details/6062118/ * * @param request */ private void getRequestHead(HttpServletRequest request) throws UnsupportedEncodingException { System.out.println("打印请求头----->"); Enumeration<String> headerNames = request.getHeaderNames();//获取所有的请求头名称 while (headerNames.hasMoreElements()) { String name = headerNames.nextElement(); String value = request.getHeader(name);//通过请求头的名称获取请求头的值 //解决中文乱码 // System.out.println(name + ":" + transactionStr(value)); System.out.println(name + ":" + value); } } /** * 获取 Request 请求行 * @param request http://localhost:8080/webDemo/MyServlet4?username=zhangsan&age=10 */ private void getRequestLine(HttpServletRequest request) { //获取请求方式 get/set: GET String method = request.getMethod(); System.out.println("request 请求方式:" + method); //获取协议及版本: HTTP/1.1 String protocol = request.getProtocol(); System.out.println("获取协议及版本:" + protocol); //获取客户机Ip地址: 0:0:0:0:0:0:0:1 host:0:0:0:0:0:0:0:1 port:52125 String remoteAddr = request.getRemoteAddr(); String remoteHost = request.getRemoteHost(); int remotePort = request.getRemotePort(); System.out.println("获取客户机Ip地址:" + remoteAddr + " host:" + remoteHost + " port:" + remotePort); //获取请求uri: /webDemo/MyServlet4 String requestURI = request.getRequestURI(); System.out.println("获取请求uri:" + requestURI); //获取请求url: http://localhost:8080/webDemo/MyServlet4 StringBuffer requestURL = request.getRequestURL(); System.out.println("获取请求url:" + requestURL); //获取虚拟目录(项目目录): /webDemo String contextPath = request.getContextPath(); System.out.println("获取虚拟目录(项目目录):" + contextPath); //获取Servlet路径: /MyServlet4 String servletPath = request.getServletPath(); System.out.println("获取Servlet路径:" + servletPath); //获取get方法的所有参数:username=zhangsan&age=10 if (method.equalsIgnoreCase("get")) { String queryString = request.getQueryString(); System.out.println("获取get方法的所有参数:" + queryString); } } }
MyServlet5

/** * @authour cyf * 2023/7/6 11:38 * 使用 @WebServlet 注解,不用在web.xml 里配 */ @WebServlet(name = "MyServlet5", value = "/myServlet5") public class MyServlet5 extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("myServlet5 收到转发共享数据 forword:" + request.getAttribute("forword")); request.removeAttribute("forword"); System.out.println("移除共享数据后:"+request.getAttribute("forword")); //设置重定向 response.setContentType("text/html;charset=utf-8"); //方式一 // response.setStatus(302);//设置重定向状态码 // response.setHeader("location","/webDemo/myServlet6");//设置重定向地址,注意带/webDemo 项目地址 //方式二 (推荐) uri里携带汉字,需要进行转码拼接 response.sendRedirect("/webDemo/myServlet6?name="+new String("李四".getBytes("utf-8"), "iso-8859-1")); } }
MyServlet6

/** * @authour cyf * 2023/7/6 15:30 * 使用 @WebServlet 注解,不用在web.xml 里配 */ @WebServlet(name = "MyServlet6", value = "/myServlet6") public class MyServlet6 extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("utf-8"); String name = request.getParameter("name"); System.out.println("定向跳转过来的数据 name :" + name); //response乱码处理 //方式一 // response.setCharacterEncoding("utf-8"); // response.setHeader("Content-type", "text/html;charset=utf-8"); //方式二 (推荐) response.setContentType("text/html;charset=utf-8"); //response 返回方式 //方式一 字符流返回 PrintWriter writer = response.getWriter(); writer.write("重定向成功!"); writer.close(); //方式二 字节流返回 (一般用于返回大文件) // ServletOutputStream outputStream = response.getOutputStream(); // outputStream.write("重定向成功!".getBytes("utf-8")); } }
logion.html

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>登录</title> </head> <body> <form action="/webDemo/MyServlet4" method="post"> <table align="center" border="1" cellspacing="0" cellpadding="0"> <tr> <td>用户名</td> <td><input type="text" name="username"/></td> </tr> <tr> <td>密码</td> <td><input type="password" name="password"/></td> </tr> <tr> <td></td> <td><input type="submit" value="登录"/></td> </tr> </table> </form> </body> </html>
新建一个Servlet,就会按照模板方式生成带有 @WebServlet 注解的代码文件
添加 web.xml 配置

<?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"> <!--Servlet配置 --> <servlet> <!--名称 --> <servlet-name>myServlet1</servlet-name> <!--Servlet的全称类名 --> <servlet-class>com.test.MyServlet1</servlet-class> <!--启动的优先级,数字越⼩越先起作⽤ --> <load-on-startup>1</load-on-startup> </servlet> <!--映射配置 --> <servlet-mapping> <!--名称 servlet-name 属性--> <servlet-name>myServlet1</servlet-name> <!--资源的匹配规则:精确匹配 --> <url-pattern>/myServlet1</url-pattern> </servlet-mapping> <servlet> <servlet-name>myServlet2</servlet-name> <servlet-class>com.test.MyServlet2</servlet-class> </servlet> <servlet-mapping> <servlet-name>myServlet2</servlet-name> <url-pattern>/myServlet2</url-pattern> </servlet-mapping> <servlet> <servlet-name>myServlet3</servlet-name> <servlet-class>com.test.MyServlet3</servlet-class> </servlet> <servlet-mapping> <servlet-name>myServlet3</servlet-name> <url-pattern>/myServlet3</url-pattern> </servlet-mapping> <!-- url-pattern定义匹配规则,取值说明: 精确匹配 /具体的名称 只有url路径是具体的名称的时候才会触发Servlet 后缀匹配 *.xxx 只要是以xxx结尾的就匹配触发Servlet 通配符匹配 /* 匹配所有请求,包含服务器的所有资源 --> <!-- load-on-startup 1元素标记容器是否应该在web应⽤程序启动的时候就加载这个servlet。 2它的值必须是⼀个整数,表示servlet被加载的先后顺序。 3如果该元素的值为负数或者没有设置,则容器会当Servlet被请求时再加载。 4如果值为正整数或者0时,表示容器在应⽤启动时就加载并初始化这个servlet,值越⼩,servlet的优 先级越⾼,就越先被加载。值相同时,容器就会⾃⼰选择顺序来加载。 --> </web-app>
转发,重定向测试地址:http://localhost:8080/webDemo/logion.html 访问登录页面,可以修改进行 get / post 访问
注意此时代码的访闻路径是:http://localhost:8080/webDemo/myServlet
控制台会打印出:
返回错误码:
状态代码 | 状态描述 | 说明 |
200 | OK | 客户端请求成功 |
302 | Found | 临时重定向 |
403 | Forbidden |
服务器收到请求,但是拒绝提
供服务。服务器通常会在响应
正⽂中给出不提供服务的原因
|
404 | Not Found |
请求的资源不存在,例如,输
⼊了错误的URL。
|
500 | Internal Server Error |
服务器发⽣不可预期的错误,
导致⽆法完成客户端的请求。
|
sendRedirect跳转时,地址栏改变,代表客户端重新发送的请求。属于两次请求
● response没有作⽤域,两次request请求中的数据⽆法共享
● 传递数据:通过URI的拼接进⾏数据传递("/WebProject/b?username=tom");
● 获取数据:request.getParameter("username");
乱码处理参考:https://blog.csdn.net/qq_45093483/article/details/126951681
filter 过滤器的使用
<!--xml 方式 指定拦截器--> <filter> <filter-name>tomcatFilter</filter-name> <filter-class>com.practice.filter.TomcatFilter</filter-class> </filter> <filter-mapping> <filter-name>tomcatFilter</filter-name> <!--通过servlet名字拦截--> <servlet-name>tomcatServlet</servlet-name> <!--通过路径拦截--> <!--<url-pattern>/*</url-pattern>--> </filter-mapping>
/** * urlPatterns = "/*" //监听所有 * 过滤器的过滤路径通常有三种形式: * 1.精确过滤匹配 ,⽐如/index.jsp /myservlet1 * 2.后缀过滤匹配,⽐如*.jsp、*.html、*.jpg * 3.通配符过滤匹配/*,表示拦截所有。注意过滤器不能使⽤/匹配。 * /aaa/bbb/* 允许 * * 过滤器链:在⼀个Web应⽤中,可以开发编写多个Filter,这些Filter组合起来称之为⼀个Filter链。 * 优先级: * 1.如果为注解的话,是按照类全名称的字符串顺序决定作⽤顺序 * 2.如果web.xml,按照 filter-mapping注册顺序,从上往下 * 3.web.xml配置⾼于注解⽅式 如果注解和web.xml同时配置,会创建多个过滤器对象,造成过滤多次。 */ // 注解方式指定拦截器 @WebFilter(filterName = "MyFilter1", urlPatterns = "/*") public class TomcatFilter implements Filter{ @Override public void init(FilterConfig filterConfig) throws ServletException {} @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { //设置编码过滤 servletRequest.setCharacterEncoding("UTF-8"); servletResponse.setContentType("text/html;charset=utf-8"); //执行传递 filterChain.doFilter(servletRequest, servletResponse); } @Override public void destroy() {} }
listener 监听器的使用
<!--xml 方式加载监听器--> <listener> <listener-class>com.test.listener.MyListener1</listener-class> </listener>
/** * @authour cyf * 2023/7/7 10:01 * 注解方式加载监听器 */ @WebListener public class MyListener1 implements ServletContextListener, HttpSessionListener, HttpSessionAttributeListener { public MyListener1() {} @Override public void contextInitialized(ServletContextEvent sce) { /* This method is called when the servlet context is initialized(when the Web application is deployed). */ System.out.println("MyListener1 contextInitialized()--------->"); ServletContext servletContext = sce.getServletContext(); String contextLoadConfig = servletContext.getInitParameter("contextLoadConfig"); System.out.println("contextLoadConfig:"+contextLoadConfig); } @Override public void contextDestroyed(ServletContextEvent sce) { /* This method is called when the servlet Context is undeployed or Application Server shuts down. */ System.out.println("MyListener1 contextDestroyed()--------->"); } @Override public void sessionCreated(HttpSessionEvent se) { /* Session is created. */ } @Override public void sessionDestroyed(HttpSessionEvent se) { /* Session is destroyed. */ } @Override public void attributeAdded(HttpSessionBindingEvent sbe) { /* This method is called when an attribute is added to a session. */ } @Override public void attributeRemoved(HttpSessionBindingEvent sbe) { /* This method is called when an attribute is removed from a session. */ } @Override public void attributeReplaced(HttpSessionBindingEvent sbe) { /* This method is called when an attribute is replaced in a session. */ } }
指定初始化参数
<!--指定初始化的参数--> <context-param> <param-name>contextLoadConfig</param-name> <param-value>/WEB-INF/classes/applicationContext.xml</param-value> </context-param>
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
2021-07-05 SVN分支/主干Merge操作小记