一、java web工程
- java web工程启动
启动一个java web工程,需要一个web中间件,如tomcat容器,整个项目是在web容器中跑起来的 - web容器启动
启动一个web容器,首先会加载web.xml文件,完成一些初始化操作,加载成功,完成初始化,web容器才能成功启动 - web.xml常用配置
<web-app>
<display-name>Archetype Created Web Application</display-name>
<!--加载spring配置文件-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!--配置过滤器-->
<filter>
<filter-name>filterTest</filter-name>
<filter-class>com.mon.flier.MyFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>filterTest</filter-name>
<url-pattern>/</url-pattern>
</filter-mapping>
<!--启动web容器的时候,调用contextInitialized方法,完成初始化,这里借用了Spring的初始化-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--自定义servlet-->
<servlet>
<servlet-name>init</servlet-name>
<servlet-class>com.moon.controller.BaseController</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>init</servlet-name>
<url-pattern>/init</url-pattern>
</servlet-mapping>
</web-app>
加载web.xml,会监听listener,调用初始化方式,listener需要实现ServletContextListener,调用contextInitialized方法,ContextLoaderListener正式spring的初始化方法,换言之就是spring的入口
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent event) {
initWebApplicationContext(event.getServletContext());
}
}
二、servlet接口
- 接口定义
public interface Servlet {
void init(ServletConfig config) throws ServletException;
ServletConfig getServletConfig();
void service(ServletRequest request, ServletResponse response ) throws ServletException, IOException;
String getServletInfo();
void destroy();
}
实现了Servlet接口的类就可以配置到web.xml中的servlet标签下面,再第一次调用的时候,会执行init方法,创建这个Servlet,在执行service方法,在第二次访问的时候,不会再进行初始化,直接调用service方法,在Servlet销毁的时候会调用destroy方法
init方法会将上下文信息封装到ServletConfig对象中
public interface ServletConfig {
String getServletName();
ServletContext getServletContext();
String getInitParameter(String var1);
Enumeration<String> getInitParameterNames();
}
请求信息会封装到ServletContext 中,包括参数信息等
public interface ServletContext {
String TEMPDIR = "javax.servlet.context.tempdir";
String ORDERED_LIBS = "javax.servlet.context.orderedLibs";
Object getAttribute(String var1);
void setAttribute(String var1, Object var2);
//*** 不全部进行展示
}
总之ServletConfig 对象提供的从请求端的信息,我们可以通过不同的url找到不同的Servlet进行处理
三、Servlet线程安全问题
- web容器关系
web中间件,会有一个上下文路径,不同的上下文路径对应不同的web应用,一个web应用有不同的Servlet,Servlet是最小单位,处理唯一路径请求,Servlet在上下文应用中跑,上下文应用在web中间件里面跑
用户访问一个地址,找到ip地址+端口号,找到主机
上下文路径找到web应用
URL找到唯一的Servlet - 为什么有线程安全问题
所有的Servlet共用一个ServletConfig 对象,可能存在一个请求找到一个Servlet的时候,参数name=xxx1,当走到了控制器的时候,需要对参数进行解析的时候,另一个请求访问Servlet,将name修改为xxx2,这个时候就会出现问题 - 怎么解决
加锁,显然速度太慢,不能并发
copy,先复制到一边,用的时候再取出来
四、揭开HttpServlet神秘面纱
- 继承关系
public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {}
public abstract class HttpServlet extends GenericServlet{}
HttpServlet 继承了 实现Servlet的GenericServlet 具备了Servlet能力
- 具体做了什么
public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {
//初始化方式,将ServletConfig 设置为环境变量
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
//提供了初始化方法init,不需要再对ServletConfig 进行设置
public void init() throws ServletException {
}
//service 抽象方法,未进行实现
public abstract void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
//提供获取ServletConfig
public ServletConfig getServletConfig() {
return this.config;
}
//提供获取ServletContext
public ServletContext getServletContext() {
return this.getServletConfig().getServletContext();
}
}
public abstract class HttpServlet extends GenericServlet{
//实现Servlet的service方法,在调用自己的service
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
HttpServletRequest request;
HttpServletResponse response;
try {
/*
将ServletRequest --> HttpServletRequest
将ServletResponse --> HttpServletResponse
增加了Cookie[] getCookies();
增加了String getHeader(String var1);
增加了HttpSession getSession();
*** 省略很多
将Servlet转化为HttpServlet,增加了一些http特有的属性
*/
request = (HttpServletRequest)req;
response = (HttpServletResponse)res;
} catch (ClassCastException var6) {
throw new ServletException("non-HTTP request or response");
}
this.service(request, response);
}
//自己的service方法,再根据method 调用doGet,doPost
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getMethod();
long lastModified;
if (method.equals("GET")) {
lastModified = this.getLastModified(req);
if (lastModified == -1L) {
this.doGet(req, resp);
}
//*** 省略
}
}
}
因此我们在继承HttpServlet的时候,可以覆写HttpServlet自己的service方法,或者是doGet,doPost等方法实现功能