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