Servlet
Servlet是sun公司开发的解决动静web的技术
Servlet有两个默认的实现类:HttpServlet、GenericServlet
开发Servlet程序的步骤:
- 编写一个类,实现Servlet接口
- 把开发好的Java类部署到web服务器中
实现了Servlet接口的java程序称为Servlet
创立HelloServlet我的项目
- 构建一个一般的maven我的项目,删除src文件夹,这个空的工程作为maven主工程
引入servlet jsp依赖到pom.xml
<dependencies> <!--增加Servlet和JSP依赖--> <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>
或手动下载jar包到文件夹中
依赖下载地址:https://mvnrepository.com/
- 新建Module子模块servlet01(maven-archetype-webapp)
父我的项目中的java子项目能够间接应用,反之不能够
问题1:子项目pom中没有parent标签
解决:在子项目的pom.xml中手动增加parent标签并刷新maven
<parent> <groupId>cn.itxiaoma</groupId> <artifactId>HelloMaven</artifactId> <version>1.0-SNAPSHOT</version></parent>
问题2:子项目pom文件有一条横线并变灰
解决:file -->setting–>maven–>Ignored Files 将清单中对应我的项目的pom.xml文件勾销选中
将servlet01中的web.xml改为最新,在main目录下新建java和resources
<?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>
- 在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,并配置浏览器可能拜访的门路
<!-- 注册Servlet--><servlet> <servlet-name>hello</servlet-name> <servlet-class>cn.itxiaoma.servlet.HelloServlet</servlet-class></servlet><!-- Servlet的申请门路--><servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern></servlet-mapping>
配置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问题
指定映射门路
<servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern></servlet-mapping>
多映射门路
<servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello/*</url-pattern></servlet-mapping>
默认申请门路(会笼罩首页)
<servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/*</url-pattern></servlet-mapping>
指定后缀/前缀
<servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>*.test</url-pattern></servlet-mapping>
注:自定义后缀/前缀 * 前不能有映射门路
自定义异样解决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("<h1>404</h1>"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); }}
src/main/webapp/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_4_0.xsd" version="4.0" metadata-complete="true"> <servlet> <servlet-name>hello</servlet-name> <servlet-class>cn.itxiaoma.servlet.HelloServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping> <servlet> <servlet-name>error</servlet-name> <servlet-class>cn.itxiaoma.servlet.ErrorServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>error</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping></web-app>
优先级问题:制订了固有的映射门路优先级最高,找不到再走默认的映射
ServletContext对象
web容器在启动的时候,它会为每个web程序创立一个ServletContext对象,代表以后的web利用
1. 共享数据
在一个Servlet中保留的数据,能够在其余Servlet中获取
web.xml
<servlet> <servlet-name>get</servlet-name> <servlet-class>cn.itxiaoma.servlet.GetServlet</servlet-class></servlet><servlet-mapping> <servlet-name>get</servlet-name> <url-pattern>/get</url-pattern></servlet-mapping>
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
<context-param> <param-name>url</param-name> <param-value>jdbc:mysql://localhost:3306/testdb</param-value></context-param>
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=rootpassword=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
解决登录申请
<html><body><form action="${pageContext.request.contextPath}/login" method="post"> Username<input type="text" name="username"/><br> Password<input type="password" name="password"/><br> Hobby<input type="checkbox" name="hobby" value="1"/> <input type="checkbox" name="hobby" value="2"/> <input type="checkbox" name="hobby" value="3"/> <input type="submit"/></form></body></html>
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"> <servlet> <servlet-name>login</servlet-name> <servlet-class>cn.itxiaoma.servlet.LoginServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>login</servlet-name> <url-pattern>/login</url-pattern> </servlet-mapping></web-app>
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作用域是会话(浏览器)
<servlet> <servlet-name>setSession</servlet-name> <servlet-class>cn.itxiaoma.servlet.SetSession</servlet-class></servlet><servlet-mapping> <servlet-name>setSession</servlet-name> <url-pattern>/set</url-pattern></servlet-mapping><servlet> <servlet-name>getSession</servlet-name> <servlet-class>cn.itxiaoma.servlet.GetSession</servlet-class></servlet><servlet-mapping> <servlet-name>getSession</servlet-name> <url-pattern>/get</url-pattern></servlet-mapping><servlet> <servlet-name>delSession</servlet-name> <servlet-class>cn.itxiaoma.servlet.DelSession</servlet-class></servlet><servlet-mapping> <servlet-name>delSession</servlet-name> <url-pattern>/del</url-pattern></servlet-mapping><!-- 设置session默认生效工夫--><session-config> <!-- 15分钟后主动生效--> <session-timeout>15</session-timeout></session-config>
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();//删除指定sessionsession.removeAttribute("name");//手动登记session