![cover](https://img-blog.csdnimg.cn/15b8b6cb6f064636ba4e3bc38cb7ad91.png)
Servlet --- HttpServlet
Java打卡:第99天javaWeb — Servlet内容导航HttpServlet快捷创建ServletHttpServletRequest请求的生命周期请求参数parameter域属性attributeJava EEServlet昨天已经分享了欢迎页面和一般Servlet的书写,GenericServlet;使用模板方法模式和适配器模式其实系统已经定义好一个之前的实现的GenericServ
Java打卡:第99天
javaWeb — Servlet
Java EE
Servlet
昨天已经分享了欢迎页面和一般Servlet的书写,GenericServlet;使用模板方法模式和适配器模式
其实系统已经定义好一个之前的实现的GenericServlet;可以直接使用import javax.servlet.GenericServlet;
可以看一下源码
public abstract class GenericServlet implements Servlet, ServletConfig,
java.io.Serializable {
private static final long serialVersionUID = 1L;
private transient ServletConfig config;
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
/**
* A convenience method which can be overridden so that there's no need to
* call <code>super.init(config)</code>.
* <p>
* Instead of overriding {@link #init(ServletConfig)}, simply override this
* method and it will be called by
* <code>GenericServlet.init(ServletConfig config)</code>. The
* <code>ServletConfig</code> object can still be retrieved via
* {@link #getServletConfig}.
*
* @exception ServletException
* if an exception occurs that interrupts the servlet's
* normal operation
*/
public void init() throws ServletException {
// NOOP by default
}
实现的思路和之前写的是相同的,使用了模板方法模式
HttpServlet
这里假如有一个需求: 验证请求方式;表单提交的方式为POST;如果直接通过地址栏访问则拦截
分析:要想拦截,那就要获得请求提交的方式method;这里的service方法中有Request类型的,这里的req变量没有getMethod方法;==但是Request下面的子接口HTTPrequest有;
所以这里使用下转型可以获得对象【java其实不支持真正的下转型,只有超类对象是由子类对象上转型得到,才可以进行下转型】 因为通信支持的是Http协议,所以这里就可以想到method应该与Http相关。
package cfeng;
import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.*;
@SuppressWarnings("serial")
public class HttpServlet extends GenericServlet {
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
//这里使用下转型
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
//获取提交的method
String method = request.getMethod();
//操作method 使用equals方法一般将常量放在前面,这里可以避免空指针异常
if("POST".equals(method)) {
doPost();
}else if("GET".equals(method)) {
doGET();
}
}
public void doGET()
System.out.println("非法操作,不允许访问");
}
public void doPost() {
System.out.println("合法操作,允许访问");
}
}
12月 19, 2021 5:30:52 下午 org.apache.catalina.loader.WebappClassLoaderBase checkThreadLocalsForLeaks
警告: 在Java 9上运行时,需要在JVM命令行参数中添加“-add opens=Java.base/Java.lang=ALL-UNNAMED”,以启用线程本地内存泄漏检测。或者,可以通过禁用ThreadLocal内存泄漏检测来抑制此警告。
12月 19, 2021 5:30:52 下午 org.apache.catalina.core.StandardContext reload
信息: 已完成重新加载名为/proj1的上下文
合法操作,允许访问
这样我们就实现了对于请求的处理,可以识别是正常的请求或者非法的请求
那么就有疑问了,操作请求是很常见的一种操作,会比较频繁使用,系统是否实现了这个类呢?就像GenericServlet一样,答案是肯定的;
当需要对请求进行操作的时候,可以不继承GenericServlet,可以直接继承HttpServlet
//这里可以看一下HttpServlet的源码
public abstract class HttpServlet extends GenericServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String msg = lStrings.getString("http.method_get_not_supported");
sendMethodNotAllowed(req, resp, msg);
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String msg = lStrings.getString("http.method_post_not_supported");
sendMethodNotAllowed(req, resp, msg);
}
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String method = req.getMethod(); //获取提交method
if (method.equals(METHOD_GET)) { //判断method
long lastModified = getLastModified(req);
if (lastModified == -1) {
// servlet doesn't support if-modified-since, no reason
// to go through further expensive logic
doGet(req, resp);
} else {
long ifModifiedSince;
try {
ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
} catch (IllegalArgumentException iae) {
// Invalid date header - proceed as if none was set
ifModifiedSince = -1;
}
if (ifModifiedSince < (lastModified / 1000 * 1000)) {
// If the servlet mod time is later, call doGet()
// Round down to the nearest second for a proper compare
// A ifModifiedSince of -1 will always be less
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
} else if (method.equals(METHOD_POST)) {
doPost(req, resp);
} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);
} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);
} else {
//
// Note that this means NO servlet supports whatever
// method was requested, anywhere on this server.
//
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
@Override //这里重写自GenericServlet的方法
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(lStrings.getString("http.non_http"));
}
service(request, response);
}
}
可以看到这里的实现方法和上面的思路基本是一致的,倒时候需要使用的时候就继承该类;并且重写doGet和doPost方法
所以之后实现的时候就不要再继承GenericServlet,而是直接继承HttpServlet;操作就是重写两个方法
package cfeng;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.*;
public class loginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
System.out.println("合法操作");
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doGet(req, resp);
doPost(req,resp); //既支持POST,也支持GET
}
}
这样当表单POST提交的时候就会执行该方法
快捷创建Servlet
经过前面的分析,以后创建Servlet的时候,就不用再继承Servlet接口或者GenericServlet抽象类了,可以直接继承HttpServlet类
并且可以使用Eclipse快捷操作,直接new的时候就创建Servlet;而不是创建Class;这样的好处就是不用手动配置
Ctrl + N 新建输入Servlet;输入名称,next;选择url-pattern;next选择方法就可快捷创建Servlet
这样就可以直接创建一个Servlet类
package cfeng;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class Some
*/
@WebServlet("/Some")
public class Some extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public Some() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
response.getWriter().append("Served at: ").append(request.getContextPath());
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
像这样之后就可以访问了
package cfeng;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(description = "this is just a test class", urlPatterns = { "/TestServlet" })
public class TestServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("hello");
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
注意:这里的Servlet配置没有再web.xml中配置,而是直接使用注解websocket配置,推荐使用这种方式,更加方便动态
@WebServlet(description = “this is just a test class”, urlPatterns = { “/TestServlet” })
在Servlet类的前面加上这个,和在web.xml中配置的效果相同
HttpServletRequest
HttpServletRequest是SUN指定的Servlet规范,表示请求,父接口是ServletRequest;HTTP请求协议的完整的内容都要封装request对象中;Tomcat对于该接口进行了实现;之前使用抓包工具抓取的请求内容就是封装在HttpServletRequest中;
这里比如
用HttpWatch来进行观察,当使用表单POST提交的请求的内容
POST /Test/TestServlet HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://localhost:8080/Test/
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 23
user=%E5%BC%A0%E4%B8%89
这里出现了请求的中文乱码问题,稍后会进行分析;HttpServletRequest就是将上面的内容封装起来,方便直接进行操作。
request = org.apache.catalina.connector.RequestFacade@57a3f54a
这个对象就是Tomcat调用的时候对于HttpServletRequest接口的实现使用的就是该实现类的对象
请求的生命周期
当请求到达服务器的时候Tomcat服务器就会解析这个请求,按照HTTP协议解析之后就会封装成一个HttpServletRequest对象,这样就初始化完成。请求对象是由服务器创建。
当服务器向客户端响应结束后,HttpServletRequest实例对象由服务器销毁
一次请求对应一个请求对象
,也就是对应一个Request对象;另外的对应另外一个对象,与之前的请求对象没有任何关系,所以Request对象的生命周期很短暂
HTTP是无状态【数据】协议,不同的请求之间是没有关系的
请求参数parameter
HttpServletRequest的父接口ServletRequest中就有关于请求参数的方法比如
- getParameter: Returns the value of a request parameter as a
String
, ornull
if the parameter does not exist.
这里的请求参数是指的是提交表单的时候提交的参数,比如表单中的name就是,value就是参数的值
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("hello");
System.out.println("request = " + request);
String user = request.getParameter("user");
System.out.println("user = " + user);
}
已完成重新加载名为/Test的上下文
hello
request = org.apache.catalina.connector.RequestFacade@6ed310ae
user = hh
- getParameterNames 返回的就是一个name的枚举集合,和之前的config类中的获取初始化参数 相同
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("hello");
System.out.println("request = " + request);
Enumeration<String> parameterNames = request.getParameterNames();
//遍历枚举类型
while(parameterNames.hasMoreElements()) {
String name = parameterNames.nextElement();
System.out.println(request.getParameter(name));
}
}
还是可以输出,和上面的结果相同的
HttpServletRequest对于请求所携带的参数是以Map的形式接收的,并且这个Map的key是String,value是String[]类型的,为什么是数组类型,而不是String类型?
表单中的复选框的类型比如checkbox,这里的value有多个值
<form action="/login" method="get">
爱好:
<input type="checkbox" name="hobby" value="soccer"/>足球
<input type="checkbox" name="hobby" value="basketball"/>篮球
<br>
</form>
这里的复选框就有多个值,那么获取的时候如果是一个String类型,是不能正常执行的,这里是一个Map类型的
user=zhangsan&hobby=soccer&hobby=basketball&hobby=tennis
//上面是请求的请求报文
hello
request = org.apache.catalina.connector.RequestFacade@6ed310ae
user=zhangsan
hobby=soccer
这里看到普通的根据name获取的参数值就是hobby=soccer;就是使用getParameter输出的只是一个value
getParameter本质上等同于getParameterValues(“name”)[0];
所以这里要将所有的value给取出来就要使用Map;之后使用getParameterValues;如果没有复选框,就使用getParameter就可以了
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("hello");
System.out.println("request = " + request);
Map<String, String[]> map = request.getParameterMap();
for (String key : map.keySet()) {//keySet就是Map的键的集合
String[] values = request.getParameterValues(key); //获取map的value集合
System.out.print(key + " = ");
for(String value : values) {
System.out.print(value + " ");
}
System.out.print("\n");
}
}
user = zhangsan
hobby = soccer basketball tennis
这样子就可以获取复选框这种有多个值的name
域属性 attribute
dispatcher 调度程序,分配器 wrapper 包装
之前分析config的时候就提到了初始化参数和域属性;那里的域属性是所有的Servlet共享,就是一个应用程序对应一个应用程序;complex
在HttpRequest中也有域属性空间,用来存放有名称的数据,该数据只是在当前的request的请求中可以进行访问;只要请求还存在,就可以访问域属性空间的数据; 所以域属性的作用就是用来传递数据的
和之前的complex相同,直接get,set就可
ServletRequest的一个方法是getRequestDispatch方法 就是获得一个请求的分配器;就是正常来说一个请求提交给一个Servlet,可能完成不了任务;这个时候就要通过dispatcher转发给其他的servlet处理
- getRequestDispatcher (String path) 将一个当前Servlet的请求转发给另外一个path;path可对应一个servlet 得到一个对应终点路径的调度器
- forward: Forwards a request from a servlet to another resource (servlet, JSP file, or HTML file) on the server. [forward 向前、传递] 就是将当前路径的请求传递给另外一个resource
- include : Includes the content of a resource (servlet, JSP page, HTML file) in the response. 包含一个相应的所有的内容
在其中一个请求没有结束的时候,被分配的Servlet就可以 访问操作域属性;和Complex是相同的;比如remove,set和get
package cfeng;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(description = "just for testing", urlPatterns = { "/some" })
public class someServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println(this.getServletName());
String author = (String)request.getAttribute("author");
System.out.println("author = " + author);
System.out.println("addree = " + request.getAttribute("address"));
}
这里就是被分配的类,配置的信息就是在webServlet注释中,不需要再xml中配置
package cfeng;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Map;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(description = "this is just a test class", urlPatterns = { "/TestServlet" })
public class TestServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setAttribute("author", "Cfeng");
request.setAttribute("address", "Peking");
Map<String, String[]> map = request.getParameterMap();
request.getRequestDispatcher("/some").forward(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);//这里POST提交的时候也会执行GET中的方法
}
}
这样就和之前讲解的域属性是类似的,就是Servlet之间实现属性共享,可以设置,访问和删除;这是这里的域属性必须是请求有效区间。♉️
更多推荐
所有评论(0)