本系列转载自 http://blog.csdn.net/haitao111313/article/category/1179996
我们知道,在Tomcat的世界里,一个Host容器代表一个虚机器资源,Context容器代表一个应用,所谓的部署器就是能够把Context容器添加进Host容器中去的一个组件。显然,一个Host容器应该拥有一个部署器组件。简单的部署代码应该是下面这样的:
- Context context = new StandardContext();
- Host host = new StandardHost();
- host.addChild(context);
- public void lifecycleEvent(LifecycleEvent event) {
- // Identify the host we are associated with
- try {
- host = (Host) event.getLifecycle();
- if (host instanceof StandardHost) { //如果监听到的事件对象类型是StandardHost就设置相关属性。
- int hostDebug = ((StandardHost) host).getDebug();
- if (hostDebug > this.debug) {
- this.debug = hostDebug;
- }
- setDeployXML(((StandardHost) host).isDeployXML());//是否发布xml文件的标识,默认为true
- setLiveDeploy(((StandardHost) host).getLiveDeploy());//是否动态部署标识,默认为true
- setUnpackWARs(((StandardHost) host).isUnpackWARs());//是否要将war文件解压缩,默认为true
- }
- } catch (ClassCastException e) {
- log(sm.getString("hostConfig.cce", event.getLifecycle()), e);
- return;
- }
- // Process the event that has occurred
- if (event.getType().equals(Lifecycle.START_EVENT)) //监听到容器开始,则调用start方法,方法里面调用了部署应用的代码
- start();
- else if (event.getType().equals(Lifecycle.STOP_EVENT))
- stop();
- }
- protected void start() {
- if (debug >= 1)
- log(sm.getString("hostConfig.start"));
- if (host.getAutoDeploy()) {
- deployApps();//发布应用
- }
- if (isLiveDeploy()) {
- threadStart();//动态发布应用,因为HostConfig也实现了Runnable接口,threadStart启动该线程来实现动态发布
- }
- }
- --------------------》deployApps方法,该方法会把webapps目录下的所有目录都看作成一个应用程序
- protected void deployApps() {
- if (!(host instanceof Deployer))
- return;
- if (debug >= 1)
- log(sm.getString("hostConfig.deploying"));
- File appBase = appBase();//返回webapps目录
- if (!appBase.exists() || !appBase.isDirectory())
- return;
- String files[] = appBase.list();//列出webapps目录下的所有文件
- deployDescriptors(appBase, files);//通过描述符发布应用
- deployWARs(appBase, files);//发布war文件的应用
- deployDirectories(appBase, files);//发布目录型的应用
- }
以上三个发布应用的方式大同小异,所以只说说常用的发布方式--目录型的应用,下面看看deployDirectories方法,只写了关键的逻辑:
- protected void deployDirectories(File appBase, String[] files) {
- for (int i = 0; i < files.length; i++) {
- if (files[i].equalsIgnoreCase("META-INF"))
- continue;
- if (files[i].equalsIgnoreCase("WEB-INF"))
- continue;
- if (deployed.contains(files[i]))
- continue;
- File dir = new File(appBase, files[i]);
- if (dir.isDirectory()) {
- deployed.add(files[i]);
- // Make sure there is an application configuration directory
- // This is needed if the Context appBase is the same as the
- // web server document root to make sure only web applications
- // are deployed and not directories for web space.
- File webInf = new File(dir, "/WEB-INF");
- if (!webInf.exists() || !webInf.isDirectory() ||
- !webInf.canRead())
- continue;
- // Calculate the context path and make sure it is unique
- String contextPath = "/" + files[i];
- if (files[i].equals("ROOT"))
- contextPath = "";
- if (host.findChild(contextPath) != null)
- continue;
- // Deploy the application in this directory
- log(sm.getString("hostConfig.deployDir", files[i]));
- try {
- URL url = new URL("file", null, dir.getCanonicalPath());//得到应用的路径,路径的写法是 file://应用名称
- ((Deployer) host).install(contextPath, url); //安装应用到目录下
- } catch (Throwable t) {
- log(sm.getString("hostConfig.deployDir.error", files[i]),
- t);
- }
- }
- }
- }
((Deployer) host).install(contextPath, url);会调用到StandardHost的install方法,再由StandardHost转交给StandardHostDeployer的install方法,StandardHostDeployer是一个辅助类,帮助StandardHost来实现发布应用,它实现了Deployer接口,看它的install(URL config, URL war)方法(它有两个install方法,分别用来发布上面不同方式的应用):
- public synchronized void install(String contextPath, URL war)
- throws IOException {
- ..............................................
- // Calculate the document base for the new web application
- host.log(sm.getString("standardHost.installing",
- contextPath, war.toString()));
- String url = war.toString();
- String docBase = null;
- if (url.startsWith("jar:")) { //如果是war类型的应用
- url = url.substring(4, url.length() - 2);
- }
- if (url.startsWith("file://"))//如果是目录类型的应用
- docBase = url.substring(7);
- else if (url.startsWith("file:"))
- docBase = url.substring(5);
- else
- throw new IllegalArgumentException
- (sm.getString("standardHost.warURL", url));
- // Install the new web application
- try {
- Class clazz = Class.forName(host.getContextClass());//host.getContextClass得到的其实是StandardContext,
- Context context = (Context) clazz.newInstance();
- context.setPath(contextPath);//设置该context的访问路径为contextPath,即我们的应用访问路径
- context.setDocBase(docBase);//设置该应用在磁盘的路径
- if (context instanceof Lifecycle) {
- clazz = Class.forName(host.getConfigClass());//实例化host的监听器类,并关联上context
- LifecycleListener listener =
- (LifecycleListener) clazz.newInstance();
- ((Lifecycle) context).addLifecycleListener(listener);
- }
- host.fireContainerEvent(PRE_INSTALL_EVENT, context);
- host.addChild(context); //添加到host实例,即把context应用发布到host。
- host.fireContainerEvent(INSTALL_EVENT, context);
- } catch (Exception e) {
- host.log(sm.getString("standardHost.installError", contextPath),
- e);
- throw new IOException(e.toString());
- }
- }
经过上面的代码分析,已经完全了解了怎么发布一个目录型的应用到StandardHost中,其他war包和文件描述符类型的应用发布跟StandardHost大体类似,在这里就不说了,有兴趣的可以自己查看源代码。