Servlet 工作原理-源码分析

一、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等方法实现功能

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值