前言:
catalina下面的core包,包含了很多我们平时一直使用到的东西,以前一直用不知道原理,也只知道j2EE api里面的东西,在tomcat下写了这么久得程序,一直疑惑它到底是怎么做到的,下面就可以好好看看了。
首先application,实现javax.servlet Interface ServletContext
引用
Defines a set of methods that a servlet uses to communicate with its servlet container, for example, to get the MIME type of a file, dispatch requests, or write to a log file.
There is one context per "web application" per Java Virtual Machine. (A "web application" is a collection of servlets and content installed under a specific subset of the server's URL namespace such as /catalog and possibly installed via a .war file.)
In the case of a web application marked "distributed" in its deployment descriptor, there will be one context instance for each virtual machine. In this situation, the context cannot be used as a location to share global information (because the information won't be truly global). Use an external resource like a database instead.
The ServletContext object is contained within the ServletConfig object, which the Web server provides the servlet when the servlet is initialized.
这家伙很重要,那么每个容器必须自己实现他的规则,tomcat如何实现呢?
eclipse果断查找
ApplicationContextFacade是为ApplicationContext服务的一个类,ApplicationContext才是标准实现了这个借口的类,至于JspCServletContext是Simple ServletContext
那么就重点看ApplicationContext,有的属性如下
构造方法如下:
引用
/**
* Construct a new instance of this class, associated with the specified
* Context instance.
*
* @param context The associated Context instance
*/
public ApplicationContext(String basePath, StandardContext context) {
super();
this.context = context;
this.basePath = basePath;
}
里面的StandardContext是实现标准Context借口的类,这个上下文借口是tomcat自己定义的,里面涉及到tomcat容器的众多处理,并且他还是个集成Container借口的接口,这么看来这个StandardContext是相当强大,当然没有这么一个强大的处理类注入,并且这个注入的类也需要从从操作里面反馈给自己很多东西
具体看里面的方法,经常用到的属性值set/getAttribute(String) ,重点看set,能解决很多以前的疑惑,里面的层层判断说明不是自己想得那样就是map里面put,它有自己的策略,没次还要去看readOnlyAttributes里面是否有这个key,有就不能移除。
public void setAttribute(String name, Object value) {
// Name cannot be null
if (name == null)
throw new IllegalArgumentException
(sm.getString("applicationContext.setAttribute.namenull"));
// Null value is the same as removeAttribute()
if (value == null) {
removeAttribute(name);
return;
}
Object oldValue = null;
boolean replaced = false;
// Add or replace the specified attribute
// Check for read only attribute
if (readOnlyAttributes.containsKey(name))
return;
oldValue = attributes.get(name);
if (oldValue != null)
replaced = true;
attributes.put(name, value);
// Notify interested application event listeners
Object listeners[] = context.getApplicationEventListeners();
if ((listeners == null) || (listeners.length == 0))
return;
ServletContextAttributeEvent event = null;
if (replaced)
event =
new ServletContextAttributeEvent(context.getServletContext(),
name, oldValue);
else
event =
new ServletContextAttributeEvent(context.getServletContext(),
name, value);
for (int i = 0; i < listeners.length; i++) {
if (!(listeners[i] instanceof ServletContextAttributeListener))
continue;
ServletContextAttributeListener listener =
(ServletContextAttributeListener) listeners[i];
try {
if (replaced) {
context.fireContainerEvent
("beforeContextAttributeReplaced", listener);
listener.attributeReplaced(event);
context.fireContainerEvent("afterContextAttributeReplaced",
listener);
} else {
context.fireContainerEvent("beforeContextAttributeAdded",
listener);
listener.attributeAdded(event);
context.fireContainerEvent("afterContextAttributeAdded",
listener);
}
} catch (Throwable t) {
if (replaced)
context.fireContainerEvent("afterContextAttributeReplaced",
listener);
else
context.fireContainerEvent("afterContextAttributeAdded",
listener);
// FIXME - should we do anything besides log these?
log(sm.getString("applicationContext.attributeEvent"), t);
}
}
}
容器里面使用的是哪一个呢?我们并不知道,我们完全是面对借口来编程,遵循j2ee api规范,然后具体的方法具体的容器有自己的实现。
同样下面还有req reps,分别有两个实现,比如继承自HttpServletRequestWrapper或者ServletRequestWrapper的两个类,还来了句这个,顶层都是ServletRequest
引用
WARNING</strong>: Due to Java's lack of support for multiple
* inheritance, all of the logic in <code>ApplicationRequest</code> is
* duplicated in <code>ApplicationHttpRequest</code>. Make sure that you
* keep these two classes in synchronization when making changes!
我比较关注session,里面还是一些session的策略,如果怎样变怎样,session为了安全只有一份,原来这两个getSession(),getSession(boolean create)是这么个关系,以前都挺纠结的
/**
* Return the session associated with this Request, creating one
* if necessary.
*/
public HttpSession getSession() {
return (getSession(true));
}
/**
* Return the session associated with this Request, creating one
* if necessary and requested.
*
* @param create Create a new session if one does not exist
*/
public HttpSession getSession(boolean create) {
if (crossContext) {
// There cannot be a session if no context has been assigned yet
if (context == null)
return (null);
// Return the current session if it exists and is valid
if (session != null && session.isValid()) {
return (session.getSession());
}
HttpSession other = super.getSession(false);
if (create && (other == null)) {
// First create a session in the first context: the problem is
// that the top level request is the only one which can
// create the cookie safely
other = super.getSession(true);
}
if (other != null) {
Session localSession = null;
try {
localSession =
context.getManager().findSession(other.getId());
if (localSession != null && !localSession.isValid()) {
localSession = null;
}
} catch (IOException e) {
// Ignore
}
if (localSession == null && create) {
localSession =
context.getManager().createSession(other.getId());
}
if (localSession != null) {
localSession.access();
session = localSession;
return session.getSession();
}
}
return null;
} else {
return super.getSession(create);
}
}
ps:好累啊,看不动了,下次重点看下面的几个复杂的容器类ContainerBase Lifecycle