Tomcat 소스 코드 분석: 4. Tomcat 초기 화
121859 단어 Tomcat 소스 코드 분석Tomcat
org. apache. catalina. startup. bootstrap 류 는 Tomcat 의 입구 입 니 다. IDE 에서 Tomcat 를 실행 하여 디 버 깅 을 하려 면 이 종 류 를 직접 main 을 실행 하 는 방법 을 찾 을 수 있 습 니 다.
Bootstrap 의 main 방법 을 보 겠 습 니 다.
public static void main(String args[]) {
if (daemon == null) {
// Bootstrap
Bootstrap bootstrap = new Bootstrap();
try {
// init Tomcat ,
bootstrap.init();
} catch (Throwable t) {
handleThrowable(t);
t.printStackTrace();
return;
}
daemon = bootstrap;
} else {
Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
}
try {
String command = "start";
// args , command start
if (args.length > 0) {
command = args[args.length - 1];
}
if (command.equals("startd")) {
args[args.length - 1] = "start";
daemon.load(args);
daemon.start();
} else if (command.equals("stopd")) {
args[args.length - 1] = "stop";
daemon.stop();
} else if (command.equals("start")) {
daemon.setAwait(true);
// Tomcat , start
daemon.load(args);
daemon.start();
} else if (command.equals("stop")) {
daemon.stopServer(args);
} else if (command.equals("configtest")) {
daemon.load(args);
if (null==daemon.getServer()) {
System.exit(1);
}
System.exit(0);
} else {
log.warn("Bootstrap: command \"" + command + "\" does not exist.");
}
} catch (Throwable t) {
// Unwrap the Exception for clearer error reporting
if (t instanceof InvocationTargetException &&
t.getCause() != null) {
t = t.getCause();
}
handleThrowable(t);
t.printStackTrace();
System.exit(1);
}
}
위의 코드 는 아래 와 같다.
Bootstrap bootstrap = new Bootstrap();
/*
*
*/
bootstrap.init();
daemon = bootstrap;
daemon.setAwait(true);
daemon.load(args);
/*
*
*/
daemon.start();
이 절 은 초기 화 만 말 하기 때문에 init, load 방법, start 방법 다음 절 은 분석 합 니 다.
init 방법
public void init() throws Exception {
/*
* ClassLoader
*/
initClassLoaders();
Thread.currentThread().setContextClassLoader(catalinaLoader);
SecurityClassLoad.securityClassLoad(catalinaLoader);
// Load our startup class and call its process() method
if (log.isDebugEnabled())
log.debug("Loading startup class");
/*
* classLoader org.apache.catalina.startup.Catalina
*/
Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
Object startupInstance = startupClass.getConstructor().newInstance();
// Set the shared extensions class loader
if (log.isDebugEnabled())
log.debug("Setting startup class properties");
String methodName = "setParentClassLoader";
Class<?> paramTypes[] = new Class[1];
paramTypes[0] = Class.forName("java.lang.ClassLoader");
Object paramValues[] = new Object[1];
paramValues[0] = sharedLoader;
Method method =
startupInstance.getClass().getMethod(methodName, paramTypes);
/*
* Catalina setParentClassLoader
*/
method.invoke(startupInstance, paramValues);
//
catalinaDaemon = startupInstance;
}
위의 init 방법 을 간단하게 정리 한 다음 에 순서대로 분석 합 니 다.
String methodName = "setParentClassLoader";
Class<?> paramTypes[] = new Class[1];
paramTypes[0] = Class.forName("java.lang.ClassLoader");
Object paramValues[] = new Object[1];
paramValues[0] = sharedLoader;
Method method = startupInstance.getClass().getMethod(methodName, paramTypes);
method.invoke(startupInstance, paramValues);
initClassLoaders 방법
private void initClassLoaders() {
try {
commonLoader = createClassLoader("common", null);
if( commonLoader == null ) {
// no config file, default to this loader - we might be in a 'single' env.
commonLoader=this.getClass().getClassLoader();
}
catalinaLoader = createClassLoader("server", commonLoader);
sharedLoader = createClassLoader("shared", commonLoader);
} catch (Throwable t) {
handleThrowable(t);
log.error("Class loader creation threw exception", t);
System.exit(1);
}
}
실제로 createClassLoader 방법 을 호출 했 습 니 다.
private ClassLoader createClassLoader(String name, ClassLoader parent)
throws Exception {
/*
* conf/catalina.properties name.loader ,
*/
String value = CatalinaProperties.getProperty(name + ".loader");
/*
* value , parentClassLoader
*/
if ((value == null) || (value.equals("")))
return parent;
/*
* value ${catalina.base}、${catalina.home}
*/
value = replace(value);
List<Repository> repositories = new ArrayList<>();
/*
* "" () ,
*/
String[] repositoryPaths = getPaths(value);
/*
* path, Repository
*/
for (String repository : repositoryPaths) {
// Check for a JAR URL repository
try {
@SuppressWarnings("unused")
URL url = new URL(repository);
repositories.add(
new Repository(repository, RepositoryType.URL));
continue;
} catch (MalformedURLException e) {
// Ignore
//e.printStackTrace();
}
// Local repository
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));
}
}
/*
* URLClassLoader, ,
*/
return ClassLoaderFactory.createClassLoader(repositories, parent);
}
간단하게 정리 해 볼 게 요. initClassLoaders.
그래서 init 방법 은 분석 이 끝 났 습 니 다. 주로 세 개의 ClassLoader 를 만 들 었 고 ClassLoader 를 Catalina 에 설정 하 였 습 니 다.
이어서 load 방법 을 보 겠 습 니 다.
load 방법
private void load(String[] arguments)
throws Exception {
// Call the load() method
String methodName = "load";
Object param[];
Class<?> paramTypes[];
if (arguments==null || arguments.length==0) {
paramTypes = null;
param = null;
} else {
paramTypes = new Class[1];
paramTypes[0] = arguments.getClass();
param = new Object[1];
param[0] = arguments;
}
Method method =
catalinaDaemon.getClass().getMethod(methodName, paramTypes);
if (log.isDebugEnabled())
log.debug("Calling startup class " + method);
method.invoke(catalinaDaemon, param);
}
위 에 서 는 실제로 카 탈리 나 류 의 load 방법 을 반사 적 으로 호출 했다.
public void load() {
if (loaded) {
return;
}
loaded = true;
long t1 = System.nanoTime();
initDirs();
// Before digester - it may be needed
initNaming();
// Digester XML
Digester digester = createStartDigester();
InputSource inputSource = null;
InputStream inputStream = null;
File file = null;
try {
try {
/**
* server.xml File
*/
file = configFile();
inputStream = new FileInputStream(file);
/**
* JDK SAX server.xml
* InputSource
*/
inputSource = new InputSource(file.toURI().toURL().toString());
} catch (Exception e) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("catalina.configFail", file), e);
}
}
/*
* server.xml
* ClassLoader conf/server.xml
*/
if (inputStream == null) {
try {
inputStream = getClass().getClassLoader()
.getResourceAsStream(getConfigFile());
inputSource = new InputSource
(getClass().getClassLoader()
.getResource(getConfigFile()).toString());
} catch (Exception e) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("catalina.configFail",
getConfigFile()), e);
}
}
}
/*
* server.xml
* ClassLoader server-embed.xml
*/
if (inputStream == null) {
try {
inputStream = getClass().getClassLoader()
.getResourceAsStream("server-embed.xml");
inputSource = new InputSource
(getClass().getClassLoader()
.getResource("server-embed.xml").toString());
} catch (Exception e) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("catalina.configFail",
"server-embed.xml"), e);
}
}
}
/*
* server.xml server-embed.xml, ,return
*/
if (inputStream == null || inputSource == null) {
if (file == null) {
log.warn(sm.getString("catalina.configFail",
getConfigFile() + "] or [server-embed.xml]"));
} else {
log.warn(sm.getString("catalina.configFail",
file.getAbsolutePath()));
if (file.exists() && !file.canRead()) {
log.warn("Permissions incorrect, read permission is not allowed on the file.");
}
}
return;
}
try {
inputSource.setByteStream(inputStream);
// Catalina, Digester , ,
digester.push(this);
/**
* server.xml ,
* 1、Server:org.apache.catalina.core.StandardServer
* 2、Server :
* 1、org.apache.catalina.startup.VersionLoggerListener
* 2、org.apache.catalina.core.AprLifecycleListener
* 3、org.apache.catalina.core.JreMemoryLeakPreventionListener
* 4、org.apache.catalina.mbeans.GlobalResourcesLifecycleListener
* 5、org.apache.catalina.core.ThreadLocalLeakPreventionListener
* 3、Service:org.apache.catalina.core.StandardService
* 4、2 Connector,
* 1、org.apache.coyote.http11.Http11NioProtocol: HTTP
* 2、org.apache.coyote.http11.Http11AprProtocol: AJP
* 5、Engine:org.apache.catalina.core.StandardEngine
* 6、Host:org.apache.catalina.core.StandardHost
*/
digester.parse(inputSource);
} catch (SAXParseException spe) {
log.warn("Catalina.start using " + getConfigFile() + ": " +
spe.getMessage());
return;
} catch (Exception e) {
log.warn("Catalina.start using " + getConfigFile() + ": " , e);
return;
}
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
// Ignore
}
}
}
/**
* Server Catalina
*/
getServer().setCatalina(this);
getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());
// Stream redirection
initStreams();
// Start the new server
try {
/**
* Server init ,
*/
getServer().init();
} catch (LifecycleException e) {
if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {
throw new java.lang.Error(e);
} else {
log.error("Catalina.start", e);
}
}
long t2 = System.nanoTime();
if(log.isInfoEnabled()) {
log.info("Initialization processed in " + ((t2 - t1) / 1000000) + " ms");
}
}
위의 내용 을 간단하게 요약 하면:
다음은 서버 의 init 방법 을 살 펴 보 겠 습 니 다. StandardServer 류 는 LifecycleMBeanBase 를 계 속 했 기 때문에 실제 적 으로 템 플 릿 방법 인 initInternal 을 호출 했 습 니 다. 모 르 겠 습 니 다. 제 가 말 한 Lifestyle 실행 체 제 를 돌아 볼 수 있 습 니 다.
protected void initInternal() throws LifecycleException {
/*
* initInternal, JMX
*/
super.initInternal();
/*
* StringCache MBeanFactory JMX
*/
onameStringCache = register(new StringCache(), "type=StringCache");
// Register the MBeanFactory
MBeanFactory factory = new MBeanFactory();
factory.setContainer(this);
onameMBeanFactory = register(factory, "type=MBeanFactory");
// Register the naming resources
globalNamingResources.init();
if (getCatalina() != null) {
ClassLoader cl = getCatalina().getParentClassLoader();
while (cl != null && cl != ClassLoader.getSystemClassLoader()) {
if (cl instanceof URLClassLoader) {
URL[] urls = ((URLClassLoader) cl).getURLs();
for (URL url : urls) {
if (url.getProtocol().equals("file")) {
try {
File f = new File (url.toURI());
if (f.isFile() &&
f.getName().endsWith(".jar")) {
ExtensionValidator.addSystemResource(f);
}
} catch (URISyntaxException e) {
// Ignore
} catch (IOException e) {
// Ignore
}
}
}
}
cl = cl.getParent();
}
}
// Service init ,
for (int i = 0; i < services.length; i++) {
services[i].init();
}
}
여기 역시 Service 를 호출 하 는 init 방법 입 니 다. 이 Service 는 Standard Service 이 고 LifecycleMBeanBase 도 계승 되 었 기 때문에 템 플 릿 방법 initInternal 도 호출 되 었 습 니 다.
protected void initInternal() throws LifecycleException {
/*
* initInternal, JMX
*/
super.initInternal();
/*
* Engine init
*/
if (engine != null) {
engine.init();
}
/*
* server.xml Service Executor
* Executor init
* Executor Tomcat JUC Executor,
* Lifecycle, ,
* StandardThreadExecutor
*/
for (Executor executor : findExecutors()) {
if (executor instanceof JmxEnabled) {
((JmxEnabled) executor).setDomain(getDomain());
}
executor.init();
}
mapperListener.init();
// Initialize our defined Connectors
synchronized (connectorsLock) {
/*
* Connector init
* 2 Connector,1 AJP,1 Http
*/
for (Connector connector : connectors) {
try {
connector.init();
} catch (Exception e) {
String message = sm.getString(
"standardService.connector.initFailed", connector);
log.error(message, e);
if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"))
throw new LifecycleException(message);
}
}
}
}
간단하게 요약 하면 Standard Service 의 initInternal 방법:
protected void initInternal() throws LifecycleException {
// Ensure that a Realm is present before any attempt is made to start
// one. This will create the default NullRealm if necessary.
getRealm();
super.initInternal();
}
간단 합 니 다. Realm 을 가 져 오고 자신 을 JMX 에 등록 하 며 부모 클래스 의 initInternal () 방법 을 호출 했 습 니 다.
ContainerBase
protected void initInternal() throws LifecycleException {
BlockingQueue<Runnable> startStopQueue = new LinkedBlockingQueue<>();
/*
* , coreThread=1 maxThread=1,
* start
*/
startStopExecutor = new ThreadPoolExecutor(
getStartStopThreadsInternal(),
getStartStopThreadsInternal(), 10, TimeUnit.SECONDS,
startStopQueue,
new StartStopThreadFactory(getName() + "-startStop-"));
/*
* ThreadPoolExecutor.allowCoreThreadTimeOut true
* Task 10s, coreThread
*/
startStopExecutor.allowCoreThreadTimeOut(true);
super.initInternal();
}
이어서 커 넥 터 의 initInternal 방법 을 살 펴 보 겠 습 니 다. 먼저 커 넥 터 의 구조 방법 을 살 펴 보 겠 습 니 다.
public Connector(String protocol) {
/*
* protocol HTTP/1.1, protocolHandlerClassName = org.apache.coyote.http11.Http11NioProtocol
* AJP/1.3, protocolHandlerClassName = org.apache.coyote.ajp.AjpAprProtocol
*/
setProtocol(protocol);
// Instantiate protocol handler
ProtocolHandler p = null;
try {
/*
* protocolHandlerClassName,
*/
Class<?> clazz = Class.forName(protocolHandlerClassName);
p = (ProtocolHandler) clazz.getConstructor().newInstance();
} catch (Exception e) {
log.error(sm.getString(
"coyoteConnector.protocolHandlerInstantiationFailed"), e);
} finally {
this.protocolHandler = p;
}
if (Globals.STRICT_SERVLET_COMPLIANCE) {
uriCharset = StandardCharsets.ISO_8859_1;
} else {
uriCharset = StandardCharsets.UTF_8;
}
}
public void setProtocol(String protocol) {
boolean aprConnector = AprLifecycleListener.isAprAvailable() &&
AprLifecycleListener.getUseAprConnector();
if ("HTTP/1.1".equals(protocol) || protocol == null) {
if (aprConnector) {
setProtocolHandlerClassName("org.apache.coyote.http11.Http11AprProtocol");
} else {
setProtocolHandlerClassName("org.apache.coyote.http11.Http11NioProtocol");
}
} else if ("AJP/1.3".equals(protocol)) {
if (aprConnector) {
setProtocolHandlerClassName("org.apache.coyote.ajp.AjpAprProtocol");
} else {
setProtocolHandlerClassName("org.apache.coyote.ajp.AjpNioProtocol");
}
} else {
setProtocolHandlerClassName(protocol);
}
}
/*
* Nio2Endpoint , ,
*
*/
public Http11Nio2Protocol() {
super(new Nio2Endpoint());
}
protected void initInternal() throws LifecycleException {
/*
* JMX
*/
super.initInternal();
/*
* CoyoteAdapter ,
* Request、Response HttpServletRequest、HttpServletResponse
*/
adapter = new CoyoteAdapter(this);
protocolHandler.setAdapter(adapter);
// Make sure parseBodyMethodsSet has a default
if (null == parseBodyMethodsSet) {
setParseBodyMethods(getParseBodyMethods());
}
if (protocolHandler.isAprRequired() && !AprLifecycleListener.isAprAvailable()) {
throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerNoApr",
getProtocolHandlerClassName()));
}
if (AprLifecycleListener.isAprAvailable() && AprLifecycleListener.getUseOpenSSL() &&
protocolHandler instanceof AbstractHttp11JsseProtocol) {
AbstractHttp11JsseProtocol<?> jsseProtocolHandler =
(AbstractHttp11JsseProtocol<?>) protocolHandler;
if (jsseProtocolHandler.isSSLEnabled() &&
jsseProtocolHandler.getSslImplementationName() == null) {
// OpenSSL is compatible with the JSSE configuration, so use it if APR is available
jsseProtocolHandler.setSslImplementationName(OpenSSLImplementation.class.getName());
}
}
try {
/*
* protocolHandler init
* HTTP/1.1 , org.apache.coyote.http11.Http11NioProtocol
*/
protocolHandler.init();
} catch (Exception e) {
throw new LifecycleException(
sm.getString("coyoteConnector.protocolHandlerInitializationFailed"), e);
}
}
이 어 org. apache. coyote. http 11. Http 11 NioProtocol 의 init 방법 을 호출 하 였 으 며, 실제로 부모 클래스 org. apache. coyote. AbstractProtocol 의 init 를 호출 하 였 다.
public void init() throws Exception {
if (getLog().isInfoEnabled()) {
getLog().info(sm.getString("abstractProtocolHandler.init", getName()));
}
if (oname == null) {
// Component not pre-registered so register it
oname = createObjectName();
if (oname != null) {
Registry.getRegistry(null, null).registerComponent(this, oname, null);
}
}
if (this.domain != null) {
rgOname = new ObjectName(domain + ":type=GlobalRequestProcessor,name=" + getName());
Registry.getRegistry(null, null).registerComponent(
getHandler().getGlobal(), rgOname, null);
}
String endpointName = getName();
endpoint.setName(endpointName.substring(1, endpointName.length()-1));
endpoint.setDomain(domain);
/* endpoint init, Nio2Endpoint ,
* Http11Nio2Protocol ,
*/
endpoint.init();
}
Http11Nio2Protocol 의 init 방법 을 호출 하 는 것 도 실제 적 으로 부모 클래스 org. apache. tomcat. util. net. AbstractEndpoint 를 호출 하 는 init 입 니 다.
public void init() throws Exception {
if (bindOnInit) {
/*
* bind() ,
*/
bind();
bindState = BindState.BOUND_ON_INIT;
}
if (this.domain != null) {
// Register endpoint (as ThreadPool - historical name)
oname = new ObjectName(domain + ":type=ThreadPool,name=\"" + getName() + "\"");
Registry.getRegistry(null, null).registerComponent(this, oname, null);
for (SSLHostConfig sslHostConfig : findSslHostConfigs()) {
registerJmx(sslHostConfig);
}
}
}
Nio2Endpoint
public void bind() throws Exception {
// Create worker collection
/*
* server.xml service Executor,
* createExecutor(), Executor
*/
if ( getExecutor() == null ) {
createExecutor();
}
if (getExecutor() instanceof ExecutorService) {
threadGroup = AsynchronousChannelGroup.withThreadPool((ExecutorService) getExecutor());
}
// AsynchronousChannelGroup currently needs exclusive access to its executor service
if (!internalExecutor) {
log.warn(sm.getString("endpoint.nio2.exclusiveExecutor"));
}
/*
* NIO ,
*/
serverSock = AsynchronousServerSocketChannel.open(threadGroup);
socketProperties.setProperties(serverSock);
InetSocketAddress addr = (getAddress()!=null?new InetSocketAddress(getAddress(),getPort()):new InetSocketAddress(getPort()));
serverSock.bind(addr,getAcceptCount());
// Initialize thread count defaults for acceptor, poller
if (acceptorThreadCount != 1) {
// NIO2 does not allow any form of IO concurrency
acceptorThreadCount = 1;
}
// Initialize SSL if needed
initialiseSsl();
}
지금까지 Tomcat 초기 화 완료.
총결산
간단하게 요약 하면 Tomcat 초기 화 는 주로 몇 가지 로 나 뉜 다.
그래서 Tomcat 이 초기 화 되 었 을 때 War 프로젝트 를 불 러 오지 않 았 습 니 다. start () 가 시 작 될 때 만 프로젝트 를 불 러 옵 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Windows에 Jenkins를 설치하지 않고 사용하는 방법windows 10 OpenJDK tomcat Jenkins(war) 1. 다음 OpenJDK 사이트로 이동 2. 다운로드 링크를 누르십시오(현시점(2019/10/19)에서는 13이 최신) 3. Windows zip...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.