JavaWeb之Servlet,开发动态Web的技术
Servlet简介
Servlet其实就是一个遵循Servlet开发的java类。Servlet是由服务器调用的,运行在服务器端。
开发Servlet需要实现的步骤:
编写一个类实现Servlet接口
把开发好的Java类部署到Web服务器中
Tomcat下的Servlet案例
启动Tomcat,进入http://localhost:8080/examples/
可以看到Tomcat为我们提供的Examples
选择Servlets examples ,就可以看到实例以及源码
选择HelloWorld Example 源码可以看到
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import java.io.*;import javax.servlet.*;import javax.servlet.http.*;public class HelloWorld extends HttpServlet { public void doGet (HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { response.setContentType("text/html" ); PrintWriter out = response.getWriter(); out.println("<html>" ); out.println("<head>" ); out.println("<title>Hello World!</title>" ); out.println("</head>" ); out.println("<body>" ); out.println("<h1>Hello World!</h1>" ); out.println("</body>" ); out.println("</html>" ); } }
初使用Servlet
使用Maven将Servlet有关的包
去Maven资源库网站 寻找需要的包,搜索servlet api(搜索结果 )
已4.0.1为例 ,复制Maven格式的项目依赖到pom.xml中的<dependencies>...</dependencies>内
1 2 3 4 5 6 7 8 <dependency > <groupId > javax.servlet</groupId > <artifactId > javax.servlet-api</artifactId > <version > 4.0.1</version > <scope > provided</scope > </dependency >
在IEDA中刷新本地项目依赖的pom.xml,便会自动下载需要的依赖
创建Servlet项目
在HelloServlet文件中输入
1 2 3 public class HelloServlet extends HttpServlet {}
IDEA会进行自动导包
重写doGet和doPost方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public class HelloServlet extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/html" ); resp.setContentType("text/html" ); resp.setCharacterEncoding("utf-8" ); PrintWriter out = resp.getWriter(); out.println("<html>" ); out.println("<head>" ); out.println("<title>Hello World!</title>" ); out.println("</head>" ); out.println("<body>" ); out.println("<h1>Hello World!</h1>" ); out.println("</body>" ); out.println("</html>" ); } } @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { super .doGet(req, resp); } }
去webapp→WEB-INF→web.xml注册Servlet,将以下内容放入<web-app>...</web-app>内
1 2 3 4 5 6 7 8 9 10 11 12 <servlet > <servlet-name > helloServlet</servlet-name > <servlet-class > top.whl123456.servlet.HelloServlet</servlet-class > </servlet > <servlet-mapping > <servlet-name > helloServlet</servlet-name > <url-pattern > /hello</url-pattern > </servlet-mapping >
启动Tomcat,访问web.xml中设置的映射http://localhost:8080/hello 便可以进入到动态页面
到此的项目源码 已在GitHub中
HelloServlet
创建一个普通Maven项目,删除src文件夹
在pom.xml文件中添加依赖
1 2 3 4 5 6 7 8 <dependencies > <dependency > <groupId > javax.servlet</groupId > <artifactId > javax.servlet-api</artifactId > <version > 4.0.1</version > </dependency > </dependencies >
通过创建Module建立子模块
在父项目中的pom.xml中自动生成<modules>...</modules>
在子项目中的pom.xml中自动生成<parent>...</parent>
父项目中的jar包子项目可以直接使用,反之不可以
更换web.xml文件内容至最新内容
1 2 3 4 5 6 7 8 9 10 11 <?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 >
在main文件夹中建立java和resources文件夹,文件结构图如下所示
1 2 3 4 5 6 7 8 9 10 11 12 13 JavaWeb02 ├── JavaWeb02.iml ├── pom.xml └── servlet01 ├── pom.xml └── src └── main ├── java ├── resources └── webapp ├── index.jsp └── WEB-INF └── web.xml
创建一个简单的Servlet Demo
1 2 public class HelloServlet extends HttpServlet {}
Servlet有两个默认的实现类:HttpServlet,GenericServlet
重写doPost()和doGet()方法
IDEA中重写方法的快捷键是Ctrl+O
1 2 3 4 5 6 7 8 9 10 11 12 13 @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { PrintWriter writer = resp.getWriter(); writer.print("hello,servlet" ); } @Override protected void doPut (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); }
编写Servlet的映射,见上节配置
配置Tomcat,配置项目发布路径
对于artifact配置可参考,该博客
ServletContext
HttpServletResponse
Web服务器接收到客户端的Http请求,针对这个请求,分别创建一个代表请求的 HttpServletRequest对象,代表响应的一个 HttpServletResponse
获取客户端请求的参数:HttpServletRequest
给客户端响应一些信息:HttpServletResponse
1 2 3 public interface HttpServletResponse extends ServletResponse {}
分类
向浏览器发送数据的方法:
1 2 3 4 public interface ServletResponse { public ServletOutputStream getOutputStream () throws IOException ; public PrintWriter getWriter () throws IOException ; }
向浏览器发送响应头的方法
1 2 3 4 5 6 7 public interface ServletResponse { public void setCharacterEncoding (String charset) ; public void setContentLength (int len) ; public void setContentLengthLong (long len) ; public void setContentType (String type) ; }
1 2 3 4 5 6 7 8 public interface HttpServletResponse extends ServletResponse { public void setDateHeader (String name, long date) ; public void addDateHeader (String name, long date) ; public void setHeader (String name, String value) ; public void addHeader (String name, String value) ; public void setIntHeader (String name, int value) ; public void addIntHeader (String name, int value) ; }
状态码常量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 public interface HttpServletResponse extends ServletResponse { public static final int SC_CONTINUE = 100 ; public static final int SC_SWITCHING_PROTOCOLS = 101 ; public static final int SC_OK = 200 ; public static final int SC_CREATED = 201 ; public static final int SC_ACCEPTED = 202 ; public static final int SC_NON_AUTHORITATIVE_INFORMATION = 203 ; public static final int SC_NO_CONTENT = 204 ; public static final int SC_RESET_CONTENT = 205 ; public static final int SC_PARTIAL_CONTENT = 206 ; public static final int SC_MULTIPLE_CHOICES = 300 ; public static final int SC_MOVED_PERMANENTLY = 301 ; public static final int SC_MOVED_TEMPORARILY = 302 ; public static final int SC_FOUND = 302 ; public static final int SC_SEE_OTHER = 303 ; public static final int SC_NOT_MODIFIED = 304 ; public static final int SC_USE_PROXY = 305 ; public static final int SC_TEMPORARY_REDIRECT = 307 ; public static final int SC_BAD_REQUEST = 400 ; public static final int SC_UNAUTHORIZED = 401 ; public static final int SC_PAYMENT_REQUIRED = 402 ; public static final int SC_FORBIDDEN = 403 ; public static final int SC_NOT_FOUND = 404 ; public static final int SC_METHOD_NOT_ALLOWED = 405 ; public static final int SC_NOT_ACCEPTABLE = 406 ; public static final int SC_PROXY_AUTHENTICATION_REQUIRED = 407 ; public static final int SC_REQUEST_TIMEOUT = 408 ; public static final int SC_CONFLICT = 409 ; public static final int SC_GONE = 410 ; public static final int SC_LENGTH_REQUIRED = 411 ; public static final int SC_PRECONDITION_FAILED = 412 ; public static final int SC_REQUEST_ENTITY_TOO_LARGE = 413 ; public static final int SC_REQUEST_URI_TOO_LONG = 414 ; public static final int SC_UNSUPPORTED_MEDIA_TYPE = 415 ; public static final int SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416 ; public static final int SC_EXPECTATION_FAILED = 417 ; public static final int SC_INTERNAL_SERVER_ERROR = 500 ; public static final int SC_NOT_IMPLEMENTED = 501 ; public static final int SC_BAD_GATEWAY = 502 ; public static final int SC_SERVICE_UNAVAILABLE = 503 ; public static final int SC_GATEWAY_TIMEOUT = 504 ; public static final int SC_HTTP_VERSION_NOT_SUPPORTED = 505 ; }
常见应用
向浏览器输出消息
下载文件
获取下载文件的路径
下载文件的文件名
浏览器支持
获取下载文件的输入流
创建缓冲区
获取OutPutStream对象
将文件OutPutStream流写入到buffer缓冲区
使用OutPutStream将缓冲区中的数据输出到客户端
下载文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public class fileServlet extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String realPath = "E:\\Desktop\\JavaWeb02\\response\\src\\main\\resources\\1.png" ; System.out.println("下载文件的路径:" + realPath); String fileName = realPath.substring(realPath.lastIndexOf("\\" ) + 1 ); resp.setHeader("Content-Disposition" ,"attachment;filename=" + URLEncoder.encode(fileName,"utf-8" )); FileInputStream fileInputStream = new FileInputStream(realPath); int len = 0 ; byte [] buffer = new byte [1024 ]; ServletOutputStream outputStream = resp.getOutputStream(); while ((len=fileInputStream.read(buffer)) != -1 ){ outputStream.write(buffer,0 ,len); } fileInputStream.close(); outputStream.close(); } @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }
验证码功能
前端实现,js
后端实现,java图片类,生成一张图片
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setHeader("refresh" ,"3" ); BufferedImage bufferedImage = new BufferedImage(80 ,20 ,BufferedImage.TYPE_3BYTE_BGR); Graphics2D graphics = (Graphics2D) bufferedImage.getGraphics(); graphics.setColor(Color.white); 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.setDateHeader("expires" ,-1 ); resp.setHeader("Cache-Control" ,"no-cache" ); resp.setHeader("Pragma" ,"no-cache" ); boolean write = ImageIO.write(bufferedImage,"jpg" , resp.getOutputStream()); } private String makeNum () { Random random = new Random(); String num = random.nextInt(9999999 ) + "" ; StringBuffer stringBuffer = new StringBuffer(); for (int i = 0 ; i < 7 - num.length(); i++) { stringBuffer.append("0" ); } num = stringBuffer.toString() + num; return num; }
Response实现重定向
重定向:一个Web资源B收到客户端A的请求后,B通知客户端A访问另外一个web资源C的过程
使用场景:用户登录
1 void sendRedirect (String var1) throws IOException ;
1 2 3 4 @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.sendRedirect("/response_war/img" ); }
Status Code:302
Location:/response_war/img
实现原理
1 2 resp.setHeader("Location" ,"/response_war/img" ); resp.setStatus(302 );
重定向url会发生变化
转发url不会发生变化
1 2 3 4 5 6 7 8 String username = req.getParameter("username" ); String password = req.getParameter("password" ); System.out.println("username = " + username); System.out.println("password = " + password); resp.sendRedirect("/response_war/success.jsp" );
注意文件路径
HttpServletRequest
HttpServletRequest代表客户端的请求,用户通过Http协议访问服务器,Http请求中的所有信息会被封装到HttpServletRequest,通过这个 HttpServletRequest的方法,获得客户端的所有信息
获取前端传递的参数
1 2 String username = req.getParameter("username" ); String password = req.getParameter("password" );
请求转发
1 req.getRequestDispatcher("/success.jsp" ).forward(req,resp);
Cookie和Session
cookie
从请求中拿到 cookie信息
服务器响应给客户端 cookie
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 Cookie[] cookies = req.getCookies(); if (cookies != null ) { out.write("上次访问时间是:" ); for (int i = 0 ; i < cookies.length; i++) { Cookie cookie = cookies[i]; if (cookie.getName().equals("time" )){ long time = Long.parseLong(cookie.getValue()); Date date = new Date(time); out.write(date.toLocaleString()); } } }else { out.write("第一次访问" ); } Cookie cookie = new Cookie("time" , System.currentTimeMillis()+"" ); cookie.setMaxAge(24 *60 *60 ); resp.addCookie(cookie);
session
一次登录多次使用,保存用户信息
购物车信息
网站经常使用的信息
与Cookie的区别
Cookie是把用户的数据写给用户的浏览器,浏览器保存(可以保存多个)
Session把用户的数据写到用户独占 Session中,服务器端保存(保存重要的信息,减少服务器资源的浪费
Session对象由服务创建;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 HttpSession session = req.getSession(); session.setAttribute("name" ,new Person("Lanqilu" ,18 )); String id = session.getId(); boolean aNew = session.isNew();if (aNew) {resp.getWriter().write("session创建成功" + id); }else { resp.getWriter().write("session已经存在" + id); }
1 2 3 4 5 6 HttpSession session = req.getSession(); Person person = (Person) session.getAttribute("name" ); System.out.println("person.toString() = " + person.toString());
1 2 3 4 HttpSession session = req.getSession(); session.removeAttribute("name" ); session.invalidate();
Session自动过期,web.xml
1 2 3 4 5 <session-config > <session-timeout > 15</session-timeout > </session-config >