在Tomcat中,Wrapper組件封裝了servlet定義和參數(shù)。
創(chuàng)建過(guò)程
前面一篇文章提到ContextConfig監(jiān)聽(tīng)器響應(yīng)配置開(kāi)始事件時(shí)會(huì)解析web.xml,進(jìn)而將每個(gè)servlet定義都包裝成Wrapper,這是由Context組件的createWrapper方法實(shí)現(xiàn)的。
StandardContext類實(shí)現(xiàn)的createWrapper方法代碼如下:
@Override
public Wrapper createWrapper() {
Wrapper wrapper = null;
if (wrapperClass != null) {
try {
wrapper = (Wrapper) wrapperClass.getConstructor().newInstance();
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
log.error("createWrapper", t);
return null;
}
} else {
wrapper = new StandardWrapper();
}
synchronized (wrapperLifecyclesLock) {
for (int i = 0; i < wrapperLifecycles.length; i++) {
try {
Class<?> clazz = Class.forName(wrapperLifecycles[i]);
LifecycleListener listener =
(LifecycleListener) clazz.getConstructor().newInstance();
wrapper.addLifecycleListener(listener);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
log.error("createWrapper", t);
return null;
}
}
}
synchronized (wrapperListenersLock) {
for (int i = 0; i < wrapperListeners.length; i++) {
try {
Class<?> clazz = Class.forName(wrapperListeners[i]);
ContainerListener listener =
(ContainerListener) clazz.getConstructor().newInstance();
wrapper.addContainerListener(listener);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
log.error("createWrapper", t);
return null;
}
}
}
return wrapper;
}
- StandardContext類的wrapperClass成員變量表示W(wǎng)rapper接口的實(shí)現(xiàn)類杂抽,如果沒(méi)有在配置中指定,那么默認(rèn)使用StandardWrapper韩脏;
- 為Wrapper添加生命周期事件監(jiān)聽(tīng)器和容器事件監(jiān)聽(tīng)器缩麸。
Wrapper組件
Wrapper接口繼承了Container接口,StandardWrapper是默認(rèn)實(shí)現(xiàn)類赡矢,同其他容器組件一樣杭朱,它也繼承自ContainerBase類。
public class StandardWrapper extends ContainerBase
implements ServletConfig, Wrapper, NotificationEmitter {
private final Log log = LogFactory.getLog(StandardWrapper.class); // must not be static
protected static final String[] DEFAULT_SERVLET_METHODS = new String[] {
"GET", "HEAD", "POST" };
public StandardWrapper() {
super();
swValve=new StandardWrapperValve();
pipeline.setBasic(swValve);
broadcaster = new NotificationBroadcasterSupport();
}
protected long available = 0L;
protected final AtomicInteger countAllocated = new AtomicInteger(0);
/**
* The facade associated with this wrapper.
*/
protected final StandardWrapperFacade facade = new StandardWrapperFacade(this);
protected volatile Servlet instance = null;
protected volatile boolean instanceInitialized = false;
/**
* The load-on-startup order value (negative value means load on
* first call) for this servlet.
*/
protected int loadOnStartup = -1;
/**
* Mappings associated with the wrapper.
*/
protected final ArrayList<String> mappings = new ArrayList<>();
/**
* The initialization parameters for this servlet, keyed by
* parameter name.
*/
protected HashMap<String, String> parameters = new HashMap<>();
// 省略一些代碼
/**
* Multipart config
*/
protected MultipartConfigElement multipartConfigElement = null;
/**
* Async support
*/
protected boolean asyncSupported = false;
protected boolean enabled = true;
private boolean overridable = false;
// 省略一些代碼
}
重要的成員變量如下:
- instance表示W(wǎng)rapper封裝的servlet實(shí)例吹散;
- instanceInitialized表示上述servlet實(shí)例是否已被初始化弧械;
- loadOnStartup表示servlet配置的load-on-startup值;
- mappings表示該servlet關(guān)聯(lián)的映射空民;
- parameters表示該servlet的初始化參數(shù)刃唐,以參數(shù)名為鍵;
- multipartConfigElement表示該servlet的Multipart配置。
StandardWrapper的構(gòu)造函數(shù)為自己的Pipeline添加了基本閥StandardWrapperValve画饥。
組件初始化
StandardWrapper類并沒(méi)有重寫initInternal方法衔瓮,因此它的初始化過(guò)程只是為自己創(chuàng)建了一個(gè)線程池用于啟動(dòng)和停止自己的子容器。
組件啟動(dòng)
StandardWrapper類的startInternal方法如下所示:
@Override
protected synchronized void startInternal() throws LifecycleException {
// Send j2ee.state.starting notification
if (this.getObjectName() != null) {
Notification notification = new Notification("j2ee.state.starting",
this.getObjectName(),
sequenceNumber++);
broadcaster.sendNotification(notification);
}
// Start up this component
super.startInternal();
setAvailable(0L);
// Send j2ee.state.running notification
if (this.getObjectName() != null) {
Notification notification =
new Notification("j2ee.state.running", this.getObjectName(),
sequenceNumber++);
broadcaster.sendNotification(notification);
}
}
- 發(fā)送j2ee.state.starting通知抖甘;
- 調(diào)用基類ContainerBase類的startInternal方法报辱,先啟動(dòng)子容器組件,然后啟動(dòng)Pipeline单山,最后發(fā)布LifecycleState.STARTING事件給添加到Host組件自身的生命周期事件監(jiān)聽(tīng)器;
- 發(fā)送j2ee.state.running通知幅疼。
加載servlet
前面一篇文章提到StandardContext啟動(dòng)時(shí)會(huì)加載被標(biāo)識(shí)為“啟動(dòng)加載”的servlet米奸,這是通過(guò)StandardContext類的loadOnStartup方法實(shí)現(xiàn)的:
public boolean loadOnStartup(Container children[]) {
// Collect "load on startup" servlets that need to be initialized
TreeMap<Integer, ArrayList<Wrapper>> map = new TreeMap<>();
for (int i = 0; i < children.length; i++) {
Wrapper wrapper = (Wrapper) children[i];
int loadOnStartup = wrapper.getLoadOnStartup();
if (loadOnStartup < 0)
continue;
Integer key = Integer.valueOf(loadOnStartup);
ArrayList<Wrapper> list = map.get(key);
if (list == null) {
list = new ArrayList<>();
map.put(key, list);
}
list.add(wrapper);
}
// Load the collected "load on startup" servlets
for (ArrayList<Wrapper> list : map.values()) {
for (Wrapper wrapper : list) {
try {
wrapper.load();
} catch (ServletException e) {
getLogger().error(sm.getString("standardContext.loadOnStartup.loadException",
getName(), wrapper.getName()), StandardWrapper.getRootCause(e));
// NOTE: load errors (including a servlet that throws
// UnavailableException from the init() method) are NOT
// fatal to application startup
// unless failCtxIfServletStartFails="true" is specified
if(getComputedFailCtxIfServletStartFails()) {
return false;
}
}
}
}
return true;
}
loadOnStartup方法遍歷Context的子容器(即所有Wrapper),對(duì)load-on-startup屬性值大于等于0的servlet按從小到大的順序依次加載爽篷。加載由Wrapper的load方法完成悴晰。StandardWrapper類實(shí)現(xiàn)的load方法如下:
@Override
public synchronized void load() throws ServletException {
instance = loadServlet();
if (!instanceInitialized) {
initServlet(instance);
}
if (isJspServlet) {
StringBuilder oname = new StringBuilder(getDomain());
oname.append(":type=JspMonitor");
oname.append(getWebModuleKeyProperties());
oname.append(",name=");
oname.append(getName());
oname.append(getJ2EEKeyProperties());
try {
jspMonitorON = new ObjectName(oname.toString());
Registry.getRegistry(null, null)
.registerComponent(instance, jspMonitorON, null);
} catch( Exception ex ) {
log.info("Error registering JSP monitoring with jmx " +
instance);
}
}
}
loadServlet方法會(huì)加載并初始化該Wrapper封裝的servlet。