Servlet简介
- Servlet就是sun公司开发动态web的一门技术
- Sun在这些APi中提供一个接口叫做:Servlet,如果你想开发一个Servlet程序,只需要完成两个小步骤:
- 编写一个类,实现Serlet接口
- 把开发好java类部署到web服务器中。
把实现了Servlet接口的Java程序叫做,Servlet
HelloServlet
Serlvet接口Sun公司有两个默认的实现类:HttpServlet,GenericServled
HelloServlet
- 构建一个普通的Maven项目,删掉里面的src目录,以后我们的学习就在这个项目里面建立Moudel;这个空的工程就题Maven主工程;
在pom.xml里面放依赖
https://mvnrepository.com/artifact/javax.servlet/javax.servlet-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><scope>provided</scope> 可以删掉</dependency></dependencies>
JavaServer Page jsp依赖
https://mvnrepository.com/artifact/javax.servlet.jsp/javax.servlet.jsp-api/2.3.3
<!-- 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><scope>provided</scope></dependency>
关于Maven父子工程的理解;
父项目中会有<modules><module>servlet-01</module></modules>
子项目会有
<parent><artifactId>javaweb-02-servlet</artifactId><groupId>com.kuang</groupId><version>1.0-SNAPSHOT</version></parent>
父项目中的java子项目可以直接使用
son extends father
Maven环境优化
- 修改web.xml为最新的 ```
<?xml version=”1.0” encoding=”UTF-8”?>
1. 将maven的结构搭建完整.4. 编写一个Servlet程序1. 编写一个普通类1. 实现Servlet接口,这里我们直接继承HttpServlet
public class HelloServlet extends HttpServlet {
//由于get或者post只是请求实现的不同的方式,可以相互调用,业务逻辑都一样;@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//ServletOutputStream outputStream = resp.getOutputStream();PrintWriter writer = resp.getWriter(); //响应流writer.print("Hello,Serlvet");}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doGet(req, resp);}}
5. 编写Servlet的映射<br />为什么需要映射:我们写的是JAVA程序,但是要通过浏览器访问,而浏览器需要连接web服务器,所以我们需<br />要再web服务中注册我们写的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>
6. 配置Tomcat<br />注意:配置项目发布的路径就可以了<br /><br />6. 启动测试,OK!<br /><a name="pM5eE"></a>## Servlet原理Servlet是由Web服务器调用,web服务器在收到浏览器请求之后,会:<br /><a name="hjTWj"></a>## Mapping问题1. 一个Servlet可以指定一个映射路径<br />.
2. 一个servlet可以指定多个映射路径
3. 一个servlet可以指定通用映射路径
4. 默认请求路径
<servlet-mapping><servlet-name>hello</servlet-name><url-pattern>/*</url-pattern></servlet-mapping>
5. 指定一些后缀或者前缀等等…
6. 优先级问题<br />指定了固有的映射路径优先级最高,如果找不到就会走默认的处理请求;
<a name="3BVu2"></a># ServletConfig<a name="DMlax"></a>## ServletConfig对象有什么用?通过此对象可以**读取web.xml中**配置的**初始化参数**。<a name="PXVXw"></a>## 🙄why?**能够让你的程序更加灵活**【更换需求,更改配置文件web.xml即可,程序代码不用改】<a name="Flmvo"></a>## 获取web.xml文件配置的参数信息- 为Demo1这个Servlet配置一个参数,参数名是name,值是zhongfucheng```xml<servlet><servlet-name>Demo1</servlet-name><servlet-class>zhongfucheng.web.Demo1</servlet-class><init-param><param-name>name</param-name><param-value>zhongfucheng</param-value></init-param></servlet><servlet-mapping><servlet-name>Demo1</servlet-name><url-pattern>/Demo1</url-pattern></servlet-mapping>
在Servlet中获取ServletConfig对象,通过ServletConfig对象获取在web.xml文件配置的参数 ```java public class servletConfig extends HttpServlet { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取ServletConfig对象ServletConfig servletConfig = this.getServletConfig();//根据配置的名字获取值String name = servletConfig.getInitParameter("name");System.out.println(name);
} }
<a name="ServletContext"></a># ServletContext<a name="ciijZ"></a>## 共享数据- 在一个Servlet中,可以在另一个Servlet中拿到,实现了Servlet之间的通信```javapublic class HelloServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("Hello");//this.getInitParameter() 初始化参数//this.getServletConfig() Servlet配置//this.getServletContext() Servlet上下文ServletContext context1 = this.getServletContext();//将一个数据保存到 ServletContext中,名字为:username,值为usernameresp.setContentType("text/html");resp.setCharacterEncoding("UTF-8");String username = "shilin.z";context1.setAttribute("username",username);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doGet(req, resp);}}
public class GetServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {ServletContext context2 = this.getServletContext();String username = (String) context2.getAttribute("username");resp.setContentType("text/html");resp.setCharacterEncoding("UTF-8");resp.getWriter().print("s1传输的内容: " + username);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doGet(req, resp);}}
<servlet><servlet-name>hello</servlet-name><servlet-class>edu.cqupt.servlet.HelloServlet</servlet-class></servlet><servlet-mapping><servlet-name>hello</servlet-name><url-pattern>/hello</url-pattern></servlet-mapping><servlet><servlet-name>getc</servlet-name><servlet-class>edu.cqupt.servlet.GetServlet</servlet-class></servlet><servlet-mapping><servlet-name>getc</servlet-name><url-pattern>/getc</url-pattern></servlet-mapping>
测试结果:
先 http://localhost:8080/s2/hello, 不然输出为null
获取初始化参数
<!-- 配置一些web应用的初始化参数--><context-param><param-name>url</param-name><param-value>jdbc:mysql://local:3306/mybatis</param-value></context-param><servlet><servlet-name>getp</servlet-name><servlet-class>edu.cqupt.servlet.Servlet03</servlet-class></servlet><servlet-mapping><servlet-name>getp</servlet-name><url-pattern>/getp</url-pattern></servlet-mapping>
@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {ServletContext context = this.getServletContext();String url = context.getInitParameter("url");resp.getWriter().print(url);}
请求转发(不是重定向)

@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("进入了Servlet04");ServletContext context = this.getServletContext();//RequestDispatcher requestDispatcher = context.getRequestDispatcher("/getp"); //转发的请求路径//requestDispatcher.forward(req,resp); //调用forward实现请求转发context.getRequestDispatcher("/getp").forward(req,resp);}
<servlet><servlet-name>sd4</servlet-name><servlet-class>edu.cqupt.servlet.Servlet04</servlet-class></servlet><servlet-mapping><servlet-name>sd4</servlet-name><url-pattern>/sd4</url-pattern></servlet-mapping>
测试结果:
读取资源文件
- Properties类
- 在java目录下新建properties
- 在resources目录下新建properties
- 发现:都被打包到了同一路径下:classes,我们俗称路径为classpath

需要在本项目的pom.xml中配置resources
<!-- 在bulid中配置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>
需要一个文件流:
Servlet05.java
public class Servlet05 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//获得文件流InputStream is = this.getServletContext().getResourceAsStream("/WEB-INF/classes/db.properties");Properties prop = new Properties();prop.load(is);String user = prop.getProperty("username");String pwd = prop.getProperty("password");resp.getWriter().print(user + ":" + pwd);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doGet(req, resp);}}
db.properties
username=rootpassword=123456
web.xml
<servlet><servlet-name>sd5</servlet-name><servlet-class>edu.cqupt.servlet.Servlet05</servlet-class></servlet><servlet-mapping><servlet-name>sd5</servlet-name><url-pattern>/sd5</url-pattern></servlet-mapping>
HttpServletResponse
web服务器接收到客户端的http请求,针对这个请求,分别创建一个代表请求的HttpServletRequest对象,一个代表响应的HttpServletResponse对象;
- 如果要获取客户端请求过来的参数:找HttpServletRequset对象
如果要给客户端响应一些信息: 找HttpServletResponse对象
简单分类
负责向浏览器发送数据的方法
ServletOutputStream getOutputStream() throws IOException;PrintWriter getWriter() throws IOException;
负责向浏览器发送响应头的方法
void setCharacterEncoding(String var1);void setContentLength(int var1);void setContentLengthLong(long var1);void setContentType(String var1);void setDateHeader(String var1, long var2);void addDateHeader(String var1, long var2);void setHeader(String var1, String var2);void addHeader(String var1, String var2);void setIntHeader(String var1, int var2);void addIntHeader(String var1, int var2);
响应的状态码
HttpServletResponse定义了很多状态码的常量(具体可以查看Servlet的API),当需要向客户端发送响应状态码时,可以使用这些常量,避免了直接写数字,常见的状态码对应的常量:
int SC_CONTINUE = 100;int SC_OK = 200;int SC_MULTIPLE_CHOICES = 300;int SC_NOT_FOUND = 404;int SC_INTERNAL_SERVER_ERROR = 500;.....
常见应用
向浏览器输出消息
使用PrintWriter流输出中文注意问题:
在获取PrintWriter输出流之前首先使用
response.setCharacterEncoding(charset)设置字符以什么样的编码输出到浏览器- 如:
response.setCharacterEncoding("UTF-8");设置将字符以”UTF-8”编码输出到客户端浏览器,然后再使用response.getWriter();获取PrintWriter输出流,这两个步骤不能颠倒 ```java response.setCharacterEncoding(“UTF-8”);//设置将字符以”UTF-8”编码输出到客户端浏览器 /**
- PrintWriter out = response.getWriter();这句代码必须放在response.setCharacterEncoding(“UTF-8”);之后
- 否则response.setCharacterEncoding(“UTF-8”)这行代码的设置将无效,浏览器显示的时候还是乱码 */ PrintWriter out = response.getWriter();//获取PrintWriter输出流 ```
- 然后再使用
response.setHeader("content-type", "text/html;charset=字符编码");设置响应头,控制浏览器以指定的字符编码编码进行显示,例如:
```java /**//通过设置响应头控制浏览器以UTF-8的编码显示数据,如果不加这句话,那么浏览器显示的将是乱码response.setHeader("content-type", "text/html;charset=UTF-8");
- 多学一招:使用HTML语言里面的标签来控制浏览器行为,模拟通过设置响应头控制浏览器行为 *response.getWriter().write(““);
等同于response.setHeader(“content-type”, “text/html;charset=UTF-8”); */ response.getWriter().write(““); ```
下载文件
- 获取下载文件的路径
- 下载的文件名
- 设置想办法让浏览器能够支持下载我们需要的东西
- 获取下载文件的输入流
- 创建缓冲区
- 获取OutputStream对象
- 将FileOutputStream流写入到buffer缓冲区
- 使用OutputStream将缓冲区中的数据输出到客户端 ```java package com.dyq.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.要获取下载文件的路径 // target/classes/段.jpg String realPath = “C:\Users\DYQ\IdeaProjects\javaweb-02-servlet\response\target\classes\段.jpg”; System.out.println(“下载文件的路径”+realPath); //2.下载文件名是啥? String fileName = realPath.substring(realPath.lastIndexOf(“\“) + 1); //“\“是转义 //3.设置想办法让浏览器能够支持下在我们需要的东西,中文文件名URLEncoder.encode编码,否则有可能乱码 resp.setHeader(“Content-Disposition”,”attachment;filename=”+ URLEncoder.encode(fileName,”utf-8”)); //4.获取下载文件的输入流 FileInputStream in = new FileInputStream(realPath); //5.创建缓冲区 int len = 0; byte[] buffer = new byte[1024]; //6.获取OutputStream对象 ServletOutputStream out = resp.getOutputStream(); //7.将FileOutputStream流写入到buffer缓冲区 使用OutputStream将缓冲区中的数据输出到客户端 while (in.read(buffer)>0){ out.write(buffer,0,len); } in.close(); out.close(); }
@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {super.doGet(req, resp);}
}
<a name="DKCF2"></a>### 验证码功能**验证怎么来的?**- 前端实现- 后端实现,需要用到 java 的图片类,生成一个图片```javapublic class ImageServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//1. 如何让浏览器3秒自动刷新一次;resp.setHeader("refresh","3");//在内存中创建一个图片BufferedImage image = new BufferedImage(80, 20, BufferedImage.TYPE_3BYTE_BGR);//得到图片Graphics2D g = (Graphics2D)image.getGraphics();// 设置图片的背景颜色g.setColor(Color.white);g.fillRect(0,0,80,20);//给图片写数据g.setColor(Color.blue);g.setFont(new Font("宋体",Font.BOLD,20));g.drawString(makeNum(),0,20);// 告诉浏览器,这个请求用图片的方式打开resp.setContentType("image/jpeg");//网站存在缓存,不让浏览器缓存resp.setDateHeader("expires", -1);resp.setHeader("Cache-Control","no-cache");resp.setHeader("Pragma","no-cache");// 把图片写给浏览器boolean write = ImageIO.write(image, "jpg",resp.getOutputStream());}//生产随机数private String makeNum(){Random random = new Random();String num = random.nextInt(9999999) + "";StringBuffer sb = new StringBuffer();for (int i = 0; i < 7 - num.length() ; i++) { //保证生成的随机数只有7位sb.append("0"); //不足7位用0填充}num = sb.toString() + num;return num;}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doGet(req, resp);}}
<servlet><servlet-name>img</servlet-name><servlet-class>edu.cqupt.servlet.ImageServlet</servlet-class></servlet><servlet-mapping><servlet-name>img</servlet-name><url-pattern>/img</url-pattern></servlet-mapping>

实现重定向

B一个web资源收到客户端请求后,他会通知客户端去访问另外一个web资源,这个过程叫重定向。
常见场景:
- 用户登录:登录成功,跳转到另外的页面。 ```java public class RedirectServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// resp.setHeader(“Location”, “/s3/img”); // resp.setStatus(302); resp.sendRedirect(“/s3/img”); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }
<a name="J83bq"></a>## 🧐面试题:请求和重定向的区别?- 相同点- 页面都会跳转- 不同点- 请求转发,url地址不会发生变化 307- 重定向,url地址会发生变化 302```javapublic class RequestTestServlet extends HttpServlet {@Overrideprotected void doGet(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);// 重定向一定要注意,路径问题,否则就会404resp.sendRedirect("/s3/home.jsp");}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doGet(req, resp);}}
<%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head><title>Title</title></head><body><h1> Success ! </h1></body></html>
HttpServletRequest
HttpServletRequest对象代表客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP请求头中的所有信息都封装在这个对象中,通过这个对象提供的方法,可以获得客户端请求的所有信息。
常用方法
getRequestURL方法返回客户端发出请求时的完整URL。
getRequestURI方法返回请求行中的资源名部分。
getQueryString 方法返回请求行中的参数部分。
getPathInfo方法返回请求URL中的额外路径信息。额外路径信息是请求URL中的位于Servlet的路径之后和查询参数之前的内容,它以“/”开头。
getRemoteAddr方法返回发出请求的客户机的IP地址。
getRemoteHost方法返回发出请求的客户机的完整主机名。
getRemotePort方法返回客户机所使用的网络端口号。
getLocalAddr方法返回WEB服务器的IP地址。
getLocalName方法返回WEB服务器的主机名。
🙄获得客户机请求参数(客户端提交的数据)
- getParameter(String)方法(常用)
getParameterValues(String name)方法(常用)
public class LoginServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setCharacterEncoding("utf-8");req.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("---------------");//重定向// resp.sendRedirect("/s4/success.jsp");//通过请求转发System.out.println(req.getContextPath());//这里的/代表当前的web应用req.getRequestDispatcher("/success.jsp").forward(req,resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doGet(req, resp);}}
更多
Cookie、Session
会话
会话:用户打开一个浏览器,点击了很多web资源,访问多个web资源,关闭浏览器,这个过程就叫做会话。
有状态会话:客户端访问服务器,下次在访问服务器,服务器知晓客户端曾今访问过。
一个网站怎么证明用户访问过?
客户端 服务器
- 服务端给客户端一个cookie,客户端下次访问带上cookie就可以了
- 服务器通过session登记客户端访问过,下次客户端再次访问,服务器匹配客户端
保存会话的两种技术
cookie(发票)
- 客户端技术(响应、请求)
session(登记)
- 服务器技术:利用这个技术,可以保存用户的会话信息,我们可以把信息或者数据放在Session中
常见场景:
- 网站登录过后,下次不用登录,第二次访问直接就进去。
cookie
- 从请求中拿到cookie
服务器响应给客户端cookie
// 保存用户上一次访问的时间public class CookieDemo extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 服务器告诉你 ,你访问的时间,把这个时间封装成一个信件,下次访问的时候,需要带上信件req.setCharacterEncoding("gbk");resp.setCharacterEncoding("gbk");PrintWriter out = resp.getWriter();// 服务器端从客户端获取Cookie[] cookies = req.getCookies(); // cookie可能存在多个// 判断cookie是否存在if(cookies!=null){//如果存在out.write("你上次访问的时间是:");for (int i = 0; i <cookies.length ; i++) {Cookie cookie = cookies[i];// 获取cookie的名字if(cookie.getName().equals("lastLoginTime")){long lastLoginTime = Long.parseLong(cookie.getValue());Date date = new Date(lastLoginTime);out.write(date.toLocaleString());}System.out.println(cookie.getName());}}else{out.write("这是您第一次访问本网站。");}//服务器给客户端响应一个CookieCookie cookie = new Cookie("lastLoginTime", System.currentTimeMillis() + "");cookie.setMaxAge(24*60*60);resp.addCookie(cookie);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doGet(req, resp);}}
🍋Session(重点)

什么是Session:
服务器给每一个用户(浏览器)创建一个Session对象
- 一个Session独占一个浏览器,只要浏览器没有关闭,这个Session就存在
- 用户登录之后,整个网站都可以访问!
- 场景:保存用户的信息,保存购物车信息,在整个网站中经常会使用的数据,我们将它保存在session中
Session和Cookie的区别:**
- Cookie是把用户的数据写给用户得浏览器,浏览器保存(可以保存多个)
- Session把用户的数据写到用户独占的Session中,服务器端保存(保存重要的信息,减少服务器资源的浪费)
- Session对象由服务器创建
使用Session
public class SessionDemo extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 解决乱码问题resp.setHeader("content-type","text/html; charset=utf-8");req.setCharacterEncoding("utf-8");resp.setCharacterEncoding("utf-8");//得到sessionHttpSession session = req.getSession();//获取Session的IDString sessionId = session.getId();//判断session是否为新创建的if(session.isNew()){resp.getWriter().write("session 创建成功。session ID:" + sessionId );}else{resp.getWriter().write("已经在服务器中存在了。session ID:" + sessionId );}// Session创建的时候做了什么事情//Cookie cookie = new Cookie("JSESSIONID",sessionId);//resp.addCookie(cookie);//给Session中存字符串session.setAttribute("name","shilin.z");String name = (String) session.getAttribute("name");System.out.println(name);//给Session中存用户信息session.setAttribute("name",new Person("shilin.z",20));Person person = (Person) session.getAttribute("name");System.out.println(person.toString());//手动注销Session: 刷新,会重新生成sessionID//session.removeAttribute("name");//session.invalidate();//自动注销:在xml中配置}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doGet(req, resp);}}
<!-- 设置Session 默认的失效时间,以分钟为单位--><session-config><!-- 1分钟后失效 --><session-timeout>1</session-timeout></session-config>


