传统企业项目部署采用方式位打war,后部署到tomcat中,而且SpringBoot的横空出世,以jar包方式直接部署企业应用的,开始流行。
身为一个java开发者,对于为什么jar包的方式也可以直接启动web服务,很是费解。通过仔细研究,然后得出下列结论。注:如有读者感觉有问题,欢迎指证,我会第一时间修改。
- Tomcat启动war服务是会 执行下列指令
[root@localhost tomcat]# ./bin/startup.sh
一、 查看 /bin/startup.sh 文件,内容如下图:
如上图红框标识,startup.sh 继续运行 catalina.sh。
二、查看 catalina.sh 文件
查看catalina.sh 文件得知,此脚本
"$_RUNJAVA" $JAVA_OPTS $CATALINA_OPTS \
//运行方法Bootstrap 类中main方法携带的参数
-Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" -classpath "$CLASSPATH" \
-Djava.security.manager \
-Djava.security.policy=="$CATALINA_BASE"/conf/catalina.policy \
-Dcatalina.base="$CATALINA_BASE" \
-Dcatalina.home="$CATALINA_HOME" \
-Djava.io.tmpdir="$CATALINA_TMPDIR" \
org.apache.catalina.startup.Bootstrap "$@" start \
//此方法打印日志保存到
>> "$CATALINA_BASE"/logs/catalina.out 2>&1 &
三、反编译 tomcat/bin/bootstrap.jar ,获得org.apache.catalina.startup.Bootstrap类
查看此类代码:
1.类中main方法
//Bootstrap类中main, 上图shell脚本会执行这个方法,并传入参数。
public static void main(String[] args) {
if (daemon == null) {
daemon = new Bootstrap();
try {
//BootStrap类初始化信息
daemon.init();
} catch (Throwable var3) {
var3.printStackTrace();
return;
}
}
try {
String command = "start";
if (args.length > 0) {
command = args[args.length - 1];
}
if (command.equals("startd")) {
args[0] = "start";
//启动线程对象加载参数
daemon.load(args);
//tomcat开始启动
daemon.start();
} else if (command.equals("stopd")) {
args[0] = "stop";
daemon.stop();
} else if (command.equals("start")) {
daemon.setAwait(true);
daemon.load(args);
daemon.start();
} else if (command.equals("stop")) {
daemon.stopServer(args);
} else {
log.warn("Bootstrap: command \"" + command + "\" does not exist.");
}
} catch (Throwable var2) {
var2.printStackTrace();
}
}
2.daemon.init();方法具体内容
public void init() throws Exception {
//设置Catalina的路径到系统属性
this.setCatalinaHome();
this.setCatalinaBase();
//初始化类加载器
this.initClassLoaders();
//类加载器设置到当前线程
Thread.currentThread().setContextClassLoader(this.catalinaLoader);
SecurityClassLoad.securityClassLoad(this.catalinaLoader);
if (log.isDebugEnabled()) {
log.debug("Loading startup class");
}
Class startupClass = this.catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
Object startupInstance = startupClass.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;
}
- daemon.start(); 方法内容
public void start() throws Exception {
if (this.catalinaDaemon == null) {
this.init();
}
//catalinaDaemon 对象 对应init方法中实例化的 org.apache.catalina.startup.Catalina
//使用反射调用Catalina 类中中 start()方法
Method method = this.catalinaDaemon.getClass().getMethod("start", (Class[])null);
method.invoke(this.catalinaDaemon, (Object[])null);
}
4.查看Catalina类中的start方法
public void start() {
if (this.server == null) {
//服务加载
this.load();
}
long t1 = System.currentTimeMillis();
if (this.server instanceof Lifecycle) {
try {
((Lifecycle)this.server).start();
} catch (LifecycleException var7) {
log.error("Catalina.start: ", var7);
}
}
long t2 = System.currentTimeMillis();
if (log.isInfoEnabled()) {
log.info("Server startup in " + (t2 - t1) + " ms");
}
try {
if (this.useShutdownHook) {
if (this.shutdownHook == null) {
this.shutdownHook = new Catalina.CatalinaShutdownHook();
}
Runtime.getRuntime().addShutdownHook(this.shutdownHook);
}
} catch (Throwable var6) {
}
if (this.await) {
this.await();
this.stop();
}
}
4.查看Catalina类中的load方法
public void load() {
this.initDirs();
this.initNaming();
Digester digester = this.createStartDigester();
long t1 = System.currentTimeMillis();
Exception ex = null;
InputSource inputSource = null;
InputStream inputStream = null;
File file = null;
try {
file = this.configFile();
inputStream = new FileInputStream(file);
inputSource = new InputSource("file://" + file.getAbsolutePath());
} catch (Exception var14) {
}
if (inputStream == null) {
try {
inputStream = this.getClass().getClassLoader().getResourceAsStream(this.getConfigFile());
inputSource = new InputSource(this.getClass().getClassLoader().getResource(this.getConfigFile()).toString());
} catch (Exception var13) {
}
}
if (inputStream == null) {
try {
inputStream = this.getClass().getClassLoader().getResourceAsStream("server-embed.xml");
inputSource = new InputSource(this.getClass().getClassLoader().getResource("server-embed.xml").toString());
} catch (Exception var12) {
}
}
if (inputStream == null && file != null) {
log.warn("Can't load server.xml from " + file.getAbsolutePath());
} else {
try {
inputSource.setByteStream((InputStream)inputStream);
digester.push(this);
digester.parse(inputSource);
((InputStream)inputStream).close();
} catch (Exception var11) {
log.warn("Catalina.start using " + this.getConfigFile() + ": ", var11);
return;
}
this.initStreams();
if (this.server instanceof Lifecycle) {
try {
//tomcat服务初始化
//此处为调用Catalina父类Embedded的父类StandardService 中 initialize 方法
//public class Catalina extends Embedded {
//public class Embedded extends StandardService implements Lifecycle {
this.server.initialize();
} catch (LifecycleException var10) {
log.error("Catalina.start", var10);
}
}
long t2 = System.currentTimeMillis();
if (log.isInfoEnabled()) {
log.info("Initialization processed in " + (t2 - t1) + " ms");
}
}
}
5.查看 org.apache.catalina.core.StandardService;类中 initialize() 方法
public void initialize() throws LifecycleException {
if (this.initialized) {
if (log.isInfoEnabled()) {
log.info(sm.getString("standardService.initialize.initialized"));
}
} else {
this.initialized = true;
if (this.oname == null) {
try {
Container engine = this.getContainer();
this.domain = engine.getName();
this.oname = new ObjectName(this.domain + ":type=Service,serviceName=" + this.name);
this.controller = this.oname;
//服务器注册主键
Registry.getRegistry((Object)null, (Object)null).registerComponent(this, this.oname, (String)null);
} catch (Exception var4) {
log.error(sm.getString("standardService.register.failed", this.domain), var4);
}
}
if (this.server == null) {
ServerFactory.getServer().addService(this);
}
synchronized(this.connectors) {
for(int i = 0; i < this.connectors.length; ++i) {
this.connectors[i].initialize();
}
}
}
}
查看 Registry.getRegistry((Object)null, (Object)null).registerComponent(this, this.oname, (String)null);
public void registerComponent(Object bean, ObjectName oname, String type)
throws Exception
{
if( log.isDebugEnabled() ) {
log.debug( "Managed= "+ oname);
}
if( bean ==null ) {
log.error("Null component " + oname );
return;
}
try {
if( type==null ) {
type=bean.getClass().getName();
}
ManagedBean managed = findManagedBean(null, bean.getClass(), type);
// The real mbean is created and registered
DynamicMBean mbean = managed.createMBean(bean);
if( getMBeanServer().isRegistered( oname )) {
if( log.isDebugEnabled()) {
log.debug("Unregistering existing component " + oname );
}
getMBeanServer().unregisterMBean( oname );
}
//这里会解析web.xml,并且会找到其中配置
/*
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
*/
getMBeanServer().registerMBean( mbean, oname);
} catch( Exception ex) {
log.error("Error registering " + oname, ex );
throw ex;
}
}
下一篇会继续tomcat的加载过程的验证,并会陆续更新所有的spring容器加载的过程,希望读者可以仔细阅读,如果感觉描述不对的,请指正,会及时修改。
本人希望通过一步一步看源码然后,再得知整体的框架的设计的原理,这种方法虽然比较浪费时间,但是还是可以学习到知识。