文章目录
前言
体能状态先于精神状态,习惯先于决心,聚焦先于喜好。
HttpServlet 基本介绍
HttpServlet 的基本特性
参考 javax.servlet.http.HttpServlet 源码中的注解可以得知以下信息
- HttpServlet 是一个抽象类,允许子类为一个 WEB 站点创建一个合适的 HTTP servlet
- HttpServlet 的子类必须覆盖重写下面中至少一个方法,
- doGet,servlet 用于支持 HTTP GET 请求
- doPost,用于支持 HTTP POST 请求
- doPut,用于支持 HTTP PUT 请求
- doDelete,用于支持 HTTP DELETE 请求
- init 和 destroy, 用于管理 servlet 声明周期的资源
- getServletInfo,默认返回一个空字符串, 可以覆盖重写为 作者, 版本, 和 所属权等内容 。
- HttpServlet 中不建议覆盖重写的方法——尽管你可以覆盖重写
- service,该方法会独立分发不同的 HTTP 请求,根据其 HTTP 请求类型
- HttpServlet 不用覆盖重写的方法
- doOptions
- doTrace
- Servlets 经常运行在多线程的服务器,所以应该意识到 servlet 必须处理并发的请求,并且小心处理共享资源的同步使用问题。
- 共享的资源包括缓存中的数据——比如:
- 实例或者类的变量
- 外部实体如文件、数据库连接资源、网络连接资源
HttpServlet 的使用方法
我们自定义一个子类来继承 HttpServlet ,演示一下 HttpServlet 的使用方法
- 自定义 HttpServlet 子类
package com.bestcxx.cn.webrecord.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 MyHttpServlet extends HttpServlet {
@Override
public void init() throws ServletException {
super.init();
System.out.println(System.currentTimeMillis()+":MyHttpServlet.init()方法被调用了");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//调用统一处理方法
handle(req,resp);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//不要调用 super 方法,否则报 HTTP method GET is not supported by this URL
//调用统一处理方法
handle(req,resp);
}
private void handle(HttpServletRequest req, HttpServletResponse resp){
//获取 servlet 上下文信息
ServletContext servletContext=getServletContext();
//访问路径
String contextPath=servletContext.getContextPath();
System.out.println(System.currentTimeMillis()+":项目名称为:"+contextPath);
//这里我们简化处理流程,仅返回一个字符串
PrintWriter printWriter=null;
try{
printWriter = resp.getWriter();
printWriter.write("Success ,contetPath is :"+contextPath);
}catch(Exception e){
System.out.println("系统异常:"+e.getMessage()+";"+e.getStackTrace());
}finally {
if(printWriter!=null){
printWriter.close();
}
}
}
}
- web.xml 增加 servlet 配置
<servlet>
<servlet-name>myservlet</servlet-name>
<servlet-class> com.bestcxx.cn.webrecord.servlet.MyHttpServlet</servlet-class>
<!--该值大于等于0 ,在servlet 初始化时运行 HttpServlet init()方法,
否则在第一次请求时运行且仅运行一次 HttpServlet init() 方法-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>myservlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
- 在 浏览器访问 http://localhost:8085/webrecord 两次
- 浏览器显示
Success ,contetPath is :/webrecord
- 控制台打印-无 < load-on-startup>配置
当没有配置 < load-on-startup>1</ load-on-startup>
第一次访问的时候 init()被调用,第二次访问时,init()不会被调用
1566105211059:MyHttpServlet.init()方法被调用了
1566105211070:项目名称为:/webrecord
1566105243018:项目名称为:/webrecord
- 控制台打印-有 < load-on-startup>配置
当配置 < load-on-startup>1</ load-on-startup>
启动阶段就会运行 servlet的init()方法
1566105211070:项目名称为:/webrecord
1566105243018:项目名称为:/webrecord
DispatcherServet 间接 继承了 HttpServlet
DispatcherServet 间接 继承了 HttpServlet , 从UML关系图中可以看出来。
HttpServlet 拥有的基本特性 DispatcherServet 也会拥有。
而在web项目启动阶段可以配置自动运行的 init()方法在 HttpServletBean进行了覆盖重写。
DispatcherServet 、FrameworkServlet、HttpServletBean UML图
DispatcherServet extends FrameworkServlet
FrameworkServlet extends HttpServletBean
FrameworkServlet 和 HttpServletBean 都是抽象类,它俩都有一些方法留给子类来实现
HttpServletBean.init()
完整路径 org.springframework.web.servlet.HttpServletBean.init()
重点是这里的initServletBean();
该方法由子类具体实现,这里依旧是一个空实现,运用了模板方法模式
@Override
public final void init() throws ServletException {
if (logger.isDebugEnabled()) {
logger.debug("Initializing servlet '" + getServletName() + "'");
}
// Set bean properties from init parameters.
try {
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
//这是一个空方法
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
throw ex;
}
// Let subclasses do whatever initialization they like.
//该方法的实现由子类完成:
initServletBean();
if (logger.isDebugEnabled()) {
logger.debug("Servlet '" + getServletName() + "' configured successfully");
}
}
FrameworkServlet.initServletBean()
完整路径 org.springframework.web.servlet.FrameworkServlet.initServletBean()
该方法覆盖重写了 父类 HttpServletBean 的方法
重点关注其中的this.webApplicationContext = initWebApplicationContext();
@Override
protected final void initServletBean() throws ServletException {
getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
if (this.logger.isInfoEnabled()) {
this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
}
long startTime = System.currentTimeMillis();
try {
//初始化 webApplicationContext
this.webApplicationContext = initWebApplicationContext();
//一个空方法
initFrameworkServlet();
}
catch (ServletException ex) {
this.logger.error("Context initialization failed", ex);
throw ex;
}
catch (RuntimeException ex) {
this.logger.error("Context initialization failed", ex);
throw ex;
}
if (this.logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
elapsedTime + " ms");
}
}
FrameworkServlet.initWebApplicationContext()
完整路径 org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext()
为这个servlet 初始化并发布 WebApplicationContext 对象
尤其注意wac = createWebApplicationContext(rootContext);
protected WebApplicationContext initWebApplicationContext() {
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
if (this.webApplicationContext != null) {
//WebApplicationContext 对象通过构造方法注入
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
// WebApplicationContext 对象未被刷新 -> 提供类似于父上下文对象,设置应用的上下文对象的id
if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent -> set
// the root application context (if any; may be null) as the parent
cwac.setParent(rootContext);
}
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
// 是否通过构造器注入 -> see if one
// 是否已经被注册到 servlet 上下文.如果存在,这表示任何父上下文已经初始化过并且制定了上下文对象的id
wac = findWebApplicationContext();
}
if (wac == null) {
//如果这个 servlet 没有发现上下文对象,则新建一个
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
// Either the context is not a ConfigurableApplicationContext with refresh
// support or the context injected at construction time had already been
// refreshed -> trigger initial onRefresh manually here.
onRefresh(wac);
}
if (this.publishContext) {
// Publish the context as a servlet context attribute.
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
"' as ServletContext attribute with name [" + attrName + "]");
}
}
return wac;
}