问题: spring web,在没有web.xml的情况下是如何被tomcat拉起来启动的呢?
首先是,tomcat 会通过 java 的spi 机制(其实tomcat 没java提供的spi方式,只是参照了其规范而已,下面详述),去找 javax.servlet.ServletContainerInitializer (since servlet 3.0)的实现类sping-web jar里有对 ServletContainerInitializer 的实现 SpringServletContainerInitializer 。
然后,tomcat 会调用该接口实现的 onStart 方法。这样就将执行权交给了 spring了。
java 的spi 机制:
- java 1.6 开始提供一个类叫做 java.util.ServiceLoader, 它是finnal 的,无法被继承个改动。
- 它有load(Class aClass) 方法,这个方法会去所以jar 中寻找 /META-INF/services/${aClass.fullClassName} 的文件(使用 ClassLoader.getSystemResources 方法),全部找到,然后解析其里面的内容,里面按照规范应该是每行一个对aClass 的一个实现类。并会通过 Class 的 newInstance 将其创建实例后给出来。 如此,调用者即可通过ServiceLoader 类的load 方法,找到实现了某个接口的 实现类并操作其实例。
- 然而 servlet3.0 并没有直接使用上面说的 java 的spi方式。只是参照其标准,自己写了个 WebappServiceLoader ,其虽然也是找 /META-INF/service 下的东西,但是找的范围被限定在 /WEB-INF/lib 下的jar中。
然后说说 ServletContainerInitializer 这个规范:
- 其 实现类 通过 spi 方式被找到后, 如果实现类上被注解了 类似 @javax.servlet.annotation.HandlesTypes(A.class,B.class) ,那么调用者按照规范需要将 A.class和 B.class 的实现类的 Class 对象,弄个Set ,传递给这个 ServletContainerInitializer 的 onStart 方法的第一个参数中。servlet3.0 是通过BCEL框架来扫描HandlesTypes中指定接口的实现类的。 Byte Code Engineering Library (BCEL),这是Apache Software Foundation 的Jakarta 项目的一部分,作用同ASM类似,是字节码操纵框架。