Tomcat启动分析(十一) - Wrapper组件
2019-04-03 本文已影响0人
buzzerrookie
在Tomcat中,Wrapper组件封装了servlet定义和参数。
创建过程
前面一篇文章提到ContextConfig监听器响应配置开始事件时会解析web.xml,进而将每个servlet定义都包装成Wrapper,这是由Context组件的createWrapper方法实现的。
StandardContext类实现的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成员变量表示Wrapper接口的实现类,如果没有在配置中指定,那么默认使用StandardWrapper;
- 为Wrapper添加生命周期事件监听器和容器事件监听器。
Wrapper组件
Wrapper接口继承了Container接口,StandardWrapper是默认实现类,同其他容器组件一样,它也继承自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表示Wrapper封装的servlet实例;
- instanceInitialized表示上述servlet实例是否已被初始化;
- loadOnStartup表示servlet配置的load-on-startup值;
- mappings表示该servlet关联的映射;
- parameters表示该servlet的初始化参数,以参数名为键;
- multipartConfigElement表示该servlet的Multipart配置。
StandardWrapper的构造函数为自己的Pipeline添加了基本阀StandardWrapperValve。
组件初始化
StandardWrapper类并没有重写initInternal方法,因此它的初始化过程只是为自己创建了一个线程池用于启动和停止自己的子容器。
组件启动
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);
}
}
- 发送j2ee.state.starting通知;
- 调用基类ContainerBase类的startInternal方法,先启动子容器组件,然后启动Pipeline,最后发布LifecycleState.STARTING事件给添加到Host组件自身的生命周期事件监听器;
- 发送j2ee.state.running通知。
加载servlet
前面一篇文章提到StandardContext启动时会加载被标识为“启动加载”的servlet,这是通过StandardContext类的loadOnStartup方法实现的:
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),对load-on-startup属性值大于等于0的servlet按从小到大的顺序依次加载。加载由Wrapper的load方法完成。StandardWrapper类实现的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方法会加载并初始化该Wrapper封装的servlet。