Web容器是如何加载Servlet的,对比Spring容器是如何加载bean的(一)

654

首先看什么时候需要加载Servlet:

  1. 在Web容器启动时,如果Servlet的属性中loadOnStartup的值不为-1(默认值,表示不进行预加载),则按照该属性值的顺序进行加载,如先加载该属性值为1的,然后再加载属性值为2的;
  2. 在有HTTP请求时,Web容器找到需要对应该URL的Servlet实例,如果还没有被加载,则需要进行加载;如果已经被加载了,则直接调用该实例的service()方法进行处理即可。

Servlet实例是一个Java对象,所以容器直接加载就好,但问题是如何将我们定义给这个Servlet的配置信息加载进去呢:

  1. 在实例化一个Servlet实例时,Web容器会为该实例的配置信息创建一个ServletConfig对象,用于存储配置信息:

其中存储了Servlet的名称(如果没有自定义则默认为Servlet类名);
ServletContext对象在每个Web容器中只有一个,对应于该Servlet实例所处的容器;
InitParameter就是我们定义在@WebInitParam注解中定义的一些初始默认值,你是否想起来在SpringMVC中也是存在初始值的呢?

  1. 如何将这个存放着配置信息的对象和要加载的Servelt联系起来呢?那必然是组合啦~来看类图:

public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {
    private static final long serialVersionUID = 1L;
    private transient ServletConfig config;

    public GenericServlet() {
    }
    
    public String getInitParameter(String name) {
        return this.getServletConfig().getInitParameter(name);
    }
    
    public void init(ServletConfig config) throws ServletException {
        this.config = config;
        this.init();
    }

    public void init() throws ServletException {
    }
}

可以看到在GenericServlet中通过init()方法将ServletConfig对象注入,然后通过调用这个对象的方法来实现getInitParameter以获取的一些配置信息,而这一切对于HttpServlet(即用户自定义Servlet所要继承的类)是透明的!那init方法是什么时候调用的呢?还记得Servlet的生命周期吗,init()->service()->destory();
3. 还有一个疑问,ServletConfig类中并没有配置URL的信息,那容器是如何找到对应的Servlet的呢,我盲猜HashMap,K是URL,V是Servlet的名字;

正经分析:《Spring揭秘》中将容器的作用抽象成两个层次,一个是注册与管理bean(beanFactory),一个是管理bean之间的依赖(beanDefinitionRegistry);那Spring是如何存储依赖的呢?

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
    String SCOPE_SINGLETON = "singleton";
    String SCOPE_PROTOTYPE = "prototype";

    void setParentName(@Nullable String var1);

    @Nullable
    String getParentName();

    void setBeanClassName(@Nullable String var1);

    @Nullable
    String getBeanClassName();

    void setScope(@Nullable String var1);

    @Nullable
    String getScope();

    void setLazyInit(boolean var1);

    boolean isLazyInit();

    void setDependsOn(@Nullable String... var1);

    @Nullable
    String[] getDependsOn();

    void setFactoryBeanName(@Nullable String var1);
}

可以看到BeanDefinition这个类中存储了该bean是单例还是原型模式;父类和本类的名字;是否懒加载,以及DependsOn(),这个类依赖于哪些类,返回的是一个String类型的数组,所以可以依赖的不止一个;

回到Web容器中,是否也存在和Spring一样的对于Servlet的Definition呢?

如果有这个对象,那这个类的抽象层次处于容器的范畴,而根据URL分配Servlet这个行为也处在容器的层面上; 当容器对web.xml或者@WebServlet注解扫描的时候,会得到servlet的映射数据并存储在servletMappings对象中:

public interface HttpServletMapping {
    String getMatchValue();

    String getPattern();

    String getServletName();

    MappingMatch getMappingMatch();
}

所以找到了存储映射的地方,那为什么不将映射关系一并放到servletConfig类中呢,我个人觉得是所在的应用范围不同,URLMapping是在容器层面的,而初始参数值这类是处在servlet本身这个层面的。 具体的如何加载的过程,下次整合。