设为首页 收藏本站
查看: 713|回复: 0

[经验分享] 深入Tomcat(一)

[复制链接]

尚未签到

发表于 2017-1-17 08:07:20 | 显示全部楼层 |阅读模式
  java不难,难的是那些封装好的框架和工具,由于我们对这些工具没有深入的了解,所以在开发时经常是东查西凑,也就不难想到成果物的质量。所以决定从现在开始阅读Tomcat代码。每天一点点,坚持就是胜利。    
  版本:6.0.18 
  启动类:org.apache.catalina.startup包下的Bootstrap,下面是main方法:

    public static void main(String args[]) {
if (daemon == null) {
daemon = new Bootstrap();
try {
daemon.init();
} catch (Throwable t) {
t.printStackTrace();
return;
}
}
try {
String command = "started";
if (args.length > 0) {
command = args[args.length - 1];
}
if (command.equals("startd")) {
args[0] = "start";
daemon.load(args);
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 t) {
t.printStackTrace();
}
}
  首先创建启动类的实例,调用init方法:

    public void init()
throws Exception
{
// Set Catalina path
setCatalinaHome();
setCatalinaBase();
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");
Class startupClass =
catalinaLoader.loadClass
("org.apache.catalina.startup.Catalina");
Object startupInstance = startupClass.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);
method.invoke(startupInstance, paramValues);
catalinaDaemon = startupInstance;
}
  setCatalinaHome方法是设置Tomcat安装目录的,setCatalinaBase 是设置Tomcat的工作目录的。
  Tomcat是根据工作目录来寻找运行实例所需的资源的。
  如果你只安装了一个Tomcat软件,但是想运行多个Tomcat实例,或者想把Tomcat的工作目录设置在其它位置,那么就需要设置CATALINA_BASE变量了。以下是设置多个Tomcat实例运行的方法。我使用的是Tomcat安装版本。以启动两个Tomcat实例为例。
  这是Tomcat的安装路径:
  
DSC0000.jpg
  每一个Tomcat实例都有自己的工作目录,它们共享一个安装目录。
  bin,lib这两个文件夹是共享的,存放安装文件和lib库。
  conf,logs,temp,work,webapps这几个目录是每个Tomcat实例私有的,我们只需要拷贝这几个目录就能实现运行多个实例的目的。现在我对文件夹进行了改造:
DSC0001.jpg

  新建两个工作目录Tomcat 6.0_1和Tomcat 6.0_2,将conf,logs,temp,work,webapps这五个文件夹分别拷贝到这两个工作目录下,为了更好的区分安装目录与工作目录,我将原始的安装目录下除了bin和lib文件夹以外全部删除。这样每个实例的工作目录就配好了。接下来需要配置Tomcat实例运行需要的资源---端口号,我修改了每个实例下面的server.xml。将shutdown端口号,还有http监听端口号,ajp端口号全部修改,我这里把监听http端口号设置成8080,另一个设置成8888。最后就是启动了。我用的是安装版本,不是启动bat文件的。
  双击安装目录下的tomcat6w.exe,点击java选项卡,修改-Dcatalina.base=C:\Program Files\Apache Software Foundation\Tomcat 6.0_1,点击应用,确定,双击tomcat6.exe,启动第一个实例。
  
DSC0002.jpg
  再双击安装目录下的tomcat6w.exe,点击java选项卡,修改-Dcatalina.base=C:\Program Files\Apache Software Foundation\Tomcat 6.0_2,点击应用,确定,双击tomcat6.exe,启动第二个实例。
  
DSC0003.jpg
  如果是用startup.bat文件启动,同样修改CATALINA_BASE指向的工作目录即可。
  说完了安装目录和工作目录的作用和区别。我们来看看setCatalinaHome方法。

   /**
* Set the <code>catalina.home</code> System property to the current
* working directory if it has not been set.
*/
private void setCatalinaHome() {
if (System.getProperty("catalina.home") != null)
return;
File bootstrapJar =
new File(System.getProperty("user.dir"), "bootstrap.jar");
if (bootstrapJar.exists()) {
try {
System.setProperty
("catalina.home",
(new File(System.getProperty("user.dir"), ".."))
.getCanonicalPath());
} catch (Exception e) {
// Ignore
System.setProperty("catalina.home",
System.getProperty("user.dir"));
}
} else {
System.setProperty("catalina.home",
System.getProperty("user.dir"));
}
}
  如果系统变量中已经设置了catalina.home,直接返回。
  如果没有设定,则使用user.dir系统变量来获取用户的工作目录,这里说的用户工作目录不同于上面所说的Tomcat的工作目录。 关于用户的工作目录,请参考我的博客:用户工作目录
  如果用户的工作目录里含有bootstrap.jar,那么认为该目录的上一层目录是Tomcat的安装目录。事实上,Tomcat安装后,在bin下会存在该jar文件,并且通过查看catalina.bat文件可以得知,Tomcat的用户工作目录是%Tomcat_Home% /bin。这就不难解释为什么在Web工程下,获取user.dir系统属性是Tomcat安装目录下的bin目录了。但是这个属性的值是可以变化的(Tomcat的版本不同,该目录也不同,一般是%Tomcat_Home% /bin或者%Tomcat_Home%这两个目录 ),只要更改了运行java命令(启动Tomcat的org.apache.catalina.startup.Bootstrap)的目录,这个值就会相应的改变(通过修改tomcat6w.exe或者bat文件)。所以我们在编写程序时尽量不要使用此属性,尤其在cmd命令行和Eclipse共用时。
  这样catalina.home就被设置成Tomcat的安装目录了。
  下面是设置Tomcat工作目录的方法

    /**
* Set the <code>catalina.base</code> System property to the current
* working directory if it has not been set.
*/
private void setCatalinaBase() {
if (System.getProperty("catalina.base") != null)
return;
if (System.getProperty("catalina.home") != null)
System.setProperty("catalina.base",
System.getProperty("catalina.home"));
else
System.setProperty("catalina.base",
System.getProperty("user.dir"));
}
  这个方法首先检查Tomcat的工作目录(catalina.base)是否已经设置了(参考上文安装目录和工作目录的区别)。如果已经设置,返回,否则如果Tomcat的安装目录(catalina.home)不为空,则设置工作目录为Tomcat的安装目录。如果安装目录没有设置,则认为user.dir为Tomcat的工作目录。
  OK,接下来初始化Tomcat的类装载器。
  下面是初始化类装载器的相关方法。
  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) {
log.error("Class loader creation threw exception", t);
System.exit(1);
}
}
  从这个方法可以看出,Tomcat使用了三个类装载器,分别是commonLoader, catalinaLoader,sharedLoader。
  createClassLoader()方法

    private ClassLoader createClassLoader(String name, ClassLoader parent)
throws Exception {
String value = CatalinaProperties.getProperty(name + ".loader");
if ((value == null) || (value.equals("")))
return parent;
ArrayList repositoryLocations = new ArrayList();
ArrayList repositoryTypes = new ArrayList();
int i;
StringTokenizer tokenizer = new StringTokenizer(value, ",");
while (tokenizer.hasMoreElements()) {
String repository = tokenizer.nextToken();
// Local repository
boolean replace = false;
String before = repository;
while ((i=repository.indexOf(CATALINA_HOME_TOKEN))>=0) {
replace=true;
if (i>0) {
repository = repository.substring(0,i) + getCatalinaHome()
+ repository.substring(i+CATALINA_HOME_TOKEN.length());
} else {
repository = getCatalinaHome()
+ repository.substring(CATALINA_HOME_TOKEN.length());
}
}
while ((i=repository.indexOf(CATALINA_BASE_TOKEN))>=0) {
replace=true;
if (i>0) {
repository = repository.substring(0,i) + getCatalinaBase()
+ repository.substring(i+CATALINA_BASE_TOKEN.length());
} else {
repository = getCatalinaBase()
+ repository.substring(CATALINA_BASE_TOKEN.length());
}
}
if (replace && log.isDebugEnabled())
log.debug("Expanded " + before + " to " + replace);
// Check for a JAR URL repository
try {//URL的格式:协议://主机地址:端口号/路径
URL url=new URL(repository); //异常抛出
repositoryLocations.add(repository);
repositoryTypes.add(ClassLoaderFactory.IS_URL);
continue;
} catch (MalformedURLException e) {
}
if (repository.endsWith("*.jar")) {
repository = repository.substring
(0, repository.length() - "*.jar".length());
repositoryLocations.add(repository);
repositoryTypes.add(ClassLoaderFactory.IS_GLOB);
} else if (repository.endsWith(".jar")) {
repositoryLocations.add(repository);
repositoryTypes.add(ClassLoaderFactory.IS_JAR);
} else {
repositoryLocations.add(repository);
repositoryTypes.add(ClassLoaderFactory.IS_DIR);
}
}
String[] locations = (String[]) repositoryLocations.toArray(new String[0]);
Integer[] types = (Integer[]) repositoryTypes.toArray(new Integer[0]);
ClassLoader classLoader = ClassLoaderFactory.createClassLoader
(locations, types, parent);
// Retrieving MBean server
MBeanServer mBeanServer = null;
if (MBeanServerFactory.findMBeanServer(null).size() > 0) {
mBeanServer =
(MBeanServer) MBeanServerFactory.findMBeanServer(null).get(0);
} else {
mBeanServer = MBeanServerFactory.createMBeanServer();
}
// Register the server classloader
ObjectName objectName =
new ObjectName("Catalina:type=ServerClassLoader,name=" + name);
mBeanServer.registerMBean(classLoader, objectName);
return classLoader;
}
  下面是CatalinaProperties类,负责读取catalina.properties 文件。

public class CatalinaProperties {
// ------------------------------------------------------- Static Variables
private static org.apache.juli.logging.Log log=
org.apache.juli.logging.LogFactory.getLog( CatalinaProperties.class );
private static Properties properties = null;
static {
loadProperties();
}
/**
* Return specified property value.
*/
public static String getProperty(String name) {
return properties.getProperty(name);
}
/**
* Return specified property value.
*/
public static String getProperty(String name, String defaultValue) {
return properties.getProperty(name, defaultValue);
}
/**
* Load properties.
*/
private static void loadProperties() {
InputStream is = null;
Throwable error = null;
try {
String configUrl = getConfigUrl();
if (configUrl != null) {
is = (new URL(configUrl)).openStream();
}
} catch (Throwable t) {
// Ignore
}
if (is == null) {
try {
File home = new File(getCatalinaBase());
File conf = new File(home, "conf");
File properties = new File(conf, "catalina.properties");
is = new FileInputStream(properties);
} catch (Throwable t) {
// Ignore
}
}
if (is == null) {
try {
is = CatalinaProperties.class.getResourceAsStream
("/org/apache/catalina/startup/catalina.properties");
} catch (Throwable t) {
// Ignore
}
}
if (is != null) {
try {
properties = new Properties();
properties.load(is);
is.close();
} catch (Throwable t) {
error = t;
}
}
if ((is == null) || (error != null)) {
// Do something
log.warn("Failed to load catalina.properties", error);
// That's fine - we have reasonable defaults.
properties=new Properties();
}
// Register the properties as system properties
Enumeration enumeration = properties.propertyNames();
while (enumeration.hasMoreElements()) {
String name = (String) enumeration.nextElement();
String value = properties.getProperty(name);
if (value != null) {
System.setProperty(name, value);
}
}
}
/**
* Get the value of the catalina.home environment variable.
*/
private static String getCatalinaHome() {
return System.getProperty("catalina.home",
System.getProperty("user.dir"));
}
/**
* Get the value of the catalina.base environment variable.
*/
private static String getCatalinaBase() {
return System.getProperty("catalina.base", getCatalinaHome());
}
/**
* Get the value of the configuration URL.
*/
private static String getConfigUrl() {
return System.getProperty("catalina.config");
}
}

  首先从系统属性catalina.config中寻找,如果没有此文件,则从Tomcat的工作目录下的conf中寻找该文件(存在该文件)。如果仍然没有此文件则从/org/apache/catalina/startup包中寻找catalina.properties文件。说一下getResourceAsStream()的用法,由于“/org/apache/catalina/startup”字符串使用“/”开始的,“/”代表根目录,根目录是以类文件(class文件)package的顶层目录为基准,寻找资源文件的。比如在Web应用中,有一个WEB-INF的目录,WEB-INF目录里面除了web.xml文件外,还有一个classes目录,没错了,它就是你这个WEB应用的package的顶层目录。如果不是以“/”开始,则是相对于该类文件所在目录的相对路径寻找的。
  找到catalina.properties文件后, 创建Properties类的实例,装载该文件流。之后将从属性文件中读取到的属性设置在系统属性中。
  回退到createClassLoader方法中,传入的参数为“common”和null,查阅Tomcat安装目录下conf文件夹中的catalina.properties文件,可知common.loader=${catalina.home}/lib,${catalina.home}/lib/*.jar,得到的locations{"D://xxxx//bin//lib", "D://xxxx//bin//lib//"},得到的types{ClassLoaderFactory.IS_DIR, ClassLoaderFactory.IS_GLOB}。
  ClassLoaderFactory类

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-329452-1-1.html 上篇帖子: eclipse部署Tomcat不见Tomcat选项? 下篇帖子: tomcat msm部署
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表