隨筆-72  評(píng)論-20  文章-0  trackbacks-1

          使用Tomcat 常見(jiàn) "The value for the useBean class attribute is invalid" 錯(cuò)誤。該錯(cuò)誤是指 JSP 中給定的 useBean 標(biāo)簽的 class 屬性的值無(wú)效(不是 Bean 的屬性值)。
           
          在說(shuō)明這個(gè)問(wèn)題前,先看看有關(guān)的 Tomcat 源代碼(org.apache.jasper.compiler.Generator):

            if (beanName == null) {
                try {
                    Class bean = ctxt.getClassLoader().loadClass(klass);
                    int modifiers = bean.getModifiers();
                    if (!Modifier.isPublic(modifiers) ||
                         Modifier.isInterface(modifiers) ||
                        Modifier.isAbstract (modifiers)) {
                        throw new Exception("Invalid bean class modifier");
                    }
                    // Check that there is a 0 arg constructor
                    bean.getConstructor(new Class[] {});
                    generateNew = true;
                } catch (Exception e) {
                    // Cannot instantiate the specified class
                    if (ctxt.getOptions().getErrorOnUseBeanInvalidClassAttribute()) {
                        err.jspError(n, "jsp.error.invalid.bean ", klass);
                    }
                }
            }


          可見(jiàn)錯(cuò)誤可能的原因包括:

          1. 在編譯 JSP 時(shí)(不是運(yùn)行時(shí)),指定的 Bean 類沒(méi)找到
          2. Bean 雖然找到了,但是它不是 public 的,或者找到的 class 文件是 interface 或抽象類
          3. Bean 類中沒(méi)有 public 的默認(rèn)構(gòu)建函數(shù)

          第二點(diǎn)很明顯,不用多解釋,最經(jīng)常發(fā)生的情況是 Bean 類忘了聲明為 public 。

          第三點(diǎn)中需要注意的是,如果你的 Bean 類沒(méi)有提供任何構(gòu)造函數(shù),將自動(dòng)生成一個(gè)默認(rèn)構(gòu)建函數(shù),這沒(méi)有問(wèn)題。但是,如果你有構(gòu)造函數(shù),則不會(huì)自動(dòng)生成該默認(rèn)構(gòu)造函數(shù)。經(jīng)常被忽略的問(wèn)題是寫了默認(rèn)構(gòu)造函數(shù)卻不是 public 的。

          第一點(diǎn)看起來(lái)簡(jiǎn)單,不過(guò)卻最令人頭痛,尤其是在開發(fā)環(huán)境里。這里需要注意的是,"在編譯 JSP 時(shí)",意味著引用 Bean 的 JSP 是新的,或者剛剛更新過(guò),或者 TOMCAT_HOME/work 中的編譯結(jié)果被清除了。此時(shí),Tomcat 將自動(dòng)(重新)編譯該 JSP,此時(shí)如果發(fā)現(xiàn) Bean 沒(méi)找到,就會(huì)報(bào)這個(gè)錯(cuò)。情況因?yàn)?JSP 或者 Bean 類正在開發(fā)而變得復(fù)雜,一一列舉所有情況沒(méi)有意義,這里我舉一些典型例子,借此應(yīng)該可以舉一反三:
           
          如果 JSP 編譯結(jié)果存在,且 JSP 沒(méi)有更新,Tomcat 不會(huì)重新編譯 JSP,同時(shí)默認(rèn)情況也不會(huì)自動(dòng)重新加載更新過(guò)的 Bean 類(參考 http://jakarta.apache.org/tomcat/tomcat-5.5-doc/config/context.html 中的 reloadable)。所以,你會(huì)發(fā)現(xiàn)此時(shí)即使刪除了 Bean 類都沒(méi)有問(wèn)題,當(dāng)然,更新 Bean 類也不會(huì)有什么用。如果在 JSP 編譯產(chǎn)生之后,我們重起了服務(wù)器,由于 JSP 文件編譯的結(jié)果存在,所以,可以仍然訪問(wèn) JSP 頁(yè)面,而不必重新編譯。可是如果訪問(wèn)前,刪掉了 Bean 類,就會(huì)報(bào)過(guò) ClassNotFoundException 而不是上述錯(cuò)誤。關(guān)鍵在于 JSP 是否經(jīng)過(guò)編譯,沒(méi)有編譯則沒(méi)有找到類報(bào)告題目中的編譯錯(cuò)誤 ,編譯過(guò)則是 ClassNotFoundException 運(yùn)行時(shí)實(shí)例化錯(cuò)誤。


          還有一個(gè)更為特殊的例子。如果 Web 應(yīng)用在啟動(dòng)時(shí), WEB-INF/classes 目錄不存在,則在啟動(dòng)應(yīng)用后,新建 classes 目錄,動(dòng)態(tài)添加新的類進(jìn)去是沒(méi)有用的,會(huì)報(bào)告同樣的錯(cuò)誤。原因是此時(shí)的 Tomcat 不會(huì)去檢查該目錄,也就不會(huì)找到你需要的類。對(duì) WEB-INF/lib 目錄也是同樣。這一點(diǎn)可以參考下面的源代碼(org.apache.catalina.loader.WebappLoader):
           
            // Setting up the class repository (/WEB-INF/classes), if it exists

            String classesPath = "/WEB-INF/classes";
            DirContext classes = null;

            try {
                Object object = resources.lookup(classesPath);
                if (object instanceof DirContext) {
                    classes = (DirContext) object;
                }
            } catch(NamingException e) {
                // Silent catch: it's valid that no /WEB-INF/classes collection
                // exists
            }

            if (classes != null) {

                File classRepository = null;

                String absoluteClassesPath =
                    servletContext.getRealPath(classesPath);

                if (absoluteClassesPath != null) {

                    classRepository = new File(absoluteClassesPath);

                } else {

                    classRepository = new File(workDir, classesPath);
                    classRepository.mkdirs();
                    copyDir(classes, classRepository);

                }

                if(log.isDebugEnabled())
                    log.debug(sm.getString("webappLoader.classDeploy", classesPath,
                                 classRepository.getAbsolutePath()));


                // Adding the repository to the class loader
                classLoader.addRepository(classesPath + "/", classRepository);
                loaderRepositories.add(classesPath + "/" );

            }

           

          上面最后兩個(gè)語(yǔ)句將 "/WEB-INF/classes" 注冊(cè)到類裝載器中。如果目錄不存在,則該目錄不會(huì)被注冊(cè),即使在運(yùn)行過(guò)程中創(chuàng)建該目錄,Tomcat 目前也沒(méi)有機(jī)制動(dòng)態(tài)的注冊(cè)該目錄(應(yīng)該是出于效率考慮)。


          盡管這個(gè)問(wèn)題的復(fù)雜場(chǎng)景可能不一而足,不過(guò)解決它的辦法卻很簡(jiǎn)單:停止服務(wù)器,確認(rèn)你的 JSP 和 Bean 正確部署,清理掉 TOMCAT_HOME/work 中的內(nèi)容,重起服務(wù)器。 此外,配置動(dòng)態(tài)類加載對(duì)開發(fā)而言是個(gè)不錯(cuò)的選擇。
           
          本文基于 Tomcat 5.5.9 版本。

          --
          Stephen Suen(SUNRUJUN)

           

          posted on 2006-06-14 14:25 前方的路 閱讀(801) 評(píng)論(0)  編輯  收藏

          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 汤原县| 若尔盖县| 白朗县| 华安县| 四子王旗| 蒙城县| 万山特区| 辽阳县| 遂川县| 雷山县| 五常市| 亚东县| 库尔勒市| 三亚市| 蒙山县| 民勤县| 富蕴县| 财经| 天祝| 屏东市| 南充市| 武宁县| 大港区| 石柱| 惠水县| 宝兴县| 平阳县| 体育| 湘乡市| 迭部县| 广丰县| 资中县| 防城港市| 朝阳市| 界首市| 雷州市| 南郑县| 阳高县| 宿松县| 泸州市| 遂宁市|