一、Servlet
1、Servlet简介
Servlet翻译过来叫做运行在服务器端的小程序,需要运行在Servlet容器中,我们可以将Tomcat服务器理解为Servlet容器。Servlet最终是被服务器进行管理和调用的。
2、Servlet的作用:
1️⃣接收浏览器发送的请求
2️⃣调用其他的Java程序来处理用户的请求,完成前台和后台的交互
3️⃣向浏览器发送一个响应信息
3、Servlet的HelloWorld
①编写一个类并实现Servlet接口
②在web.xml文件中配置Servlet
<servlet>
<servlet-name>别名</servlet-name>
<servlet-class>全类名</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>别名</servlet-name>
<url-pattern>/请求地址</url-pattern>
</servlet-mapping>
一个Servlet可以配置多个url-pattern,这时用户就可以通过不同的地址访问同一个Servlet了,url-pattern的名字可以是任意的,也可以是通配符,比如:表示所有的以.do结尾的url请求都会访问该Servlet
<servlet-mapping>
<servlet-name>Hello</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
Tip
:
1️⃣若配成以.html结尾的话可以将服务器资源伪装成一个html页面,因为浏览器地址栏是以.html结尾的
2️⃣url-pattern的值只有一个/时,表示所有请求都会被该Servlet处理
3️⃣当多个utl-pattern相互交织时,会匹配最接近的那个Servlet
4、Servlet的生命周期
public class HelloServlet implements Servlet {
@Override
public void destroy() {
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void init(ServletConfig config) throws ServletException {
}
@Override
public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
}
}
实现Servlet接口时有四个方法和生命周期相关:构造器、init()、service()、destroy(),构造器和init()只调用一次(说明Servlet是单例的),service()每请求一次调用一次,destroy()在关闭服务的时候调用一次(用以销毁Servlet对象等)。这些方法都是由Servlet容器(如Tomcat)调用的,在调用这些方法的时候容器会传入一些参数如ServletConfig、ServletRequest、ServletResponse等。
Servlet及其子类是线程不安全的,老式的解决方式是让它实现SingleThreadModel接口,把一个单例的Servlet变为一个普通的类,每访问一次都会创建一个实例,这种方式不好已过时,因为太耗费资源。最好的解决方法是不在Servlet中使用成员变量,全部使用局部变量。
默认情况下,Servlet实例是当有客户端访问的时候才会创建(只有首次访问时创建),这之前不会创建,但当给Servlet配置一行代码:<load-on-startup>时可以改变Servlet实例创建的时机:<load-on-startup>0</load-on-startup>,值的范围是0-5之间的整数,值越小优先级越高,一旦配置了该行代码,该Servlet实例就会在客户端首次访问之前创建:
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>com.bdm.servlets.HelloServlet</servlet-class>
<load-on-startup>3</load-on-startup>
</servlet>
其他相关接口:
①ServletConfig:获取当前Servlet的配置和参数信息,每一个Servlet都有它唯一的ServletConfig对象,该对象封装的是Servlet的配置信息,即web.xml中<Servlet>标签配置的信息,该对象是Tomcat服务器在调用Servlet的init()时传入的,可通过该对象获取到Servlet的别名、初始化参数、ServletContext等
②ServletContext:获取整个web应用的配置和参数信息,一个应用只有一个唯一的ServletContext对象,各个Servlet获取到的ServletContext对象都是同一个,该对象在服务器启动(或者说应用部署)时创建,在服务器停止时销毁,可通过ServletConfig.getServletContext()获取该对象也可通过ServletRequest.getServletContext获取,通过ServletContext可以获取整个web应用的初始化参数、资源的真实路径、数据库的配置信息以及作为域对象在整个应用上共享数据(比如在多个Servlet间共享数据,实现各个Servlet间的通信)
Tip
:
Ⅰ、在JavaWeb中有四个域对象,这些域对象的信息共享的范围不同:
1️⃣Application:即ServletContext,作用范围在服务器一开始启动服务,到服务器关闭为止
2️⃣Session:HTTP会话开始到结束这段时间,可以理解为浏览器从打开到关闭的这段时间
3️⃣Request:HTTP请求开始到结束这段时间
4️⃣Page:当前页面(jsp)从打开到关闭这段时间,它只在同一个页面中有效
Ⅱ、通过ServletContext可以获取项目中的文件,即使在WEB-INF文件夹下也能获取到
//以字节流的形式获取文件:这个path是相对于webroot的路径,项目发布之后也可以用它
InputStream is = this.getServletContext().getResourceAsStream(path);
//获取访问的绝对路径(部署在服务器上的路径):通过该路径在浏览器中可以浏览文件的内容,或者下载文件
String absolutePath = this.getServletContext().getResource(path).getPath();
//获取文件在服务器端磁盘的绝对路径:通过该路径在浏览器中可以浏览文件的内容,或者下载该文件
String realPath = this.getServletContext().getRealPath(String path);
src文件夹下的文件在编译之后会出现在WEB-INF下的classes文件夹下,相当于src是classes的子文件夹,所以直接放在src下的文件的路径获取方式如下:
String realPath = this.getServletContext().getRealPath("/WEB-INF/classes/xxx.properties");
src这种路径较为繁琐,也可以借助ClassLoader来获取,这两种方式中路径的不同点是用ServletContext获取时路径是从webroot下开始填写的,而用ClassLoader.getResourceAsStream(“路径”)时路径是从classes下开始,所以配置文件等文件最好不要放在包下,因为放在包下的时候获取的路径非常繁琐,直接放在src下或者webroot下,另外还要注意使用类的加载器读取配置文件时有弊端:
a、类的加载器是将文件读取到内存中,如果文件太大的话可能会导致内存溢出,或者内存占用太大
b、类的加载器只加载一次文件,如果文件有了更改是不会再次加载的,会导致更新不可见
二、GenericServlet
GenericServlet是一个抽象类,实现了Servlet接口和ServletConfig接口:只将service()方法作为抽象方法供用户实现,让用户更专注于业务
public abstract class GenericServlet implements Servlet, ServletConfig, java.io.Serializable {
private static final long serialVersionUID = 1L;
private transient ServletConfig config;
public GenericServlet() {
// NOOP
}
@Override
public void destroy() {
// NOOP by default
}
@Override
public String getInitParameter(String name) {
return getServletConfig().getInitParameter(name);
}
@Override
public Enumeration<String> getInitParameterNames() {
return getServletConfig().getInitParameterNames();
}
@Override
public ServletConfig getServletConfig() {
return config;
}
@Override
public ServletContext getServletContext() {
return getServletConfig().getServletContext();
}
@Override
public String getServletInfo() {
return "";
}
@Override
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
public void init() throws ServletException {
// NOOP by default
}
public void log(String msg) {
getServletContext().log(getServletName() + ": " + msg);
}
public void log(String message, Throwable t) {
getServletContext().log(getServletName() + ": " + message, t);
}
@Override
public abstract void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;
@Override
public String getServletName() {
return config.getServletName();
}
}
继承GenericServlet:只需要重写service方法即可
public class HelloServlet extends GenericServlet {
private static final long serialVersionUID = 1L;
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
}
}
三、HttpServlet
HttpServlet继承了GenericServlet,HttpServlet也是抽象类,将GenericServlet中的请求和响应强转为了Http的方式。
高版本的HttpServlet对请求方式的处理做了细化:
public abstract class HttpServlet extends GenericServlet {
private static final long serialVersionUID = 1L;
private static final String METHOD_DELETE = "DELETE";
private static final String METHOD_HEAD = "HEAD";
private static final String METHOD_GET = "GET";
private static final String METHOD_OPTIONS = "OPTIONS";
private static final String METHOD_POST = "POST";
private static final String METHOD_PUT = "PUT";
private static final String METHOD_TRACE = "TRACE";
private static final String HEADER_IFMODSINCE = "If-Modified-Since";
private static final String HEADER_LASTMOD = "Last-Modified";
private static final String LSTRING_FILE = "javax.servlet.http.LocalStrings";
private static ResourceBundle lStrings = ResourceBundle.getBundle("javax.servlet.http.LocalStrings");
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_get_not_supported");
if (protocol.endsWith("1.1"))
resp.sendError(405, msg);
else
resp.sendError(400, msg);
}
protected long getLastModified(HttpServletRequest req) {
return -1L;
}
protected void doHead(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
if (DispatcherType.INCLUDE.equals(req.getDispatcherType())) {
doGet(req, resp);
} else {
NoBodyResponse response = new NoBodyResponse(resp);
doGet(req, response);
response.setContentLength();
}
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_post_not_supported");
if (protocol.endsWith("1.1"))
resp.sendError(405, msg);
else
resp.sendError(400, msg);
}
protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_put_not_supported");
if (protocol.endsWith("1.1"))
resp.sendError(405, msg);
else
resp.sendError(400, msg);
}
protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_delete_not_supported");
if (protocol.endsWith("1.1"))
resp.sendError(405, msg);
else
resp.sendError(400, msg);
}
private static Method[] getAllDeclaredMethods(Class<?> c) {
if (c.equals(HttpServlet.class)) {
return null;
}
Method[] parentMethods = getAllDeclaredMethods(c.getSuperclass());
Method[] thisMethods = c.getDeclaredMethods();
if ((parentMethods != null) && (parentMethods.length > 0)) {
Method[] allMethods = new Method[parentMethods.length + thisMethods.length];
System.arraycopy(parentMethods, 0, allMethods, 0, parentMethods.length);
System.arraycopy(thisMethods, 0, allMethods, parentMethods.length, thisMethods.length);
thisMethods = allMethods;
}
return thisMethods;
}
protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Method[] methods = getAllDeclaredMethods(super.getClass());
boolean ALLOW_GET = false;
boolean ALLOW_HEAD = false;
boolean ALLOW_POST = false;
boolean ALLOW_PUT = false;
boolean ALLOW_DELETE = false;
boolean ALLOW_TRACE = true;
boolean ALLOW_OPTIONS = true;
for (int i = 0; i < methods.length; ++i) {
Method m = methods[i];
if (m.getName().equals("doGet")) {
ALLOW_GET = true;
ALLOW_HEAD = true;
}
if (m.getName().equals("doPost"))
ALLOW_POST = true;
if (m.getName().equals("doPut"))
ALLOW_PUT = true;
if (m.getName().equals("doDelete")) {
ALLOW_DELETE = true;
}
}
String allow = null;
if (ALLOW_GET)
allow = "GET";
if (ALLOW_HEAD)
if (allow == null)
allow = "HEAD";
else
allow = new StringBuilder().append(allow).append(", HEAD").toString();
if (ALLOW_POST)
if (allow == null)
allow = "POST";
else
allow = new StringBuilder().append(allow).append(", POST").toString();
if (ALLOW_PUT)
if (allow == null)
allow = "PUT";
else
allow = new StringBuilder().append(allow).append(", PUT").toString();
if (ALLOW_DELETE)
if (allow == null)
allow = "DELETE";
else
allow = new StringBuilder().append(allow).append(", DELETE").toString();
if (ALLOW_TRACE)
if (allow == null)
allow = "TRACE";
else
allow = new StringBuilder().append(allow).append(", TRACE").toString();
if (ALLOW_OPTIONS) {
if (allow == null)
allow = "OPTIONS";
else
allow = new StringBuilder().append(allow).append(", OPTIONS").toString();
}
resp.setHeader("Allow", allow);
}
protected void doTrace(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String CRLF = "\r\n";
StringBuilder buffer = new StringBuilder("TRACE ").append(req.getRequestURI()).append(" ")
.append(req.getProtocol());
Enumeration reqHeaderEnum = req.getHeaderNames();
while (reqHeaderEnum.hasMoreElements()) {
String headerName = (String) reqHeaderEnum.nextElement();
buffer.append(CRLF).append(headerName).append(": ").append(req.getHeader(headerName));
}
buffer.append(CRLF);
int responseLength = buffer.length();
resp.setContentType("message/http");
resp.setContentLength(responseLength);
ServletOutputStream out = resp.getOutputStream();
out.print(buffer.toString());
out.close();
}
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getMethod();
if (method.equals("GET")) {
long lastModified = getLastModified(req);
if (lastModified == -1L) {
doGet(req, resp);
} else {
long ifModifiedSince;
try {
ifModifiedSince = req.getDateHeader("If-Modified-Since");
} catch (IllegalArgumentException iae) {
ifModifiedSince = -1L;
}
if (ifModifiedSince < lastModified / 1000L * 1000L) {
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(304);
}
}
} else if (method.equals("HEAD")) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
} else if (method.equals("POST")) {
doPost(req, resp);
} else if (method.equals("PUT")) {
doPut(req, resp);
} else if (method.equals("DELETE")) {
doDelete(req, resp);
} else if (method.equals("OPTIONS")) {
doOptions(req, resp);
} else if (method.equals("TRACE")) {
doTrace(req, resp);
} else {
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(501, errMsg);
}
}
private void maybeSetLastModified(HttpServletResponse resp, long lastModified) {
if (resp.containsHeader("Last-Modified"))
return;
if (lastModified >= 0L)
resp.setDateHeader("Last-Modified", lastModified);
}
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
HttpServletRequest request;
HttpServletResponse response;
try {
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
} catch (ClassCastException e) {
throw new ServletException("non-HTTP request or response");
}
service(request, response);
}
}
HttpServletRequest:封装了浏览器发送给服务器的请求,该对象由服务器创建并在服务器调用相应处理方法时作为参数传给doGet()、doPost()等。HttpServletRequest的作用:
1️⃣获取用户发送的请求参数
2️⃣可以获取项目名,主要用来设置绝对路径
String contextPath = request.getContextPath();
3️⃣可以作为域对象,在不同的web资源之间共享数据。
4️⃣可以用来做请求的转发
HttpServletResponse:封装了服务器发送给浏览器的响应,该对象也由服务器创建并作为参数传递给doGet()、doPost()等方法。HttpServletResponse的作用:
1️⃣向浏览器响应一个页面或者一个页面片段
2️⃣做请求的重定向
response.sendRedirect("target.html");