Servlet就是sun公司开发动态web的一门技术
Sun在API中提供了一个接口:Servlet
,如果逆向开发一个Servlet程序,只需要完成两个小步骤
Servlet
接口把实现了Servlet
接口的Java程序叫做Servlet
Maven
项目删掉里面的Src
目录,这个空的工程就是Maven主工程
在 pom.xml
里面添加 servlet,jsp 依赖
Maven Repository
仓库里面搜索 javax-servlet-api 和 javax.servlet.jsp-api<dependencies> <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> </dependency> <!-- https://mvnrepository.com/artifact/javax.servlet.jsp/javax.servlet.jsp-api --> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>javax.servlet.jsp-api</artifactId> <version>2.3.3</version> </dependency> </dependencies>
Load Maven changes
新建Module
<modules> <module>servlet-01</module> </modules>
<parent> <artifactId>javaweb-02-maven</artifactId> <groupId>com.kuang</groupId> <version>1.0-SNAPSHOT</version> </parent>
父项目中的java子项目可以直接使用,意思其实就是子项目可以不用再次导入jar包,直接可以使用父项目导入的jar包
7.在 main 目录下新建Directory java
,resources
并且右键-Mark Directory as ,分别转换为java目录和资源目录
Maven环境优化
修改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" metadata-complete="true"> </web-app>
上面这一大串我们是从哪里来的呢?其实我们是在tomcat\webapps\ROOT\WEB-INF\web.xml
中与tomcat相对应的版本
为什么需要映射?
2.继承HttpServlet
public class HelloServlet extends HttpServlet { // get或者post只是请求实现的不同方式,可以互相调用,业务逻辑都一样 @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("进入doGet方法"); // 在这边重写完方法之后就去web.xml里面编写Servlet的映射 } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { super.doPost(req, resp); } }
3.编写Servlet的映射
<!-- 1.注册servlet--> <servlet> <servlet-name>hello</servlet-name> <servlet-class>com.kuang.servlet.HelloServlet</servlet-class> </servlet> <!-- servlet的请求路径--> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping>
/hello
<!--当我们这样配置映射路径的时候,我们访问的名称为:localhost:8080/s1/hello--> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping>
<!--当我们这样配置映射路径的时候,我们访问的名称为:localhost:8080/s1/hello--> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping> <!--当我们这样配置映射路径的时候,我们访问的名称为:localhost:8080/s1/hello2--> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello2</url-pattern> </servlet-mapping> <!--当我们这样配置映射路径的时候,我们访问的名称为:localhost:8080/s1/hello3--> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello3</url-pattern> </servlet-mapping> <!--当我们这样配置映射路径的时候,我们访问的名称为:localhost:8080/s1/hello4--> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello4</url-pattern> </servlet-mapping> <!--当我们这样配置映射路径的时候,我们访问的名称为:localhost:8080/s1/hello5--> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello5</url-pattern> </servlet-mapping>
<!--当我们这样配置映射路径的时候,我们访问的名称为:localhost:8080/s1/hello/****--> <!-- ****代表任何字母数字均可 --> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello/*</url-pattern> </servlet-mapping>
<!--默认请求路径--> <!--当我们这样配置映射路径的时候,我们访问的名称为:localhost:8080/s1/****--> <!-- ****代表任何字母数字均可 --> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping>
<!--可以自定义后缀实现请求映射--> <!--注意点,*前面不能加项目映射的路径--> <!--当我们这样配置映射路径的时候,我们访问的名称为:localhost:8080/s1/hello/****.qinjiang--> <!-- 例如:localhost:8080/s1/hello/sajdlkajda.qinjiang --> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>*.qinjiang</url-pattern> </servlet-mapping>
<servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping> <!--404--> <servlet> <servlet-name>error</servlet-name> <servlet-class>com.kuang.servlet.Error</servlet-class> </servlet> <servlet-mapping> <servlet-name>error</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping>
例如当我们配置上方映射时候,我们访问:localhost:8080/s1/hello ,思考此时是访问/*
还是访问/hello
呢?
/hello
概念:web容器在启动的时候,它会为每个web程序都创建一个对应的ServletContext对象,它代表了当前的web应用
白话:代表整个web应用,可以和程序的容器(服务器)来通信
ServletContext
对象拿到的
context.setAttribute(String name,Object value)
context.getAttribute(String name)
HelloServlet.java
public class HelloServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext context = this.getServletContext(); //数据 String username = "秦疆"; //将一个数据保存在了ServletContext中,名字为:username 。值 username context.setAttribute("username",username); } }
<!-- 存放username数据 --> <servlet> <servlet-name>hello</servlet-name> <servlet-class>com.kuang.servlet.HelloServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping>
GetServlet.java
public class GetServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext context = this.getServletContext(); String username = (String) context.getAttribute("username"); resp.setContentType("text/html"); resp.setCharacterEncoding("utf-8"); // 将获取到的数据显示在网页中 resp.getWriter().print("名字"+username); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }
<!-- 获取username数据--> <servlet> <servlet-name>getc</servlet-name> <servlet-class>com.kuang.servlet.GetServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>getc</servlet-name> <url-pattern>/getc</url-pattern> </servlet-mapping>
测试访问结果;
/hello
会存放数据/getc
会取出数据我们可以在web.xml
中配置一些web应用初始化参数,在ServletContext
对象中可以拿到它
GetInitParameter.java
/** * 获取初始化参数 */ public class GetInitParameter extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext context = this.getServletContext(); String url = context.getInitParameter("url"); // 将获取到的url打印到网页上 resp.getWriter().print(url); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { super.doPost(req, resp); } }
在web.xml中配置初始化参数和注册servlet映射为 /gp
<!-- 配置初始化参数 参数为url,值为 jdbc:mysql://localhost:3306/mybatis --> <context-param> <param-name>url</param-name> <param-value>jdbc:mysql://localhost:3306/mybatis</param-value> </context-param> <!-- 注册servlet映射 --> <servlet> <servlet-name>gp</servlet-name> <servlet-class>com.kuang.servlet.GetInitParameter</servlet-class> </servlet> <servlet-mapping> <servlet-name>gp</servlet-name> <url-pattern>/gp</url-pattern> </servlet-mapping> </web-app>
GetRequestDispatcher.java
/** * 请求转发 */ public class GetRequestDispatcher extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("进入了GetRequestDispatcher"); ServletContext context = this.getServletContext(); //RequestDispatcher requestDispatcher = context.getRequestDispatcher("/gp"); 转发的请求路径 //requestDispatcher.forward(req,resp); 调用forward实现请求转发; context.getRequestDispatcher("/gp").forward(req,resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { super.doPost(req, resp); } }
在web.xml中配置servlet映射为 /gr
<!-- 请求转发 --> <servlet> <servlet-name>gr</servlet-name> <servlet-class>com.kuang.servlet.GetRequestDispatcher</servlet-class> </servlet> <servlet-mapping> <servlet-name>gr</servlet-name> <url-pattern>/gr</url-pattern> </servlet-mapping>
原因:我们请求/gr
,但是/gr
通过getRequestDispatcher
转发到另外一个页面,但是我们访问的路径不变,依旧是localhost:8080/s2/gr
请求转发的特点:
请求转发:A想要拿到C中的资源,但是A没法与C连接,A只能与B连接,于是A对B说,B你去C里面给我拿,B就与C建立连接,C把资源给B,B再把资源给A
重定向:A连接B说想要拿到C中的资源,B对A说,你去连接C然后拿资源
db.properties
username=root12312 password=zxczxczxc
PropertiesServlet.java
/** * 读取资源文件properties */ public class PropertiesServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext context = this.getServletContext(); // 思路:需要一个文件流 // 这里的路径: InputStream is = context.getResourceAsStream("/WEB-INF/classes/db.properties"); Properties prop = new Properties(); prop.load(is); String username = prop.getProperty("username"); String password = prop.getProperty("password"); resp.getWriter().print(username +":"+password ); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { super.doPost(req, resp); } }
<!-- 获取资源文件properties --> <servlet> <servlet-name>pr</servlet-name> <servlet-class>com.kuang.servlet.PropertiesServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>pr</servlet-name> <url-pattern>/pr</url-pattern> </servlet-mapping>
web服务器接收到客户端的http请求,针对这个请求,分别创建一个代表请求HttpServletRequest
对象,代表响应的一个HttpServletResponse
对象
HttpServletRequest
HttpServletResponse
HttpServletrequest对象和HttpServleresponse对象的原理
接收到客户端的请求后,可以通过 HttpServletResponse 对象直接进行响应,响应时需要获取输出流,服务器输出字符数据到浏览器有两种方法:
步骤:
两种形式
resp.getWriter()
:获取字符流(只能响应回字符)resp.getOutputStream()
:获取字节流(能响应一切数据)注意:两者不能同时使用
// 字符输出流(输出字符串) PrintWriter writer = resp.getWriter(); writer.write("hello"); writer.write("<h2>Hello</h2>");
// 字节输出流(输出一切数据) ServletOutputStream out = resp.getOutputStream(); out.write("hello".getBytes()); out.write("<h2>Hello</h2>".getBytes());
为什么不能带标签式的输出呢?是因为我们没有指定编码,要使服务器输出字符数据到浏览器,必须指定编码,否则就按字符串格式显示在浏览器上
resp.setContentType("text/html;charset=UTF-8"); PrintWriter writer = resp.getWriter(); writer.write("hello"); writer.write("<h1>hello</h1>");
我们加上指定编码,再次访问此时:
resp.setContentType("text/html;charset=UTF-8");
在响应中,如果我们响应的内容中含有中文,则有可能出现乱码,这是因为服务器响应的数据也会经过网络传输,服务器有一种编码方式,在客户端也存在一种编码方式。当两端使用的编码方式不同时则会出现乱码。
要解决该种乱码只能在服务器端告知服务器使用一种能够支持中文的编码格式,比如我们通常使用的"UTF-8"
// 指定服务器编码 resp.setCharacterEncoding("UTF-8"); // 指定客户端编码 resp.setHeader("Content-type","text/html;charset=UTF-8");
resp.setContentType("text/html;charset=UTF-8");
示例:
resp.setContentType("text/html;charset=UTF-8"); // 得到字符输出流 PrintWriter writer = resp.getWriter(); writer.write("<h2>你好</h2>");
访问测试:
一个web资源B收到客户端A的请求后,它B会通知A客户端去访问另一个web资源C,这个过程叫重定向
常见场景:
void sendRedirect(String var1);
RedirectServlet.java
/** * 实现重定向 */ public class RedirectServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { /* 重定向的原理 resp.setHeader("Location","/r/img"); resp.setStatus(302); */ resp.sendRedirect("/r/img"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { super.doPost(req, resp); } }
<!-- 重定向redirect注册映射 '/red'--> <servlet> <servlet-name>redirect</servlet-name> <servlet-class>com.kuang.servlet.RedirectServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>redirect</servlet-name> <url-pattern>/red</url-pattern> </servlet-mapping>
请求转发(req.getRequestDispatcher().forward()) | 重定向(resp.sendRedirect()) |
---|
请求转发的地址栏不会发送改变,重定向的地址栏会发送改变
请求转发只有一次请求,重定向有两次请求
请求转发 request 对象可共享,重定向时 request 对象不共享
请求转发是服务器行为,重定向是客户端行为
请求转发时的地址只能是当前站点(当前项目)的资源,重定向时可以是任何地址
两者都可进行跳转,根据实际需求选取即可
resp.setHeader("Location","/r/img"); resp.setStatus(302);
index.jsp
<html> <body> <h2>Hello World!</h2> <%--这里提交的路径,需要寻找到项目的路径--%> <%--${pageContext.request.contextPath}代表当前的web项目--%> <form action="${pageContext.request.contextPath}/login" method="get"> 用户名:<input type="text" name="username"><br> 密码:<input type="password" name="password"><br> <input type="submit"> </form> </body> </html>
RequestTest.java
/** * 简单的登录小案例 */ public class RequestTest extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 设置utf-8编码,网站不乱码 resp.setCharacterEncoding("utf-8"); // 获取index.jsp中的参数 String username = req.getParameter("username"); String password = req.getParameter("password"); System.out.println(username + ":" + password); // 重定向,注意路径问题,否则会404 resp.sendRedirect("/r/success.jsp"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { super.doPost(req, resp); } }
<servlet> <servlet-name>request</servlet-name> <servlet-class>com.kuang.servlet.RequestTest</servlet-class> </servlet> <servlet-mapping> <servlet-name>request</servlet-name> <url-pattern>/login</url-pattern> </servlet-mapping>
控制台输出用户名和密码
HttpServletRequest代表客户端的请求,用户通过Http协议访问服务器,HTTP请求中的所有信息会被封装到HttpServletRequest中
HttpServletRequest对象:主要作用是用来接收客户端发送过来的请求信息
例如:请求的参数,发送的头信息等都属于客户端发来的信息
String getContextPath()
String getServletPath()
String getRequestURI():
/s1/helloStringBuffer getRequestURL()
http://localhost/s1/helloreq.getParameter(String name)
:根据参数名称获取参数值
req.getParameterValues(String name)
:根据参数名称获取参数值的数组
req.setCharacterEncoding("utf-8")
get方式:tomcat 8 已经将get方式乱码问题解决了
post方式:会乱码
req.setCharacterEncoding("utf-8")
req.setCharacterEncoding("utf-8");
请求转发,是一种服务器的行为,当客户端请求到达后,服务器进行转发,此时会将请求对象进行保存,地址栏中的URL地址不会变,得到相应后,服务器再将响应发送给客户端,从始至终只有一个请求发出,request数据可以共享
步骤:
getRequestDispatcher(String path)
forward(ServletRequest request, ServletResponse response)
req.getRequestDispatcher("/gp").forward(req,resp);
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请求--%> <%-- ${pageContext.request.contextPath}代表我们的web项目--%> <form action="${pageContext.request.contextPath}/login" method="post"> 用户名:<input type="text" name="username"><br> 密码:<input type="password" name="password"><br> 爱好: <input type="checkbox" name="hobbies" value="女孩">女孩 <input type="checkbox" name="hobbies" value="代码">代码 <input type="checkbox" name="hobbies" value="唱歌">唱歌 <input type="checkbox" name="hobbies" value="电影">电影 <br> <input type="submit"> </form> </div> </body> </html>
LoginServlet.java
public class LoginServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("utf-8"); resp.setCharacterEncoding("utf-8"); String username = req.getParameter("username"); String password = req.getParameter("password"); String[] hobbies = req.getParameterValues("hobbies"); System.out.println("----------------"); System.out.println(username); System.out.println(password); System.out.println(Arrays.toString(hobbies)); System.out.println("----------------"); // 通过请求转发 // 这里的 / 代表当前的web应用 req.getRequestDispatcher("/success.jsp").forward(req,resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }