前言
说到 Java Web 就是 Tomcat,在后面学习 Java 无文件 WebShell 之前先来观摩一下 Tomcat 源码。
主要看看 filter、servlet 等。
catalina.sh
启动 Tomcat 的方法之一:./catalina.sh start,在脚本中执行的启动如下:
eval exec "\"$_RUNJAVA\"" "\"$CATALINA_LOGGING_CONFIG\"" $LOGGING_MANAGER "$JAVA_OPTS" "$CATALINA_OPTS" \
-D$ENDORSED_PROP="\"$JAVA_ENDORSED_DIRS\"" \
-classpath "\"$CLASSPATH\"" \
-Dcatalina.base="\"$CATALINA_BASE\"" \
-Dcatalina.home="\"$CATALINA_HOME\"" \
-Djava.io.tmpdir="\"$CATALINA_TMPDIR\"" \
org.apache.catalina.startup.Bootstrap "$@" start
通过 ps 查看到的完整命令如下:
/usr/lib/jvm/jdk1.8.0_191/jre/bin/java
-Djava.util.logging.config.file=/opt/apache-tomcat-9.0.12/conf/logging.properties
-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
-Djdk.tls.ephemeralDHKeySize=2048
-Djava.protocol.handler.pkgs=org.apache.catalina.webresources
-Dorg.apache.catalina.security.SecurityListener.UMASK=0027
-Dignore.endorsed.dirs=
-classpath /opt/apache-tomcat-9.0.12/bin/bootstrap.jar:/opt/apache-tomcat-9.0.12/bin/tomcat-juli.jar
-Dcatalina.base=/opt/apache-tomcat-9.0.12
-Dcatalina.home=/opt/apache-tomcat-9.0.12
-Djava.io.tmpdir=/opt/apache-tomcat-9.0.12/temp
org.apache.catalina.startup.Bootstrap start
这里的 classpath 主要是两个 jar:
-classpath /opt/apache-tomcat-9.0.12/bin/bootstrap.jar:/opt/apache-tomcat-9.0.12/bin/tomcat-juli.jar
以 start 为参数调用了 org.apache.catalina.startup.Bootstrap 类。
load/init
这个类在 bootstrap.jar 中,静态代码块主要是设置各种环境变量,就不提了,看下 main 函数,前一半代码如下:
public static void main(String[] args) {
synchronized(daemonLock) {
if (daemon == null) {
Bootstrap bootstrap = new Bootstrap();
try {
bootstrap.init();
} catch (Throwable var5) {
...
}
daemon = bootstrap;
} else {
Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
}
}
...
}
首先设置线程的环境,初始化环境的代码在 Bootstrap 的 init 函数中,前一半如下:
public void init() throws Exception {
this.initClassLoaders();
Thread.currentThread().setContextClassLoader(this.catalinaLoader);
SecurityClassLoad.securityClassLoad(this.catalinaLoader);
if (log.isDebugEnabled()) {
log.debug("Loading startup class");
}
...
}
首先初始化了类加载器:
private void initClassLoaders() {
try {
this.commonLoader = this.createClassLoader("common", (ClassLoader)null);
if (this.commonLoader == null) {
this.commonLoader = this.getClass().getClassLoader();
}
this.catalinaLoader = this.createClassLoader("server", this.commonLoader);
this.sharedLoader = this.createClassLoader("shared", this.commonLoader);
} catch (Throwable var2) {
handleThrowable(var2);
log.error("Class loader creation threw exception", var2);
System.exit(1);
}
}
可以看到这里会创建三个类加载器,其中 catalinaLoader 和 sharedLoader 是 commonLoader 的子类加载器,createClassLoader 函数中的创建代码如下:
private ClassLoader createClassLoader(String name, ClassLoader parent) throws Exception {
String value = CatalinaProperties.getProperty(name + ".loader");
if (value != null && !value.equals("")) {
value = this.replace(value);
List<Repository> repositories = new ArrayList();
String[] repositoryPaths = getPaths(value);
String[] var6 = repositoryPaths;
int var7 = repositoryPaths.length;
for(int var8 = 0; var8 < var7; ++var8) {
String repository = var6[var8];
try {
new URL(repository);
repositories.add(new Repository(repository, RepositoryType.URL));
} catch (MalformedURLException var11) {
if (repository.endsWith("*.jar")) {
repository = repository.substring(0, repository.length() - "*.jar".length());
repositories.add(new Repository(repository, RepositoryType.GLOB));
} else if (repository.endsWith(".jar")) {
repositories.add(new Repository(repository, RepositoryType.JAR));
} else {
repositories.add(new Repository(repository, RepositoryType.DIR));
}
}
}
return ClassLoaderFactory.createClassLoader(repositories, parent);
} else {
return parent;
}
}
简单来说,就是从配置中获取加载类的路径然后创建类加载器,观察 CatalinaProperties 类的 getProperty 和 loadProperties 函数,可以看到配置来自 /org/apache/catalina/startup/catalina.properties:
common.loader="${catalina.base}/lib","${catalina.base}/lib/*.jar","${catalina.home}/lib","${catalina.home}/lib/*.jar"
server.loader=
shared.loader=
可以看到,commonLoader 类加载器的加载路径是 Tomcat 的 lib 目录,而 catalinaLoader 和 sharedLoader 的加载路径为空,也就是说他们的值都是 commonLoader。
创建完类加载器后, 会加载一个 org.apache.catalina.startup.Catalina 类,实例化并将 sharedLoader,即 commonLoader 放入其中:
Class<?> startupClass = this.catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
Object startupInstance = startupClass.getConstructor().newInstance();
if (log.isDebugEnabled()) {
log.debug("Setting startup class properties");
}
String methodName = "setParentClassLoader";
Class<?>[] paramTypes = new Class[]{Class.forName("java.lang.ClassLoader")};
Object[] paramValues = new Object[]{this.sharedLoader};
Method method = startupInstance.getClass().getMethod(methodName, paramTypes);
method.invoke(startupInstance, paramValues);
this.catalinaDaemon = startupInstance;
回到 main 函数,start 命令会执行的代码如下:
else if (command.equals("start")) {
daemon.setAwait(true);
daemon.load(args);
daemon.start();
if (null == daemon.getServer()) {
System.exit(1);
}
}
setAwait 只是给一个布尔属性赋值,跳过它看 load 函数。
调用的 load 函数来自 org.apache.catalina.startup.Catalina 类:
public void load() {
if (!this.loaded) {
this.loaded = true;
long t1 = System.nanoTime();
this.initDirs();
this.initNaming();
this.parseServerXml(true);
...
}
}
此时还没有加载,所以 loaded 为 false,前面的环境变量设置(时间、目录等)跳过,后面的 parseServerXml 函数一看就是跟加载 web 配置文件相关的函数:
protected void parseServerXml(boolean start) {
ConfigFileLoader.setSource(new CatalinaBaseConfigurationSource(Bootstrap.getCatalinaBaseFile(), this.getConfigFile()));
File file = this.configFile();
File serverXmlLocation = null;
if (this.generateCode) {
...
}
Catalina.ServerXml serverXml = null;
if (this.useGeneratedCode) {
...
}
if (serverXml != null) {
serverXml.load(this);
} else {
try {
Resource resource = ConfigFileLoader.getSource().getServerXml();
Throwable var6 = null;
try {
Digester digester = start ? this.createStartDigester() : this.createStopDigester();
InputStream inputStream = resource.getInputStream();
InputSource inputSource = new InputSource(resource.getURI().toURL().toString());
inputSource.setByteStream(inputStream);
digester.push(this);
if (this.generateCode) {
...
}
digester.parse(inputSource);
if (this.generateCode) {
...
}
}
...
}
...
}
}
实例化了一个 Digester XML 解析器,并将自身 Catalina 对象放入栈中,用于后面保存解析过程中生成的对象。
配置文件来自 CatalinaBaseConfigurationSource 类,使用 ConfigFileLoader.getSource().getServerXml() 获取配置文件资源:
public Resource getServerXml() throws IOException {
IOException ioe = null;
Resource result = null;
try {
if (this.serverXmlPath != null && !this.serverXmlPath.equals("conf/server.xml")) {
result = this.getResource(this.serverXmlPath);
} else {
result = super.getServerXml();
}
} catch (IOException var6) {
ioe = var6;
}
if (result == null) {
...
}
if (result == null && ioe != null) {
throw ioe;
} else {
return result;
}
}
this.serverXmlPath 来自实例化时传入的 this.getConfigFile(),即 Catalina 类的 configFile 属性:
protected String configFile = "conf/server.xml";
获取配置文件资源后,然后开始使用 Digester 解析 xml(参考文章),这是个匹配 -> 回调感觉的解析器,详细的规则可以在 createStartDigester 函数中看到,比如这三句:
digester.addObjectCreate("Server", "org.apache.catalina.core.StandardServer", "className");
digester.addSetProperties("Server");
digester.addSetNext("Server", "setServer", "org.apache.catalina.Server");
这里的规则匹配 Server 标签,addObjectCreate 规则在没有 className 属性的情况下默认实例化一个 org.apache.catalina.core.StandardServer 类并放入栈中:
public void begin(String namespace, String name, Attributes attributes) throws Exception {
String realClassName = this.getRealClassName(attributes);
if (realClassName == null) {
throw new NullPointerException(sm.getString("rule.noClassName", new Object[]{namespace, name}));
} else {
Class<?> clazz = this.digester.getClassLoader().loadClass(realClassName);
Object instance = clazz.getConstructor().newInstance();
this.digester.push(instance);
...
}
}
protected String getRealClassName(Attributes attributes) {
String realClassName = this.className;
if (this.attributeName != null) {
String value = attributes.getValue(this.attributeName);
if (value != null) {
realClassName = value;
}
}
return realClassName;
}
addSetProperties 规则用其他属性给 StandardServer 类赋值,跳过。
addSetNext 规则将 StandardServer 对象作为参数,调用 Catalina 对象的 setServer 函数将 StandardServer 保存起来。
解析完 server.xml 之后,Tomcat 服务端对象已经创建好并按层次保存在 Catalina 对象的 server 属性中了,回到 load 函数,接下来的代码如下:
Server s = this.getServer();
if (s != null) {
this.getServer().setCatalina(this);
this.getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
this.getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());
this.initStreams();
try {
this.getServer().init();
} catch (LifecycleException var5) {
...
}
if (log.isInfoEnabled()) {
log.info(...);
}
}
调用 this.getServer().init() 开始初始化服务端,看了一下各个模块类的 initInternal 函数,好像没什么特别的,那就直接回到 main 函数里面 daemon.start() 这一句吧。
start
观察 server.xml 中的配置,可以发现一个 Service 就是一个 Web 服务,所以我们关注 Service 的启动,Service 的配置如下:
<Service name="Catalina">
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<Engine name="Catalina" defaultHost="localhost">
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
</Engine>
</Service>
观察里面的配置项,可以看出 Connector 应该用于处理 HTTP 协议,而 Host 部分应该用于处理 HTTP 请求。
跟 init 类似,start 主要调用的是 startInternal 函数,Service 的 startInternal 函数如下:
protected void startInternal() throws LifecycleException {
if (log.isInfoEnabled()) {
log.info(sm.getString("standardService.start.name", new Object[]{this.name}));
}
this.setState(LifecycleState.STARTING);
if (this.engine != null) {
synchronized(this.engine) {
this.engine.start();
}
}
synchronized(this.executors) {
...
}
this.mapperListener.start();
synchronized(this.connectorsLock) {
Connector[] var10 = this.connectors;
int var11 = var10.length;
for(int var4 = 0; var4 < var11; ++var4) {
Connector connector = var10[var4];
if (connector.getState() != LifecycleState.FAILED) {
connector.start();
}
}
}
}
因为配置文件中没有 executors,所以就跳过了,简单来说就是启动 engine、mapperListener 和 connector,我们先看看 engine:
protected synchronized void startInternal() throws LifecycleException {
if (log.isInfoEnabled()) {
log.info(sm.getString("standardEngine.start", new Object[]{ServerInfo.getServerInfo()}));
}
super.startInternal();
}
engine 的启动没什么特别的,再看看 mapperListener:
public void startInternal() throws LifecycleException {
this.setState(LifecycleState.STARTING);
Engine engine = this.service.getContainer();
if (engine != null) {
this.findDefaultHost();
this.addListeners(engine);
Container[] conHosts = engine.findChildren();
Container[] var3 = conHosts;
int var4 = conHosts.length;
for(int var5 = 0; var5 < var4; ++var5) {
Container conHost = var3[var5];
Host host = (Host)conHost;
if (!LifecycleState.NEW.equals(host.getState())) {
this.registerHost(host);
}
}
}
}
看代码中的命名,engine 应该就是我们在 Tomcat 中常常看到的 container。
mapperListener 启动时首先调用 findDefaultHost:
private void findDefaultHost() {
Engine engine = this.service.getContainer();
String defaultHost = engine.getDefaultHost();
boolean found = false;
if (defaultHost != null && defaultHost.length() > 0) {
Container[] containers = engine.findChildren();
Container[] var5 = containers;
int var6 = containers.length;
for(int var7 = 0; var7 < var6; ++var7) {
Container container = var5[var7];
Host host = (Host)container;
if (defaultHost.equalsIgnoreCase(host.getName())) {
found = true;
break;
}
String[] aliases = host.findAliases();
String[] var11 = aliases;
int var12 = aliases.length;
for(int var13 = 0; var13 < var12; ++var13) {
String alias = var11[var13];
if (defaultHost.equalsIgnoreCase(alias)) {
found = true;
break;
}
}
}
}
if (found) {
this.mapper.setDefaultHostName(defaultHost);
} else {
log.error(sm.getString("mapperListener.unknownDefaultHost", new Object[]{defaultHost, this.service}));
}
}
可以看到,只有在 engine 的 children 中找到一个其 name 和 defaultHost 相对应的,才会给 this.mapper 赋值。
engine 的 children 是通过 addChild 添加的,具体的解析规则可以到 HostRuleSet 类中找到:
digester.addObjectCreate(this.prefix + "Host", "org.apache.catalina.core.StandardHost", "className");
digester.addSetProperties(this.prefix + "Host");
...
digester.addSetNext(this.prefix + "Host", "addChild", "org.apache.catalina.Container");
简单来说它的 children 就是 Host,因为 Host 中配置的 name 和 engine 中的 defaultHost 相同,所以会将 defaultHost 赋给 mapper。
findDefaultHost 结束后,调用 addListeners 将自身这个 MapperListener 对象放入 Host 及 Host 的 children 中(默认配置时应该为空)。
最后调用 registerHost,将 Host 注册进 mapper 中(感觉 MapperListener 跟 Host 互相套娃的)。
回到 Service,下一步执行的是 connector 的 start:
protected void startInternal() throws LifecycleException {
if (this.getPortWithOffset() < 0) {
throw new LifecycleException(sm.getString("coyoteConnector.invalidPort", new Object[]{this.getPortWithOffset()}));
} else {
this.setState(LifecycleState.STARTING);
try {
this.protocolHandler.start();
} catch (Exception var2) {
throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerStartFailed"), var2);
}
}
}
这里启动了 protocolHandler,看名字就是用于处理协议的类,是在 connector 的构造函数中初始化的:
ProtocolHandler p = null;
try {
p = ProtocolHandler.create(protocol, apr);
} catch (Exception var5) {
log.error(sm.getString("coyoteConnector.protocolHandlerInstantiationFailed"), var5);
}
if (p != null) {
this.protocolHandler = p;
this.protocolHandlerClassName = this.protocolHandler.getClass().getName();
} else {
this.protocolHandler = null;
this.protocolHandlerClassName = protocol;
}
而配置的协议为 HTTP/1.1:
static ProtocolHandler create(String protocol, boolean apr) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
if (protocol == null || "HTTP/1.1".equals(protocol) || !apr && Http11NioProtocol.class.getName().equals(protocol) || apr && Http11AprProtocol.class.getName().equals(protocol)) {
return (ProtocolHandler)(apr ? new Http11AprProtocol() : new Http11NioProtocol());
} else if ("AJP/1.3".equals(protocol) || !apr && AjpNioProtocol.class.getName().equals(protocol) || apr && AjpAprProtocol.class.getName().equals(protocol)) {
return (ProtocolHandler)(apr ? new AjpAprProtocol() : new AjpNioProtocol());
} else {
Class<?> clazz = Class.forName(protocol);
return (ProtocolHandler)clazz.getConstructor().newInstance();
}
}
默认没有开启 apr 的情况下,处理 HTTP 协议的类应该就是 Http11NioProtocol。
shutdown
回到 Catalina 类的 start 函数,最后会执行 await 开始监听请求:
if (this.await) {
this.await();
this.stop();
}
this.await 在 Bootstrap 对象执行 daemon.setAwait(true) 时赋值为 true,await 函数会执行到 Server 的 await 函数,首先在 8005 端口创建了一个监听本地请求的 ServerSocket:
this.awaitSocket = new ServerSocket(this.getPortWithOffset(), 1, InetAddress.getByName(this.address));
然后从 client 读取字符,如果收到的命令为 shutdown 就会中断监听并关闭 socket:
boolean match = command.toString().equals(this.shutdown);
if (match) {
log.info(sm.getString("standardServer.shutdownViaPort"));
var32 = false;
break;
}
...
serverSocket = this.awaitSocket;
this.awaitThread = null;
this.awaitSocket = null;
if (serverSocket != null) {
try {
serverSocket.close();
} catch (IOException var65) {
}
}
这里的死循环结束之后,就会结束 await 函数并调用 this.stop:
public void stop() {
try {
if (this.useShutdownHook) {
Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
LogManager logManager = LogManager.getLogManager();
if (logManager instanceof ClassLoaderLogManager) {
((ClassLoaderLogManager)logManager).setUseShutdownHook(true);
}
}
} catch (Throwable var3) {
ExceptionUtils.handleThrowable(var3);
}
try {
Server s = this.getServer();
LifecycleState state = s.getState();
if (LifecycleState.STOPPING_PREP.compareTo(state) > 0 || LifecycleState.DESTROYED.compareTo(state) < 0) {
s.stop();
s.destroy();
}
} catch (LifecycleException var4) {
log.error(sm.getString("catalina.stopError"), var4);
}
}
调用 Server 的 stop 和 destroy,大体来说就是一级级下去分别调用了 Service、Engine 等模块类的停止函数,就把整个 Tomcat 服务关闭了。
当我们使用 stop 命令关闭 Tomcat 时,Catalina 类会解析配置文件,获取 IP 端口等信息并传输 shutdown 命令:
try {
Socket socket = new Socket(s.getAddress(), s.getPortWithOffset());
Throwable var4 = null;
try {
OutputStream stream = socket.getOutputStream();
Throwable var6 = null;
try {
String shutdown = s.getShutdown();
for(int i = 0; i < shutdown.length(); ++i) {
stream.write(shutdown.charAt(i));
}
stream.flush();
}
...
}
}
...
}
这样一来就实现了关闭 Tomcat。
await
回到 connector 中执行的这一句代码:
this.protocolHandler.start();
这里就开始启动 web 部分的监听了,来到 Http11NioProtocol 父类 AbstractProtocol 的 start 函数:
public void start() throws Exception {
if (this.getLog().isInfoEnabled()) {
this.getLog().info(sm.getString("abstractProtocolHandler.start", new Object[]{this.getName()}));
this.logPortOffset();
}
this.endpoint.start();
this.monitorFuture = this.getUtilityExecutor().scheduleWithFixedDelay(new Runnable() {
public void run() {
if (!AbstractProtocol.this.isPaused()) {
AbstractProtocol.this.startAsyncTimeout();
}
}
}, 0L, 60L, TimeUnit.SECONDS);
}
首先调用了 endpoint.start,看下 endpoint 是个什么对象:
public Http11NioProtocol() {
super(new NioEndpoint());
}
然后来到 Http11NioProtocol 类的 startInternal 函数:
public void startInternal() throws Exception {
if (!this.running) {
this.running = true;
this.paused = false;
if (this.socketProperties.getProcessorCache() != 0) {
this.processorCache = new SynchronizedStack(128, this.socketProperties.getProcessorCache());
}
if (this.socketProperties.getEventCache() != 0) {
this.eventCache = new SynchronizedStack(128, this.socketProperties.getEventCache());
}
if (this.socketProperties.getBufferPool() != 0) {
this.nioChannels = new SynchronizedStack(128, this.socketProperties.getBufferPool());
}
if (this.getExecutor() == null) {
this.createExecutor();
}
this.initializeConnectionLatch();
this.poller = new NioEndpoint.Poller();
Thread pollerThread = new Thread(this.poller, this.getName() + "-ClientPoller");
pollerThread.setPriority(this.threadPriority);
pollerThread.setDaemon(true);
pollerThread.start();
this.startAcceptorThread();
}
}
Tomcat 中的客户端请求处理是通过 Acceptor 和 Poller 两部分实现的,我们先看看 Acceptor:
public void run() {
byte errorDelay = 0;
while(this.endpoint.isRunning()) {
...
this.state = Acceptor.AcceptorState.RUNNING;
try {
this.endpoint.countUpOrAwaitConnection();
if (!this.endpoint.isPaused()) {
Object socket = null;
try {
socket = this.endpoint.serverSocketAccept();
} catch (Exception var6) {
this.endpoint.countDownConnection();
if (!this.endpoint.isRunning()) {
break;
}
this.handleExceptionWithDelay(errorDelay);
throw var6;
}
errorDelay = 0;
if (this.endpoint.isRunning() && !this.endpoint.isPaused()) {
if (!this.endpoint.setSocketOptions(socket)) {
this.endpoint.closeSocket(socket);
}
} else {
this.endpoint.destroySocket(socket);
}
}
}
...
}
this.state = Acceptor.AcceptorState.ENDED;
}
调用 this.endpoint.serverSocketAccept 获取一个 SocketChannel:
protected SocketChannel serverSocketAccept() throws Exception {
return this.serverSock.accept();
}
accept 函数会一直阻塞直到新的请求到来。serverSock 的初始化在 initServerSocket 函数中(往上可以追溯到 Connector 的 this.protocolHandler.init() 这一句):
this.serverSock = ServerSocketChannel.open();
this.socketProperties.setProperties(this.serverSock.socket());
InetSocketAddress addr = new InetSocketAddress(this.getAddress(), this.getPortWithOffset());
this.serverSock.socket().bind(addr, this.getAcceptCount());
可以看到监听的地址和端口来自自身的两个属性,他们的赋值方式要追溯到 connector 的创建去了,解析 XML 时:
Connector con = new Connector(protocolName);
...
this.digester.push(con);
创建一个 Connector 并放入栈中(如上文所说,protocolHandler 就是在 connector 构造函数中创建的),然后下一条规则:
digester.addSetProperties("Server/Service/Connector", new String[]{"executor", "sslImplementationName", "protocol"});
除了这三个属性,其他都会进行赋值,SetPropertiesRule 的赋值处理如下:
if (!IntrospectionUtils.setProperty(top, name, value, true, actualMethod)) {
if (this.digester.getRulesValidation() && !"optional".equals(name)) {
this.digester.log.warn(sm.getString("rule.noProperty", new Object[]{this.digester.match, name, value}));
}
}
IntrospectionUtils 的处理比较别致,找不到特定属性的 setter 时会调用 setProperty:
if ("setProperty".equals(method.getName())) {
if (method.getReturnType() == Boolean.TYPE) {
setPropertyMethodBool = method;
} else {
setPropertyMethodVoid = method;
}
}
对于 connector 来说就是:
public boolean setProperty(String name, String value) {
return this.protocolHandler == null ? false : IntrospectionUtils.setProperty(this.protocolHandler, name, value);
}
对于 protocolHandler(Http11NioProtocol)来说就是:
public void setAddress(InetAddress ia) {
this.endpoint.setAddress(ia);
}
所以一层层反射下去就会给 endpoint 赋值。
不过又有个问题,connector 反射的参数类型为 String,而 protocolHandler 却是 InetAddress,所以中间还存在一个类型转换的操作。再回到了 IntrospectionUtils,这里又有个对 InetAddress 类型参数的别致操作:
if ("java.net.InetAddress".equals(paramType.getName())) {
try {
params[0] = InetAddress.getByName(value);
} catch (UnknownHostException var20) {
if (log.isDebugEnabled()) {
log.debug("IntrospectionUtils: Unable to resolve host name:" + value);
}
ok = false;
}
if (actualMethod != null) {
actualMethod.append(method.getName()).append("(InetAddress.getByName(\"").append(value).append("\"))");
}
}
所以在 connector 中配置 address 可以设置 Tomcat 的监听地址。
不过看 server.xml,里面是没有配置 address 的,所以就是默认的 0.0.0.0:
public InetSocketAddress(InetAddress addr, int port) {
holder = new InetSocketAddressHolder(
null,
addr == null ? InetAddress.anyLocalAddress() : addr,
checkPort(port));
}
回到 Acceptor,在接收到请求并获取到一个 SocketChannel 之后,会调用 setSocketOptions:
protected boolean setSocketOptions(SocketChannel socket) {
Object socketWrapper = null;
try {
NioChannel channel = null;
if (this.nioChannels != null) {
channel = (NioChannel)this.nioChannels.pop();
}
if (channel == null) {
SocketBufferHandler bufhandler = new SocketBufferHandler(this.socketProperties.getAppReadBufSize(), this.socketProperties.getAppWriteBufSize(), this.socketProperties.getDirectBuffer());
if (this.isSSLEnabled()) {
channel = new SecureNioChannel(bufhandler, this.selectorPool, this);
} else {
channel = new NioChannel(bufhandler);
}
}
NioEndpoint.NioSocketWrapper newWrapper = new NioEndpoint.NioSocketWrapper((NioChannel)channel, this);
((NioChannel)channel).reset(socket, newWrapper);
this.connections.put(socket, newWrapper);
socket.configureBlocking(false);
this.socketProperties.setProperties(socket.socket());
newWrapper.setReadTimeout((long)this.getConnectionTimeout());
newWrapper.setWriteTimeout((long)this.getConnectionTimeout());
newWrapper.setKeepAliveLeft(this.getMaxKeepAliveRequests());
this.poller.register((NioChannel)channel, newWrapper);
return true;
}
...
}
新建一个 NioChannel,将 SocketChannel 和 NioSocketWrapper 放进去,再将新生成的 channel 和 wrapper 一起放入 connections Map 并注册进 Poller 中,注册进 Poller 中的代码如下:
public void register(NioChannel socket, NioEndpoint.NioSocketWrapper socketWrapper) {
socketWrapper.interestOps(1);
NioEndpoint.PollerEvent event = null;
if (NioEndpoint.this.eventCache != null) {
event = (NioEndpoint.PollerEvent)NioEndpoint.this.eventCache.pop();
}
if (event == null) {
event = new NioEndpoint.PollerEvent(socket, 256);
} else {
event.reset(socket, 256);
}
this.addEvent(event);
}
简单来说就是会添加进 events 这个队列中,Acceptor 的 run 函数大致结束了,接下来看看 Poller 的:
public void run() {
while(true) {
boolean hasEvents = false;
label59: {
try {
if (!this.close) {
hasEvents = this.events();
...
}
if (!this.close) {
break label59;
}
...
}
...
}
...
}
}
看看 events 函数:
public boolean events() {
boolean result = false;
NioEndpoint.PollerEvent pe = null;
int i = 0;
for(int size = this.events.size(); i < size && (pe = (NioEndpoint.PollerEvent)this.events.poll()) != null; ++i) {
result = true;
NioChannel channel = pe.getSocket();
NioEndpoint.NioSocketWrapper socketWrapper = channel.getSocketWrapper();
int interestOps = pe.getInterestOps();
if (interestOps == 256) {
try {
channel.getIOChannel().register(this.getSelector(), 1, socketWrapper);
} catch (Exception var12) {
NioEndpoint.log.error(AbstractEndpoint.sm.getString("endpoint.nio.registerFail"), var12);
}
} else {
...
}
if (NioEndpoint.this.running && !NioEndpoint.this.paused && NioEndpoint.this.eventCache != null) {
pe.reset();
NioEndpoint.this.eventCache.push(pe);
}
}
return result;
}
调用 poll 从中队列中取出一个 event(并从队列中删除),关注这一句:
channel.getIOChannel().register(this.getSelector(), 1, socketWrapper);
这里将 channel 注册到了 selector 上,并监听 read 事件:
public static final int OP_READ = 1 << 0;
继续看 run 函数:
if (this.wakeupCounter.getAndSet(-1L) > 0L) {
this.keyCount = this.selector.selectNow();
} else {
this.keyCount = this.selector.select(NioEndpoint.this.selectorTimeout);
}
this.wakeupCounter.set(0L);
wakeupCounter 是一个初始为 0 的计数器,每次 addEvent 就会 +1,没有 event 时就会为 0,从而进入 this.selector.select 的阻塞,超时时间为 1 秒。
如果存在有效事件,keyCount 就是有效事件的数量,后面就开始处理这些事件:
Iterator iterator = this.keyCount > 0 ? this.selector.selectedKeys().iterator() : null;
while(iterator != null && iterator.hasNext()) {
SelectionKey sk = (SelectionKey)iterator.next();
NioEndpoint.NioSocketWrapper socketWrapper = (NioEndpoint.NioSocketWrapper)sk.attachment();
if (socketWrapper == null) {
iterator.remove();
} else {
iterator.remove();
this.processKey(sk, socketWrapper);
}
}
看看 processKey 函数,关注 read 的部分:
if (sk.isReadable()) {
if (socketWrapper.readOperation != null) {
if (!socketWrapper.readOperation.process()) {
closeSocket = true;
}
} else if (!NioEndpoint.this.processSocket(socketWrapper, SocketEvent.OPEN_READ, true)) {
closeSocket = true;
}
}
下一步 processSocket:
if (handshake == 0) {
AbstractEndpoint.Handler.SocketState state = AbstractEndpoint.Handler.SocketState.OPEN;
if (this.event == null) {
state = NioEndpoint.this.getHandler().process(this.socketWrapper, SocketEvent.OPEN_READ);
} else {
state = NioEndpoint.this.getHandler().process(this.socketWrapper, this.event);
}
if (state == AbstractEndpoint.Handler.SocketState.CLOSED) {
poller.cancelledKey(socket.getIOChannel().keyFor(poller.getSelector()), this.socketWrapper);
}
}
handshake 为 0 代表 TCP 握手完成(大概),然后调用 handler 的 process 函数,这里的 handler 是个 ConnectionHandler 类,在 protocolHandler 实例化时赋值(AbstractHttp11Protocol 类构造函数):
AbstractProtocol.ConnectionHandler<S> cHandler = new AbstractProtocol.ConnectionHandler(this);
this.setHandler(cHandler);
this.getEndpoint().setHandler(cHandler);
process:
if (processor == null) {
processor = this.getProtocol().createProcessor();
this.register(processor);
if (this.getLog().isDebugEnabled()) {
this.getLog().debug(AbstractProtocol.sm.getString("abstractConnectionHandler.processorCreate", new Object[]{processor}));
}
}
第一次访问时没有 processor,所以会调用 createProcessor 生成一个,AbstractHttp11Protocol 类的 createProcessor 函数如下:
protected Processor createProcessor() {
Http11Processor processor = new Http11Processor(this, this.adapter);
return processor;
}
生成的是 Http11Processor,然后调用其 process 函数:
state = processor.process(wrapper, status);
然后来到 AbstractProcessorLight 类的 process:
else if (status == SocketEvent.OPEN_READ) {
state = this.service(socketWrapper);
}
回到 Http11Processor 类的 service 函数:
this.getAdapter().service(this.request, this.response);
可以看到,这里有 request 和 response 两个属性,都来自 org.apache.coyote 包,找一下他们的生成方式,在父类 AbstractProcessor 的构造函数中:
public AbstractProcessor(Adapter adapter) {
this(adapter, new Request(), new Response());
}
protected AbstractProcessor(Adapter adapter, Request coyoteRequest, Response coyoteResponse) {
this.hostNameC = new char[0];
this.asyncTimeout = -1L;
this.asyncTimeoutGeneration = 0L;
this.socketWrapper = null;
this.errorState = ErrorState.NONE;
this.adapter = adapter;
this.asyncStateMachine = new AsyncStateMachine(this);
this.request = coyoteRequest;
this.response = coyoteResponse;
this.response.setHook(this);
this.request.setResponse(this.response);
this.request.setHook(this);
this.userDataHelper = new UserDataHelper(this.getLog());
}
可以看到 response 就存放在 request 中,Http11Processor 放在 request 和 response 中。不过仔细观察这个 request 中的属性,会发现他们似乎并不是我们习惯的那个 request。
而 Adapter 的赋值要追溯到 Connector 的 initInternal 函数:
this.adapter = new CoyoteAdapter(this);
跟进 CoyoteAdapter 类的 service 函数:
Request request = (Request)req.getNote(1);
Response response = (Response)res.getNote(1);
if (request == null) {
request = this.connector.createRequest();
request.setCoyoteRequest(req);
response = this.connector.createResponse();
response.setCoyoteResponse(res);
request.setResponse(response);
response.setRequest(request);
req.setNote(1, request);
res.setNote(1, response);
req.getParameters().setQueryStringCharset(this.connector.getURICharset());
}
可以看到我们习惯的 request(org.apache.catalina.connector),第一次访问时 request(org.apache.coyote)是刚刚生成的,所以 note 属性里面没有保存 request(org.apache.catalina.connector),这时会调用 createRequest/createResponse 从 connector 生成并保存起来:
public Request createRequest() {
return new Request(this);
}
public Response createResponse() {
int size = this.protocolHandler.getDesiredBufferSize();
return size > 0 ? new Response(size) : new Response();
}
request 和 response 互相嵌套,connector 套进了 request 中。
然后开始处理 HTTP 请求:
postParseSuccess = this.postParseRequest(req, request, res, response);
if (postParseSuccess) {
request.setAsyncSupported(this.connector.getService().getContainer().getPipeline().isAsyncSupported());
this.connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
}
调用 postParseRequest 解析请求,如果解析成功就会开始反射,看这一长串链式执行,可以简化成 engine.getPipeline().getFirst().invoke(request, response),我们回到 engine 去看看:
protected final Pipeline pipeline = new StandardPipeline(this);
StandardPipeline 类的 getFirst 函数如下:
public Valve getFirst() {
return this.first != null ? this.first : this.basic;
}
调试可以发现,这里的 first 为 null,basic 来自 engine 的构造函数:
this.pipeline.setBasic(new StandardEngineValve());
所以最后的反射函数如下:
public final void invoke(Request request, Response response) throws IOException, ServletException {
Host host = request.getHost();
if (host != null) {
if (request.isAsyncSupported()) {
request.setAsyncSupported(host.getPipeline().isAsyncSupported());
}
host.getPipeline().getFirst().invoke(request, response);
}
}
看看这里的 host 是怎么生成的,回到 postParseRequest,里面有一句:
this.connector.getService().getMapper().map(serverName, decodedURI, version, request.getMappingData());
会来到 Mapper 类的 internalMap 函数:
Mapper.MappedHost[] hosts = this.hosts;
Mapper.MappedHost mappedHost = (Mapper.MappedHost)exactFindIgnoreCase(hosts, host);
...
mappingData.host = (Host)mappedHost.object;
hosts 是一个数组,通过 addHost 函数进行添加,再往上回到 MapperListener 类的 registerHost 函数中,在前面 start 的时候会注册进去:
public void startInternal() throws LifecycleException {
this.setState(LifecycleState.STARTING);
Engine engine = this.service.getContainer();
if (engine != null) {
this.findDefaultHost();
this.addListeners(engine);
Container[] conHosts = engine.findChildren();
Container[] var3 = conHosts;
int var4 = conHosts.length;
for(int var5 = 0; var5 < var4; ++var5) {
Container conHost = var3[var5];
Host host = (Host)conHost;
if (!LifecycleState.NEW.equals(host.getState())) {
this.registerHost(host);
}
}
}
}
可以看到源数据来自 engine,即 server.xml 中配置并解析生成的 standardHost 对象。然后再调用 exactFindIgnoreCase 从这些 hosts 中获取该次请求对应的 host:
private static final <T, E extends Mapper.MapElement<T>> E exactFindIgnoreCase(E[] map, CharChunk name) {
int pos = findIgnoreCase(map, name);
if (pos >= 0) {
E result = map[pos];
if (name.equalsIgnoreCase(result.name)) {
return result;
}
}
return null;
}
简单来说就是将访问的 host 和 sever.xml 中配置的 host 匹配一下。
除了 host,mappingData 中还存放了其他重要属性,比如说 context,我们继续往下看 internalMap 函数:
Mapper.ContextList contextList = mappedHost.contextList;
Mapper.MappedContext[] contexts = contextList.contexts;
int pos = find(contexts, (CharChunk)uri);
这里出现了一个 host 中的属性 contextList,它是在 registerHost 的时候注册的:
private void registerHost(Host host) {
String[] aliases = host.findAliases();
this.mapper.addHost(host.getName(), aliases, host);
Container[] var3 = host.findChildren();
int var4 = var3.length;
for(int var5 = 0; var5 < var4; ++var5) {
Container container = var3[var5];
if (container.getState().isAvailable()) {
this.registerContext((Context)container);
}
}
this.findDefaultHost();
...
}
最后在 addContextVersion 函数中:
Mapper.ContextVersion newContextVersion = new Mapper.ContextVersion(version, path, slashCount, context, resources, welcomeResources);
if (wrappers != null) {
this.addWrappers(newContextVersion, wrappers);
}
Mapper.ContextList contextList = mappedHost.contextList;
Mapper.MappedContext mappedContext = (Mapper.MappedContext)exactFind(contextList.contexts, (String)path);
if (mappedContext == null) {
mappedContext = new Mapper.MappedContext(path, newContextVersion);
Mapper.ContextList newContextList = contextList.addContext(mappedContext, slashCount);
if (newContextList != null) {
this.updateContextList(mappedHost, newContextList);
this.contextObjectToContextVersionMap.put(context, newContextVersion);
}
}
context 和 contextVersion 放入 MappedContext,再放入 contextList 中。
context 配置在 server.xml 的 host 模块下面,代表一个 web 项目(war 包或其解压后的目录),而因为 Tomcat 开启了 autoDeploy 和 unpackWARs,所以文件中没有静态配置,而是由 Tomcat 进行动态加载。顺带一提的是 context 的 name 就是配置中的 path,在 setPath 函数中:
if (this.getName() == null) {
this.setName(this.path);
}
继续 internalMap,通过 uri 匹配到对应的 context 后,会将其放入 mappingData 中。(standardContext 就保存在 contextVersion 中)
还有一个重要属性 wrapper,获取 context 之后,会调用 internalMapWrapper 获取相应的 wrapper,先来看看 wrapper 的生成方式,context 中的 wrapper 在 ContextConfig 类中生成,这个类会解析 web.xml 并做相应处理,其 configureContext 函数一部分如下:
var35 = webxml.getServlets().values().iterator();
Iterator var7;
while(var35.hasNext()) {
ServletDef servlet = (ServletDef)var35.next();
Wrapper wrapper = this.context.createWrapper();
...
this.context.addChild(wrapper);
}
生成 wrapper 然后用 addChild 添加到 context 中,可以发现 wrapper 和 servlet 是对应的,中间有一大段将 servlet 相关数据存储在 wrapper 中的代码被我省略了,接下来回到 standardContext 类的 createWrapper 函数:
wrapper = new StandardWrapper();
wrapper 是一个 StandardWrapper 类,其他的没有什么特别操作了。在注册 context 的时候(registerContext),这些 wrapper 会经过 prepareWrapperMappingInfo 函数处理,合成一个 List:
private void prepareWrapperMappingInfo(Context context, Wrapper wrapper, List<WrapperMappingInfo> wrappers) {
String wrapperName = wrapper.getName();
boolean resourceOnly = context.isResourceOnlyServlet(wrapperName);
String[] mappings = wrapper.findMappings();
String[] var7 = mappings;
int var8 = mappings.length;
for(int var9 = 0; var9 < var8; ++var9) {
String mapping = var7[var9];
boolean jspWildCard = wrapperName.equals("jsp") && mapping.endsWith("/*");
wrappers.add(new WrapperMappingInfo(mapping, wrapper, jspWildCard, resourceOnly));
}
}
然后通过 addWrappers 分门别类组装成 MappedWrapper 放入 contextVersion:
private void addWrappers(Mapper.ContextVersion contextVersion, Collection<WrapperMappingInfo> wrappers) {
Iterator var3 = wrappers.iterator();
while(var3.hasNext()) {
WrapperMappingInfo wrapper = (WrapperMappingInfo)var3.next();
this.addWrapper(contextVersion, wrapper.getMapping(), wrapper.getWrapper(), wrapper.isJspWildCard(), wrapper.isResourceOnly());
}
}
protected void addWrapper(Mapper.ContextVersion context, String path, Wrapper wrapper, boolean jspWildCard, boolean resourceOnly) {
synchronized(context) {
String name;
Mapper.MappedWrapper newWrapper;
Mapper.MappedWrapper[] oldWrappers;
Mapper.MappedWrapper[] newWrappers;
if (path.endsWith("/*")) {
name = path.substring(0, path.length() - 2);
newWrapper = new Mapper.MappedWrapper(name, wrapper, jspWildCard, resourceOnly);
oldWrappers = context.wildcardWrappers;
newWrappers = new Mapper.MappedWrapper[oldWrappers.length + 1];
if (insertMap(oldWrappers, newWrappers, newWrapper)) {
context.Wrappers = newWrappers;
int slashCount = slashCount(newWrapper.name);
if (slashCount > context.nesting) {
context.nesting = slashCount;
}
}
} else if (path.startsWith("*.")) {
name = path.substring(2);
newWrapper = new Mapper.MappedWrapper(name, wrapper, jspWildCard, resourceOnly);
oldWrappers = context.extensionWrappers;
newWrappers = new Mapper.MappedWrapper[oldWrappers.length + 1];
if (insertMap(oldWrappers, newWrappers, newWrapper)) {
context.extensionWrappers = newWrappers;
}
} else if (path.equals("/")) {
Mapper.MappedWrapper newWrapper = new Mapper.MappedWrapper("", wrapper, jspWildCard, resourceOnly);
context.defaultWrapper = newWrapper;
} else {
if (path.length() == 0) {
name = "/";
} else {
name = path;
}
newWrapper = new Mapper.MappedWrapper(name, wrapper, jspWildCard, resourceOnly);
oldWrappers = context.exactWrappers;
newWrappers = new Mapper.MappedWrapper[oldWrappers.length + 1];
if (insertMap(oldWrappers, newWrappers, newWrapper)) {
context.exactWrappers = newWrappers;
}
}
}
}
主要有几个分类:
- wildcardWrappers:/* 通配符结尾的路径
- extensionWrappers:某一类后缀的路径
- defaultWrapper:/ 结尾的默认(首页?)路径
- exactWrappers:全匹配的详细路径
现在回到 internalMapWrapper,可以看到匹配成功后,会将 wrapper 放入 mappingData 中。
回到反射,下一步是 StandardHostValve 类,获取的 context 就是前面 internalMap 函数匹配到的那个:
Context context = request.getContext();
...
context.getPipeline().getFirst().invoke(request, response);
然后是 StandardContextValve 类:
public final void invoke(Request request, Response response) throws IOException, ServletException {
MessageBytes requestPathMB = request.getRequestPathMB();
if (!requestPathMB.startsWithIgnoreCase("/META-INF/", 0) && !requestPathMB.equalsIgnoreCase("/META-INF") && !requestPathMB.startsWithIgnoreCase("/WEB-INF/", 0) && !requestPathMB.equalsIgnoreCase("/WEB-INF")) {
Wrapper wrapper = request.getWrapper();
if (wrapper != null && !wrapper.isUnavailable()) {
try {
response.sendAcknowledgement();
} catch (IOException var6) {
this.container.getLogger().error(sm.getString("standardContextValve.acknowledgeException"), var6);
request.setAttribute("javax.servlet.error.exception", var6);
response.sendError(500);
return;
}
if (request.isAsyncSupported()) {
request.setAsyncSupported(wrapper.getPipeline().isAsyncSupported());
}
wrapper.getPipeline().getFirst().invoke(request, response);
} else {
response.sendError(404);
}
} else {
response.sendError(404);
}
}
然后是 StandardWrapperValve 类,首先会生成一个 Servlet 对象:
try {
if (!unavailable) {
servlet = wrapper.allocate();
}
}
wrapper 的 allocate 函数中,会先检查自身的 instance 属性中有没有加载好的 servlet,没有则会调用 loadServlet 进行加载:
servlet = (Servlet)instanceManager.newInstance(this.servletClass);
这里的 servletClass 来自 web.xml 中配置的类。
获取到 servlet 后,会调用 createFilterChain 创建 filterChain:
ApplicationFilterChain filterChain = ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
createFilterChain:
ApplicationFilterChain filterChain = null;
if (request instanceof Request) {
Request req = (Request)request;
if (Globals.IS_SECURITY_ENABLED) {
filterChain = new ApplicationFilterChain();
} else {
filterChain = (ApplicationFilterChain)req.getFilterChain();
if (filterChain == null) {
filterChain = new ApplicationFilterChain();
req.setFilterChain(filterChain);
}
}
} else {
filterChain = new ApplicationFilterChain();
}
首先该 request 对象中有没有存放有 filterChain,如果没有(比如第一次访问)则生成一个 ApplicationFilterChain 对象。继续往下看:
filterChain.setServlet(servlet);
filterChain.setServletSupportsAsync(wrapper.isAsyncSupported());
StandardContext context = (StandardContext)wrapper.getParent();
FilterMap[] filterMaps = context.findFilterMaps();
将 servlet 放入 filterChain,取出 context,再从 context 中取出 filterMaps,filterMaps 的赋值可以回到 configureContext:
var2 = webxml.getFilterMappings().iterator();
while(var2.hasNext()) {
FilterMap filterMap = (FilterMap)var2.next();
this.context.addFilterMap(filterMap);
}
简单来说也是从 web.xml 中来的。回到 createFilterChain:
String servletName = wrapper.getName();
FilterMap[] var10 = filterMaps;
int var11 = filterMaps.length;
int var12;
FilterMap filterMap;
ApplicationFilterConfig filterConfig;
for(var12 = 0; var12 < var11; ++var12) {
filterMap = var10[var12];
if (matchDispatcher(filterMap, dispatcher) && matchFiltersURL(filterMap, requestPath)) {
filterConfig = (ApplicationFilterConfig)context.findFilterConfig(filterMap.getFilterName());
if (filterConfig != null) {
filterChain.addFilter(filterConfig);
}
}
}
匹配上 DispatcherType 和 requestPath 就会加入 filterChain 中。
回到 StandardWrapperValve,创建完 filterChain 之后执行:
filterChain.doFilter(request.getRequest(), response.getResponse());
即:
if (this.pos < this.n) {
ApplicationFilterConfig filterConfig = this.filters[this.pos++];
try {
Filter filter = filterConfig.getFilter();
if (request.isAsyncSupported() && "false".equalsIgnoreCase(filterConfig.getFilterDef().getAsyncSupported())) {
request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", Boolean.FALSE);
}
if (Globals.IS_SECURITY_ENABLED) {
Principal principal = ((HttpServletRequest)request).getUserPrincipal();
Object[] args = new Object[]{request, response, this};
SecurityUtil.doAsPrivilege("doFilter", filter, classType, args, principal);
} else {
filter.doFilter(request, response, this);
}
} catch (ServletException | RuntimeException | IOException var15) {
throw var15;
} catch (Throwable var16) {
Throwable e = ExceptionUtils.unwrapInvocationTargetException(var16);
ExceptionUtils.handleThrowable(e);
throw new ServletException(sm.getString("filterChain.filter"), e);
}
}
依次调用这些 filter 的 doFilter 函数,在处理完 filter 之后:
try {
if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
lastServicedRequest.set(request);
lastServicedResponse.set(response);
}
if (request.isAsyncSupported() && !this.servletSupportsAsync) {
request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", Boolean.FALSE);
}
if (request instanceof HttpServletRequest && response instanceof HttpServletResponse && Globals.IS_SECURITY_ENABLED) {
Principal principal = ((HttpServletRequest)request).getUserPrincipal();
Object[] args = new Object[]{request, response};
SecurityUtil.doAsPrivilege("service", this.servlet, classTypeUsedInService, args, principal);
} else {
this.servlet.service(request, response);
}
}
就开始调用 servlet 的 service 函数,往后就是项目的具体实现代码了,就不提了。
Orz