1. Servlet运行原理
上图来自:Servlet运行原理
2. Mapping
web.xml详解
servlet映射路径位于
使用‘/’开头,使用‘/’结尾,表示使用路径匹配,比如/foo/bar/
- 一个servlet可以指定一个映射路径,即精确路径匹配
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
- 一个servlet可以指定多个映射路径
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello1</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>hello2</servlet-name>
<url-pattern>/hello2</url-pattern>
</servlet-mapping>
- 一个servlet可以指定通用映射路径
只使用‘/*’,表示匹配所有的请求;
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
- 可以使用前缀或后缀等等
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>.do</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
连接成功
- url优先级问题
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>hello2</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
默认走通用的,但如果指定了对应路径,会优先走指定的路径,而非通用路径。
tips:可以将web.xml的模板内容在项目下新建一个note.md保存起来,方便使用。
web.xml
```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"
metadata-complete="true">
</web-app>
3.ServletContext
WEB容器在启动的时候,他会为每个web程序创建一个对应的ServletContext对象,它代表了当前的web应用。
3.1 共享数据
在一个Servlet中保存的数据,可以在另一个Servlet中拿到。注意需要先提交保存数据,再访问保存的数据
package org.raylene.servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext servletContext = this.getServletContext();
String username = "tom & jerry测试中文";
servletContext.setAttribute("username",username);
}
}
package org.raylene.servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class GetContext extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset = utf-8");
ServletContext servletContext = this.getServletContext();
String username = (String)servletContext.getAttribute("username");
resp.getWriter().println("名字:"+username);
}
}
<?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">
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>org.raylene.servlet.HelloServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>getc</servlet-name>
<servlet-class>org.raylene.servlet.GetContext</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>getc</servlet-name>
<url-pattern>/getc</url-pattern>
</servlet-mapping>
</web-app>
3.2 获取初始化参数getInitParameter
<?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">
<context-param>
<param-name>url</param-name>
<param-value>jdbc:mysql://localhost:3006/mybatis</param-value>
</context-param>
<servlet>
<servlet-name>getpara</servlet-name>
<servlet-class>org.raylene.servlet.ServletDemo03</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>getpara</servlet-name>
<url-pattern>/gp</url-pattern>
</servlet-mapping>
</web-app>
package org.raylene.servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class ServletDemo03 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext servletContext = this.getServletContext();
String url = servletContext.getInitParameter("url");
resp.setContentType("text/html;charset=utf-8");
resp.getWriter().println("url为:"+url);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
3.3实现请求转发
<servlet>
<servlet-name>rd</servlet-name>
<servlet-class>org.raylene.servlet.ServletDemo04</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>rd</servlet-name>
<url-pattern>/rd</url-pattern>
</servlet-mapping>
package org.raylene.servlet;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class ServletDemo04 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
System.out.println("进入ServletDemo04");
ServletContext servletContext = this.getServletContext();
RequestDispatcher requestDispatcher = servletContext.getRequestDispatcher("/gp");//转发的请求路径
requestDispatcher.forward(req,resp);//调用forward方法实现转发
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
当我们在浏览器输入rd时,==却收到了ServletDemo03对应的资源,==说明实现了转发
请求转发:
request.getRequestDispatcher(URL地址).forward(request, response)
处理流程:
- 客户端发送请求,Servlet做出业务逻辑处理。
- Servlet调用forword()方法,服务器Servlet把目标资源返回给客户端浏览器。
参考链接:https://www.jianshu.com/p/29822c2c1ec0
3.4 读取资源文件
Properties
关于properties类的一些介绍可以参考这篇博文:Java中Properties类详解
- 在Java目录下新建properties
- 在resources目录下新建properties
发现都被打爆到同一个路径下,target下servlet-02的classes路径下,该路径称为classpath
配置pom文件
<!--在build中配置resources,来防止我们资源导出失败的问题-->
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
注册servlet映射
<servlet>
<servlet-name>prop</servlet-name>
<servlet-class>org.raylene.servlet.ServletDemo06</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>prop</servlet-name>
<url-pattern>/prop</url-pattern>
</servlet-mapping>
package org.raylene.servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
public class ServletDemo06 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext servletContext = this.getServletContext();
InputStream is = servletContext.getResourceAsStream("/WEB-INF/classes/db.properties");
Properties properties = new Properties();
properties.load(is);
String usernm = properties.getProperty("username");
String pwd= properties.getProperty("password");
resp.setContentType("text/html;charset=utf-8");
resp.getWriter().print(usernm+":"+pwd);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
读取java目录下的文件
InputStream is = servletContext.getResourceAsStream("/WEB-INF/classes/org/raylene/servlet/aa.properties");
Properties properties = new Properties();
properties.load(is);
4.HttpServletResponse
Web服务器接收到客户端的请求,针对这个请求,分别创建一个代表请求的HttpServletRequest对象,和代表响应的HttpResponse对象
- 获取客户端请求过来的参数HttpServletRequest
- 给用户返回一些信息,用HttpServletResponse对象
接口HttpServletResponse继承ServletResponse
public interface HttpServletResponse extends ServletResponse
public interface ServletResponse
ServletResponse中的方法有:
String getCharacterEncoding();
String getContentType();
ServletOutputStream getOutputStream() throws IOException;
PrintWriter getWriter() throws IOException;
void setCharacterEncoding(String var1);
void setContentLength(int var1);
void setContentType(String var1);
void setBufferSize(int var1);
int getBufferSize();
void flushBuffer() throws IOException;
void resetBuffer();
boolean isCommitted();
void reset();
void setLocale(Locale var1);
Locale getLocale();
ServletResponse中常用的方法
ServletOutputStream getOutputStream() throws IOException;
PrintWriter getWriter() throws IOException;//负责向浏览器发送响应数据
void setContentType(String var1);
HttpServletResponse中有一些HTTP状态码常量及一些相应的方法
响应状态码
int SC_CONTINUE = 100;
int SC_SWITCHING_PROTOCOLS = 101;
int SC_OK = 200;
int SC_CREATED = 201;
int SC_ACCEPTED = 202;
int SC_NON_AUTHORITATIVE_INFORMATION = 203;
int SC_NO_CONTENT = 204;
int SC_RESET_CONTENT = 205;
int SC_PARTIAL_CONTENT = 206;
int SC_MULTIPLE_CHOICES = 300;
int SC_MOVED_PERMANENTLY = 301;
int SC_MOVED_TEMPORARILY = 302;
int SC_FOUND = 302;
int SC_SEE_OTHER = 303;
int SC_NOT_MODIFIED = 304;
int SC_USE_PROXY = 305;
int SC_TEMPORARY_REDIRECT = 307;
int SC_BAD_REQUEST = 400;
int SC_UNAUTHORIZED = 401;
int SC_PAYMENT_REQUIRED = 402;
int SC_FORBIDDEN = 403;
int SC_NOT_FOUND = 404;
int SC_METHOD_NOT_ALLOWED = 405;
int SC_NOT_ACCEPTABLE = 406;
int SC_PROXY_AUTHENTICATION_REQUIRED = 407;
int SC_REQUEST_TIMEOUT = 408;
int SC_CONFLICT = 409;
int SC_GONE = 410;
int SC_LENGTH_REQUIRED = 411;
int SC_PRECONDITION_FAILED = 412;
int SC_REQUEST_ENTITY_TOO_LARGE = 413;
int SC_REQUEST_URI_TOO_LONG = 414;
int SC_UNSUPPORTED_MEDIA_TYPE = 415;
int SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
int SC_EXPECTATION_FAILED = 417;
int SC_INTERNAL_SERVER_ERROR = 500;
int SC_NOT_IMPLEMENTED = 501;
int SC_BAD_GATEWAY = 502;
int SC_SERVICE_UNAVAILABLE = 503;
int SC_GATEWAY_TIMEOUT = 504;
int SC_HTTP_VERSION_NOT_SUPPORTED = 505;
4.1 常见应用
- 向浏览器输出消息
- 下载文件
实现方法- 获取下载路径
- 下载文件的名称
- 浏览器支持下载我们需要的内容
- 获取下载文件的输入流
- 创建缓存区
- 获取OutputStream对象
- 将FileOutputStream写入buffer缓存区
- 使用OutputStream将缓存区的对象输出到客户端
4.1 Response下载资源
IO流IO
2,3搞懂
package org.raylene.servlet;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URLEncoder;
public class FileServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 获取下载路径
//获取资源路径
//文件输入流读取资源
String realPath = "G:\\file\\wonder.png";
System.out.println("文件的下载路径为:"+realPath);
// 2. 下载文件的名称
//获取文件名
//substring() 方法返回字符串的子字符串。
//lastIndexOf() 方法可返回一个指定的字符串值最后出现的位置,
// 在一个字符串中的指定位置从后向前搜索
String fileName = realPath.substring(realPath.lastIndexOf("\\")+1);
// 3. 浏览器支持下载我们需要的内容
//设置消息头,告诉浏览器,我要下载1.png这个图片
resp.setHeader("Content-Disposition", "attachment; filename="+ URLEncoder.encode(fileName,"UTF-8"));
// 4. 获取下载文件的输入流
FileInputStream in = new FileInputStream(realPath);
// 5. 创建缓存区
int len = 0;
byte[] bytes = new byte[1024];
// 6. 获取OutputStream对象
ServletOutputStream out = resp.getOutputStream();
// 7. 将FileOutputStream写入buffer缓存区
while((len=in.read(bytes))>0){
out.write(bytes, 0, len);
}
//关闭资源
out.close();
in.close();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
4.2 验证码
resp的方法解读,缓存策略解读,图片画笔
package org.raylene.servlet;
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;
public class ImageServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//如何让浏览器5秒刷新一次
//设置一下自动刷新功能
//下面两个语句均可,选其一
resp.setHeader("refresh","2");
//resp.setIntHeader("refresh", 5);
//在内存新建一个图片
BufferedImage image = new BufferedImage(80,20,BufferedImage.TYPE_INT_RGB);
//得到图片
Graphics2D graphics = (Graphics2D) image.getGraphics();
//设置背景颜色
graphics.setColor(Color.pink);
graphics.fillRect(0,0,80,20);
//给图片写数据
graphics.setColor(Color.BLUE);
graphics.setFont(new Font("null",Font.BOLD,20));
graphics.drawString(makeNum(),0,20);
resp.setContentType("image/jpeg");
//浏览器不缓存
resp.setHeader("Cache-Control", "no-cache");
resp.setHeader("Cache-Control", "no-store");
resp.setHeader("Pragma", "no-cache");
resp.setDateHeader("Expires", 0);
// 或
//
// response.setHeader("Cache-Control", "no-cache");
// response.setHeader("Pragma", "no-cache");
// response.setDateHeader("Expires", 0);
//
//把图片写给浏览器
ImageIO.write(image, "jpg", resp.getOutputStream());
}
private String makeNum(){
Random r = new Random();
String snum = r.nextInt(999999) + "";
StringBuffer sbuffer = new StringBuffer();
//防止生成数小于6位,若小于6位,则用0填充
for (int i = 0; i < 6-snum.length(); i++) {
sbuffer.append("0");
}
String s = sbuffer.toString()+snum;
return s;
}
}
4.3 Response重定向
重定向:
response.sendRedirect(URL地址)
处理流程:
客户端发送请求,Servlet做出业务逻辑处理。
Servlet调用response.sendReadirect()方法,把要访问的目标资源作为response响应头信息发给客户端浏览器。
客户端浏览器重新访问服务器资源xxx.jsp,服务器再次对客户端浏览器做出响应。
参考链接:https://www.jianshu.com/p/29822c2c1ec0
package org.raylene.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class RedirectServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.sendRedirect("/response_war/img");
}
}
注意
当进入该项目时,默认进入index.jsp,当在URL中输入red时,它会告诉浏览器,你去其他的url下找,此时,需要输入项目完整名称(除去localhost:8080这一段的后面全部),也就是重定向可以区定向到不同的项目下。
重定向到另一个项目下
为什么不能重定向到另一个项目下:此时,若你在tomcat中没有配置你要跳转的项目对应的war包,则无法跳转,若已配置,则可以成功实现跳转。如图2所示
resp.sendRedirect("/servlet_01_war/hello");
图1
图2
重定向与转发的区别
相同点:
- 页面都可以拿到对应资源
不同点
- 请求转发时,url不会发生变化
- 重定向时,url会发生变化。
5. HttpServletRequest
关于各类文件乱码:乱码参考
index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<html>
<body>
<h2>Hello World!</h2>
<%--这里的提交路径,需要找到项目的路径--%>
<form action="${pageContext.request.contextPath}/login" method="get">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
手机号<input type="tel" name="telp"><br>
<input type="submit">
</form>
</body>
</html>
success.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>success!</h1>
</body>
</html>
package org.raylene.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class RequestServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("进入了这个请求");
String username = req.getParameter("username");
String pwd = req.getParameter("password");
System.out.println(username+":"+pwd);
resp.sendRedirect("/response_war/success.jsp");
}
}
5.1 获取参数,请求转发
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登录</title>
</head>
<body>
<h1>登录</h1>
<div style="text-align: center">
<%-- 以post方式提交表单,提交的路径/login--%>
<form action="${pageContext.request.contextPath}/login" method="post">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password" ><br>
爱好:
<input type="checkbox"name="hobby" value="bear">bear
<input type="checkbox"name="hobby" value="football">football
<input type="checkbox"name="hobby" value="table tennis">table tennis
<input type="checkbox"name="hobby" value="basketball">basketball
<input type="checkbox"name="hobby" value="swimming">swimming
<input type="submit">
</form>
</div>
</body>
</html>
succcess.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>
登录成功
</h1>
</body>
</html>
package org.raylene.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
String password= req.getParameter("password");
String[] hobbies = req.getParameterValues("hobby");
System.out.println("---------------");
System.out.println(username+":"+password);
System.out.println(Arrays.toString(hobbies));
System.out.println("==========================");
// resp.sendRedirect("/request_war/succcess.jsp");
//请求转发
req.setCharacterEncoding("utf-8");
req.getRequestDispatcher("./succcess.jsp").forward(req,resp);
}
}