基本概念
通过实现Servlet来进行动态网页响应,使用Servlet,不再是直接由Tomcat服务器发送我们编写好的静态网页内容(HTML文件),而是由我们通过Java代码进行动态拼接的结果,它能够很好地实现动态网页的返回。
普通的Java程序是通过启动JVM,然后执行main()方法开始运行。但是Web应用程序有所不同,我们无法直接运行war文件,必须先启动Web服务器,再由Web服务器加载我们编写的HelloServlet,这样就可以让HelloServlet处理浏览器发送的请求。因此,我们首先要找一个支持Servlet API的Web服务器。常用的服务器有:
- Tomcat:由Apache开发的开源免费服务器;
- Jetty:由Eclipse开发的开源免费服务器;
- GlassFish:一个开源的全功能JavaEE服务器。
还有一些收费的商用服务器,如Oracle的WebLogic,IBM的WebSphere。
配置Tomcat
Tomcat是由Apache软件基金会属下Jakarta项目开发的Servlet容器,按照Sun Microsystems提供的技术规范,实现了对Servlet和JavaServer Page(JSP)的支持,并提供了作为Web服务器的一些特有功能,如Tomcat管理和控制平台、安全域管理和Tomcat阀等。Tomcat8的下载地址如下:
下载地址:https://tomcat.apache.org/download-80.cgi
本地配置
需要注意的是在IDEA高版本之后,创建web项目需要选择Java Enterprise。模板选择Web application,服务器选择本地的tomcat。如果不需maven插件,可以后期删除。
maven插件
首先安装Maven helper插件。然后再pom.xml文件中添加相关的插件方便修改端口等操作。最后在pom.xml文件中可以右键选择以maven插件方式运行tomcat。
配置参考
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 44 45 46 47 48 49
| <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId> <artifactId>TomcatMaven</artifactId> <version>1.0-SNAPSHOT</version> <name>TomcatMaven</name> <packaging>war</packaging>
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.target>1.8</maven.compiler.target> <maven.compiler.source>1.8</maven.compiler.source> </properties>
<dependencies> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency> </dependencies>
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>3.3.2</version> </plugin> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> <configuration> <uriEncoding>UTF-8</uriEncoding> <port>8099</port> <path>/</path> </configuration> </plugin> </plugins> </build> </project>
|
Servlet
它是Java EE的一个标准,大部分的Web服务器都支持此标准,包括Tomcat,就像之前的JDBC一样,由官方定义了一系列接口,而具体实现由我们来编写,最后交给Web服务器(如Tomcat)来运行我们编写的Servlet。
可以通过实现Servlet来进行动态网页响应,使用Servlet,不再是直接由Tomcat服务器发送我们编写好的静态网页内容(HTML文件),而是由我们通过Java代码进行动态拼接的结果,它能够很好地实现动态网页的返回。
创建方法
1.首先先实现Servlet接口
2.可以通过注解或者web.xml方式来注册Servlet
1 2 3 4 5
| @WebServlet("/test") public class TestServlet implements Servlet { ...实现接口方法 }
|
1 2 3 4 5 6 7 8 9
| <servlet> <servlet-name>test</servlet-name> <servlet-class>com.example.webtest.TestServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>test</servlet-name> <url-pattern>/test</url-pattern> </servlet-mapping>
|
生命周期
Servlet的生命周期为:
- 首先执行构造方法完成 Servlet 初始化
- Servlet 初始化后调用 init () 方法
- Servlet 调用 service() 方法来处理客户端的请求
- Servlet 销毁前调用 destroy() 方法
- 最后,Servlet 是由 JVM 的垃圾回收器进行垃圾回收的
基本示例
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 44 45 46 47 48 49
| package com.eldpepar;
import javax.servlet.*; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @WebServlet("/init") public class LifeSevlet implements Servlet { @Override public void init(ServletConfig servletConfig) throws ServletException { System.out.println("这里是初始化的方法"); }
@Override public ServletConfig getServletConfig() { System.out.println("----getServletConfig"); return null; }
@Override public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { System.out.println("----service"); HttpServletRequest request = (HttpServletRequest) servletRequest;
System.out.println(request.getProtocol()); System.out.println(request.getRemoteAddr()); System.out.println(request.getMethod());
HttpServletResponse response = (HttpServletResponse) servletResponse; response.setHeader("Content-type", "text/html;charset=UTF-8"); response.getWriter().write("我是响应内容!"); }
@Override public String getServletInfo() { System.out.println("----getServletInfo"); return null; }
@Override public void destroy() { System.out.println("----destroy"); } }
|
HttpServletRequest和HttpServletResponse
HttpServletRequest封装了一个HTTP请求,它实际上是从ServletRequest继承而来。最早设计Servlet时,设计者希望Servlet不仅能处理HTTP,也能处理类似SMTP等其他协议,因此,单独抽出了ServletRequest接口,但实际上除了HTTP外,并没有其他协议会用Servlet处理,所以这是一个过度设计
HttpServletResponse封装了一个HTTP响应。由于HTTP响应必须先发送Header,再发送Body,所以,操作HttpServletResponse对象时,必须先调用设置Header的方法,最后调用发送Body的方法
常用HttpServletRequest方法
- getMethod():返回请求方法,例如,”GET”,”POST”
- getRequestURI():返回请求路径,但不包括请求参数,例如,”/hello”
- getQueryString():返回请求参数,例如,”name=Bob&a=1&b=2”
- getParameter(name):返回请求参数,GET请求从URL读取参数,POST请求从Body中读取参数
- getContentType():获取请求Body的类型,例如,”application/x-www-form-urlencoded”
- getContextPath():获取当前Webapp挂载的路径,对于ROOT来说,总是返回空字符串””
- getCookies():返回请求携带的所有Cookie
- getHeader(name):获取指定的Header,对Header名称不区分大小写
- getHeaderNames():返回所有Header名称
- getInputStream():如果该请求带有HTTP Body,该方法将打开一个输入流用于读取Body
- getReader():和getInputStream()类似,但打开的是Reader
- getRemoteAddr():返回客户端的IP地址
- getScheme():返回协议类型,例如,”http”,”https”
常用HttpServletResponse方法
- setStatus(sc):设置响应代码,默认是200
- setContentType(type):设置Body的类型,例如,”text/html”
- setCharacterEncoding(charset):设置字符编码,例如,”UTF-8”
- setHeader(name, value):设置一个Header的值
- addCookie(cookie):给响应添加一个Cookie
- addHeader(name, value):给响应添加一个Header,因为HTTP协议允许有多个相同的Header
基本示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| package com.eldpepar;
import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException;
@WebServlet("/hello") public class HelloServlet extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println(req.getProtocol()); System.out.println(req.getRemoteAddr()); System.out.println(req.getMethod());
resp.setHeader("Content-type", "text/html;charset=UTF-8"); resp.getWriter().write("我是响应内容!"); } }
|
WebServlet注解
为了简化 Servlet 的配置,Servlet 3.0 中增加了注解支持,例如:@WebServlet、@WebInitParm 、@WebFilter 和 @WebLitener 等,这使得 web.xml 从 Servlet 3.0 开始不再是必选项了。
示例代码
1 2 3 4 5 6 7 8 9 10 11
| @WebServlet(urlPatterns = "/hello")
@WebServlet("/p/*")
@WebServlet("*.js")
@WebServlet({"/u1", "/u2"})
|
重定向与转发
重定向是指当浏览器请求一个URL时,服务器返回一个重定向指令,告诉浏览器地址已经变了,麻烦使用新的URL再重新发送新请求。重定向有两种:一种是302响应,称为临时重定向,一种是301响应,称为永久重定向。
Forward是指内部转发。当一个Servlet处理请求的时候,它可以决定自己不继续处理,而是转发给另一个Servlet处理。
两者区别
- 请求转发是一次请求,重定向是两次请求
- 请求转发地址栏不会发生改变, 重定向地址栏会发生改变
- 请求转发可以共享请求参数 ,重定向之后,就获取不了共享参数了
- 请求转发只能转发给内部的Servlet
示例代码
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
| package com.eldpepar; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException;
@WebServlet("/user") public class NewUrlDemo extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String name = req.getParameter("name"); if (name.equals("李四")) { resp.sendRedirect("https://bilibili.com"); }
if (name.equals("张三")) { req.getRequestDispatcher("/").forward(req,resp); } } }
|
Cookie和Session
基于唯一ID识别用户身份的机制称为Session。每个用户第一次访问服务器后,会自动获得一个Session ID。如果用户在一段时间内没有访问服务器,那么Session会自动失效,下次即使带着上次分配的Session ID访问,服务器也认为这是一个新用户,会分配新的Session ID
HTTP cookie,简称cookie,是在用户浏览网站时由网络服务器创建并由用户的网页浏览器存放在用户计算机或其他设备上的小文本文件。Cookie使Web服务器能够在用户的设备上存储状态信息(如添加到在线商店购物车中的商品)或跟踪用户的浏览活动(如点击特定按钮、登录或记录历史)
浏览器在请求某个URL时,是否携带指定的Cookie,取决于Cookie是否满足以下所有要求:
- URL前缀是设置Cookie时的Path;
- Cookie在有效期内;
- Cookie设置了secure时必须以https访问
登录注册
这里使用了cookie和session实现了一个简单的登录注册的功能。
首页
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
| package com.eldpepar;
import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter;
@WebServlet(urlPatterns = "/") public class IndexServlet extends HttpServlet { protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException, IOException { String user = (String) req.getSession().getAttribute("user"); resp.setContentType("text/html"); resp.setCharacterEncoding("UTF-8"); resp.setHeader("X-Powered-By", "JavaEE Servlet"); PrintWriter pw = resp.getWriter(); pw.write("<h1>Welcome, " + (user != null ? user : "Guest") + "</h1>"); if (user == null) { pw.write("<p><a href=\"/login\">Sign In</a></p>"); } else { pw.write("<p><a href=\"/logout\">Sign Out</a></p>"); } pw.flush(); } }
|
登录页
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 44 45 46
| package com.eldpepar.user;
import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.util.HashMap; import java.util.Map;
@WebServlet(urlPatterns = "/login") public class SignInServlet extends HttpServlet { private Map<String, String> users = new HashMap<>();
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/html"); PrintWriter pw = resp.getWriter(); pw.write("<h1>Sign In</h1>"); pw.write("<form action=\"/login\" method=\"post\">"); pw.write("<p>Username: <input name=\"username\"></p>"); pw.write("<p>Password: <input name=\"password\" type=\"password\"></p>"); pw.write("<p><button type=\"submit\">Sign In</button> <a href=\"/\">Cancel</a></p>"); pw.write("</form>"); pw.flush(); }
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { users.put("zhangsan", "123456"); users.put("lisi", "123456"); String name = req.getParameter("username"); String password = req.getParameter("password"); String expectedPassword = users.get(name.toLowerCase()); if (expectedPassword != null && expectedPassword.equals(password)) { req.getSession().setAttribute("user", name); resp.sendRedirect("/"); } else { resp.sendError(HttpServletResponse.SC_FORBIDDEN); } } }
|
退出页
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| package com.eldpepar.user;
import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException;
@WebServlet(urlPatterns = "/logout") public class SignOutServlet extends HttpServlet { protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.getSession().removeAttribute("user"); resp.sendRedirect("/"); } }
|
Filter
过滤器相当于在所有访问前加了一堵墙,来自浏览器的所有访问请求都会首先经过过滤器,只有过滤器允许通过的请求,才可以顺利地到达对应的Servlet,而过滤器不允许的通过的请求,我们可以自由地进行控制是否进行重定向或是请求转发。并且过滤器可以添加很多个
登录过滤器
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
| package com.eldpepar.user; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException;
@WebFilter("/*") public class LoginFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("filter init..."); }
@Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) servletRequest; HttpServletResponse resp = (HttpServletResponse) servletResponse; String user = (String)req.getSession().getAttribute("user"); String requestURI = req.getRequestURI(); if (requestURI.contains("/login") || requestURI.contains("/") ) { filterChain.doFilter(req, resp); } else { if (user != null) { filterChain.doFilter(req, resp); } else { req.getRequestDispatcher("/login").forward(req,resp); } } }
@Override public void destroy() { Filter.super.destroy(); } }
|
Listener
监听器,从字面上可以看出listener主要用来监听只用。通过listener可以监听web服务器中某一个执行动作,并根据其要求作出相应的响应。通俗的语言说就是在application,session,request三个对象创建消亡或者往其中添加修改删除属性时自动执行代码的功能组件。