Servlet
Servlet是sun公司开发的处理动态web的技术
Servlet有两个默认的实现类:HttpServlet、GenericServlet
开发Servlet程序的步骤:
- 编写一个类,实现Servlet接口
- 把开发好的Java类部署到web服务器中
实现了Servlet接口的java程序称为Servlet
创建HelloServlet项目
- 构建一个普通的maven项目,删除src文件夹,这个空的工程作为maven主工程
引入servlet jsp依赖到pom.xml
javax.servlet javax.servlet-api 4.0.1 javax.servlet.jsp javax.servlet.jsp-api 2.3.3 或手动下载jar包到文件夹中
依赖下载地址:https://mvnrepository.com/
- 新建Module子模块servlet01(maven-archetype-webapp)
父项目中的java子项目可以直接使用,反之不可以
问题1:子项目pom中没有parent标签
解决:在子项目的pom.xml中手动添加parent标签并刷新maven
cn.itxiaoma HelloMaven 1.0-SNAPSHOT 问题2:子项目pom文件有一条横线并变灰
解决:file -->setting–>maven–>Ignored Files 将清单中对应项目的pom.xml文件取消选中
将servlet01中的web.xml改为最新,在main目录下新建java和resources
- 在java目录下新建package包cn..itxiaoma.servlet
在包下新建java类HelloServlet,并实现Servlet接口(继承HttpServlet)
package cn.itxiaoma.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.io.PrintWriter; public class HelloServlet extends HttpServlet { //get和post只是请求实现的不同方式,业务逻辑一样,因此可以互相调用 @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { PrintWriter printWriter = resp.getWriter(); printWriter.print("Hello Servlet"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }
编写Servlet的映射
浏览器连接的是web服务,因此需要在web服务中注册我们写的Servlet,并配置浏览器能够访问的路径
hello cn.itxiaoma.servlet.HelloServlet hello /hello 配置Tomcat
Run > Edit Configurations > + > Tomcat Server - Local > Application server:选择Tomcat目录
Deployment > + > servlet01:war exploed
Application Context: /servlet01_war_exploded
启动测试
http://localhost:8081/servlet...
Servlet原理
- 浏览器发送http请求(service)到web容器(Tomcat)
- 首次访问的请求会加载Servlet类到内存
- web容器生成请求(Request)和响应(Response)两个对象,并调用Servlet的service方法
- Request会从service拿到请求,并执行我们重写的处理方法,得到响应信息传给Response
web容器读取响应信息传回浏览器
Servlet Mapping问题
指定映射路径
hello /hello 多映射路径
hello /hello/* 默认请求路径(会覆盖首页)
hello /* 指定后缀/前缀
hello *.test 注:自定义后缀/前缀 * 前不能有映射路径
自定义异常处理ErrorServlet
cn/itxiaoma/servlet/ErrorServlet.java
package cn.itxiaoma.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.io.PrintWriter; public class ErrorServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/html"); resp.setCharacterEncoding("utf-8"); PrintWriter pw = resp.getWriter(); pw.print("
404
"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }src/main/webapp/WEB-INF/web.xml
hello cn.itxiaoma.servlet.HelloServlet hello /hello error cn.itxiaoma.servlet.ErrorServlet error /* 优先级问题:制定了固有的映射路径优先级最高,找不到再走默认的映射
ServletContext对象
web容器在启动的时候,它会为每个web程序创建一个ServletContext对象,代表当前的web应用
1. 共享数据
在一个Servlet中保存的数据,可以在其他Servlet中获取
web.xml
get cn.itxiaoma.servlet.GetServlet get /get HelloServlet.java
package cn.itxiaoma.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 HelloServlet extends HttpServlet { //get和post只是请求实现的不同方式,业务逻辑一样,因此可以互相调用 @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext context= this.getServletContext(); String username = "Apple"; context.setAttribute("username", username); System.out.println(username); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }
GetServlet.java
package cn.itxiaoma.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.PrintWriter; public class GetServlet extends HttpServlet { //get和post只是请求实现的不同方式,业务逻辑一样,因此可以互相调用 @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext context = this.getServletContext(); PrintWriter printWriter = resp.getWriter(); resp.setContentType("text/html"); resp.setCharacterEncoding("utf-8"); printWriter.print("Username:" + context.getAttribute("username")); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }
2. 获取初始化参数
web.xml
url jdbc:mysql://localhost:3306/testdb HelloServlet.java
package cn.itxiaoma.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.PrintWriter; public class HelloServlet extends HttpServlet { //get和post只是请求实现的不同方式,业务逻辑一样,因此可以互相调用 @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext context= this.getServletContext(); String url = context.getInitParameter("url"); resp.getWriter().print(url); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }
3. 请求转发
ServletContext context= this.getServletContext(); RequestDispatcher requestDispatcher = context.getRequestDispatcher("/gp");//转发的请求路径 requestDispatcher.forward(req, resp);
4. 读取资源文件
resources文件夹下新建db.properties(会打包到classes路径下,称为classpath)
username=root password=123456
读取资源文件
ServletContext context= this.getServletContext(); InputStream inputStream = context.getResourceAsStream("/WEB-INF/classes/db.properties"); Properties properties = new Properties(); properties.load(inputStream); String username = properties.getProperty("username"); String password = properties.getProperty("password"); resp.getWriter().print(username + password);
HttpServletResponse
web服务器接收到客户端的http请求,针对这个请求,分表创建一个代表请求的HttpServletRequest对象,和一个代表响应的HttpServletResponse对象
javax/servlet/ServletResponse.java
负责向浏览器发送数据的方法
public ServletOutputStream getOutputStream() throws IOException; public PrintWriter getWriter() throws IOException;
负责向浏览器发送响应头的方法
public void setCharacterEncoding(String charset); public void setContentLength(int len); public void setContentLengthLong(long len); public void setContentType(String type);
常见应用
- 向浏览器输出消息
下载文件
- 获取下载文件路径,文件名
- 获取下载文件的输入流
- 创建缓冲区
- 获取OutputStream对象
- 将FileOutputStream流写入Buffer缓冲区
- 使用OutputStream将缓冲区中的数据输出到客户端
String realPath = "/Users/4wheels/Downloads/test.jpg"; String filename = realPath.substring(realPath.lastIndexOf("/") + 1); resp.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8")); //获取文件输入流 FileInputStream inputStream = new FileInputStream(realPath); //缓冲区 int len = 0; byte[] buffer = new byte[1024]; ServletOutputStream servletOutputStream = resp.getOutputStream(); while ((len = inputStream.read(buffer)) > 0) { servletOutputStream.write(buffer, 0, len); } inputStream.close(); servletOutputStream.close();
验证码功能
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //浏览器3秒自动刷新 resp.setHeader("refresh", "3"); //在内存中创建图片 BufferedImage image = new BufferedImage(80, 20, BufferedImage.TYPE_INT_RGB); //绘制图片 Graphics2D g = (Graphics2D) image.getGraphics(); g.setColor(Color.white); g.fillRect(0, 0, 80, 20);//绘制填色矩形 g.setColor(Color.BLACK); g.setFont(new Font(null, Font.BOLD, 20)); g.drawString(makeNum(), 2, 20); resp.setContentType("image/jpeg"); resp.setDateHeader("expires", -1);//浏览器不缓存 resp.setHeader("Cache-COntrol", "no-cache"); resp.setHeader("Pragma", "no-cache"); 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++) { sb.append("0"); } num = sb.toString() + num; return num; }
重定向
resp.sendRedirect("/test");
HttpServletRequest
处理登录请求
web.xml
login cn.itxiaoma.servlet.LoginServlet login /login LoginServlet.java
package cn.itxiaoma.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 { //get和post只是请求实现的不同方式,业务逻辑一样,因此可以互相调用 @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[] hobby = req.getParameterValues("hobby"); System.out.println(username); System.out.println(password); System.out.println(Arrays.toString(hobby)); // req.getRequestDispatcher("/success.jsp").forward(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }
Cookie
- cookie只能保存字符串,不能保存对象
- 一个cookie只能保存一个信息,最大4kb
- 一个web站点可以给浏览器发送多个cookie,一个站点最多20个
- 浏览器最多存300个左右cookie
cookie不设置有效期或设为0,关闭浏览器自动失效
req.setCharacterEncoding("utf-8"); resp.setCharacterEncoding("utf-8"); resp.setContentType("text/html;charset=utf-8"); PrintWriter printWriter = resp.getWriter(); Cookie[] cookies = req.getCookies(); if (cookies != null) { printWriter.print("Last Time:"); for (int i = 0; i < cookies.length; i++) { Cookie cookie = cookies[i]; if (cookie.getName().equals("lastLoginTime")) { long lastLoginTime = Long.parseLong(cookie.getValue()); Date date = new Date(lastLoginTime); printWriter.write(date.toLocaleString()); } } } else { printWriter.print("First Visit."); } Cookie cookie = new Cookie("lastLoginTime", System.currentTimeMillis()+""); resp.addCookie(cookie);
Session
服务器会给每个用户(浏览器)创建一个Session,只有浏览器没有关闭,session就存在
Session和Cookie的区别:Cookie由客户端创建在浏览器保存,Session在服务器端创建和保存
Session和ServletContext区别:ServletContext作用域是全局(Web应用),Session作用域是会话(浏览器)
setSession cn.itxiaoma.servlet.SetSession setSession /set getSession cn.itxiaoma.servlet.GetSession getSession /get delSession cn.itxiaoma.servlet.DelSession delSession /del 15 SetSession.java
req.setCharacterEncoding("utf-8"); resp.setCharacterEncoding("utf-8"); resp.setContentType("text/html;charset=utf-8"); PrintWriter printWriter = resp.getWriter(); HttpSession session = req.getSession(); session.setAttribute("name", "test"); String sessionId = session.getId(); if (session.isNew()) { printWriter.write("Session创建成功,Id:" + sessionId); } else { printWriter.write("Session已存在,Id:" + sessionId); }
GetSession.java
req.setCharacterEncoding("utf-8"); resp.setCharacterEncoding("utf-8"); resp.setContentType("text/html;charset=utf-8"); PrintWriter printWriter = resp.getWriter(); HttpSession session = req.getSession(); String sessionId = session.getId(); String sessionName = (String) session.getAttribute("name"); printWriter.write("Session Id:" + sessionId); printWriter.write("Session Name:" + sessionName);
DelSession.java
HttpSession session = req.getSession(); //删除指定session session.removeAttribute("name"); //手动注销session