tomcat启动(三)Catalina分析-load方法分析

本文详细介绍了Tomcat中Digester类如何使用SAX技术解析XML配置文件的过程。从初始化Digester到解析XML文档,再到具体解析规则的应用,深入剖析了Tomcat启动时的关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

load()方法按从上到下顺序分析(主要分析本人所没学过的知识点,其它略过。。。)。

Digester类作用

使用sax技术对xml进行解析

未开始解析时Digester.push(this)这个用来为catalina设置server

Digester的stack对象栈中持有Catalina对象,

解析xml过程中需要用到的类:

Rule:这个类有很多子类,为解析时遇到不同的匹配模式pattern调用不同的处理动作即不同rule。

  当解析到开始标记时会调用其子类ObjectCreateRule.begin()方法.

  如解析到<Server>时就会创建StandardServer类的实例并反射调用Digester的stack栈顶对象的setter方法(调用的方法通过传入的name值确定)。

  IntrospectionUtils.setProperty(top, name, value)top栈顶对象,name要设置的属性名---方法名的一部分,value要设置的属性值

  刚开始时栈顶元素是Catalina,即调用Catalina.setServer(Server object)方法设置Server为后面调用Server.start()做准备

  然后将StandardServer对象实例放入Digester的stack对象栈中,

  如上面所示会调用setter方法设置NamingResourcesImpl、NamingContextListener。调用addService()方法设置service

  当解析到<service>时StandardService实例化,并设置StandardService的Connector、Executor:StandardThreadExecutor、Container

 

上面Server,Service,Executor,Container,Connector对象设置都是在解析过程中设置了。

A Digester processes an XML input stream by matching a series of element nesting
 patterns to execute Rules that have been added prior to the start of parsing. This 
package was inspired by the XmlMapper class that was part of Tomcat 3.0 and 3.1,
 but is organized somewhat differently.
Digester通过匹配一系列元素嵌套模式来处理XML输入流,以执行在开始解析之前添加的规则。
该软件包受到作为Tomcat 3.0和3.1的一部分的XmlMapper类的启发,但是它的组织方式略有不同。

 具体的对象设置过程请跳转查看:

Catalina.createDigester方法详细理解

 一、CreateStartDigester()//Digester的初始化,为Xml的标签即解析模式增加处理规则rule
二、org.xml.sax.InputSource.
XML实体的单一输入源
创建一个带有系统标识符的新输入源。
应用程序也可以使用setPublicId来包含公共标识符,
或者setEncoding来指定字符编码,如果已知的话。
如果系统标识符是URL,则必须完全解析(它可能不是相对URL)。
inputSource = new InputSource(file.toURI().toURL().toString());//这个很标准file.toURL()已经不推荐使用了,要获取这个文件路径必须用file.toURI()进行转换
/**如果还有一个指定的字符流,SAX解析器将忽略这一点,但它会使用一个字节流来优先打开一个URI连接本身。
如果应用程序知道字节流的字符编码,则应使用setEncoding方法进行设置。*/
inputSource.setByteStream(inputStream);

 三、digester解析xml文件

digester.push(this);将catalina类放入对象栈顶
digester.parse(inputSource);这里开始解析xml数据资源

上面load方法中知道inputSource是指catalina.base/conf/server.xml文件字节流

public Object parse(InputSource input) {

configure();//提供这个Digester实例的懒惰配置的钩子。默认实现什么都不做,但子类可以根据需要重写。
getXMLReader().parse(input);
return (root);

}

 

返回用于解析输入文档的XMLReader。 FIX ME:在JAXP / XERCES中有一个bug,阻止使用包含DTD的模式的解析器。

Digester.getXMLReader() 
public XMLReader getXMLReader() throws SAXException {
        if (reader == null) {
            reader = getParser().getXMLReader();
        }

        reader.setDTDHandler(this);
        reader.setContentHandler(this);

        if (entityResolver == null) {
            reader.setEntityResolver(this);
        } else {
            reader.setEntityResolver(entityResolver);
        }

        reader.setProperty("http://xml.org/sax/properties/lexical-handler", this);

        reader.setErrorHandler(this);
        return reader;
    }

 getParser()

 

创建一个SAXParser

通过getFactory()获得SAXParserFactory工厂类

工厂类调用newSAXParser()创建parser实例

public SAXParser getParser() {

        // Return the parser we already created (if any)
        if (parser != null) {
            return (parser);
        }

        // Create a new parser
        try {
            parser = getFactory().newSAXParser();
        } catch (Exception e) {
            log.error("Digester.getParser: ", e);
            return (null);
        }

        return (parser);

    }

得到XMLReader

SAXParser.getXMLReader() 返回由该类实现封装的org.xml.sax.XMLReader

 

getXMLReader方法中reader.setDTDHandler(this)注册DTD事件处理程序,将会监听SAXParser解析器报告的DTD事件

reader.setErrorHandler(this);注册错误事件处理程序

reader.setContentHandler(this); 

注册内容事件处理程序。如果应用程序没有注册内容处理程序,SAX解析器报告的所有内容事件将被默认忽略。

应用程序可以在解析的中间注册一个新的或不同的处理程序,而SAX解析器必须立即开始使用新的处理程序

在SAX编程中,需要为XMLReader设置相应的ContentHandler,
该Handler的startDocument,endDocument,startElement,endElement及characters等方法将用于响应解析xml时的标签事件,
可看到Digester继承于DefaultHandler类,而该类则实现ContentHandler接口,
因此在对server.xml解析时将相应地调用到Digester的startDocument,endDocument,startElement,endElement及characters等方法

 最后返回一个xmlreader执行

XMLReader.parse(InputSource input)
XMLReader将通过注册的事件处理程序提供有关XML文档的信息。
此方法是同步的:它将不会返回,直到解析结束。
如果客户端应用程序想要尽早终止解析,则应该抛出异常
当开始解析xml文档时,会向ContentHandler发送内容事件,即Digester。
startDocument,endDocument,startElement,endElement及characters等方法将会被调用
 
 
 
在调用所有解析方法之前会将调用
Digester.setDocumentLocator( Locator locator)设置Locator,报告文档相关事件。如错误事件
这个Locator包含的文档的编码,可以在startDocument中用到
提供定位器:如果这样做,它必须通过调用此方法在调用ContentHandler接口中的任何其他方
法之前将定位器提供给应用程序。定位器允许应用程序确定任何文档相关事件的结束位置,即使
解析器没有报告错误。通常,应用程序将使用此信息来报告其自身的错误(例如与应用程序业务
规则不匹配的字符内容)

 

1、Digester.startDocument() 

处理通知文档的开头,这里主要设置的编码The encoding used by the source XMl document.

 ((DocumentProperties.Encoding) root).setEncoding(((Locator2) locator).getEncoding());

 

再开始startElement()方法之前会先调用

startPrefixMapping(String prefix, String namespaceURI)//命名空间前缀进入范围的通知处理方法

这个方法主要是用HashMap<String, ArrayStack<String>>  

namespaces 变量以prefix为key,ArrayStack为值保存数据ArrayStack保存着namespaceURI

ArrayStack<String> stack = namespaces.get(prefix);
        if (stack == null) {
            stack = new ArrayStack<>();
            namespaces.put(prefix, stack);
        }
        stack.push(namespaceURI);

 

SAX XML reader将自动替换元素和属性名称的前缀。

然而,有些情况下,当应用程序需要在字符数据或属性值中使用前缀时,它们无法安全地自动扩展; start / endPrefixMapping事件将信息提供给应用程序,以在必要时在这些上下文中扩展前缀。

startPrefixMapping事件都将在相应的startElement事件之前立即发生,并且所有endPrefixMapping事件都将在相应的endElement事件之后立即发生

 

2、Digester.startElement(String namespaceURI, String localName, String qName, Attributes list)

namespaceURI The Namespace URI, or the empty string if the element has no
         Namespace URI or if Namespace processing is not being performed.
命名空间URI,如果元素没有
命名空间URI或未执行命名空间处理,则为空字符串
localName
     The local name (without prefix), or the empty string if Namespace processing is 
    not being performed.
本地名称(无前缀),或为空字符串(如果命名空间处理) 没有执行。
qName   The qualified name (with prefix), or the empty string     
if qualified names are not available.
限定名称(带前缀)或为空字符串 如果限定名称不可用。
list   The attributes attached to the element. If       there are no attributes, it shall be an empty Attributes object.
附加到元素的属性。如果没有属性,它将是一个空的Attributes对象。

到达XML元素的开始处理通知

 updateAttributes(list)更新属性中的系统值引用

格式为“$ {xxx}”的文本都将被系统属性中适当的值替换。

 初始化标签体内容

bodyTexts用于周围元素的正文文本字符串缓冲区堆栈。The stack of body text string buffers for surrounding elements.

bodyText当前元素标签内的正文内容

获取路径名

将标签的路径字符赋给name。XML格式如:<Servers><Server><Server></Servers>,

当解析到<Server>标签时路径则为Servers/Server,在得到路径后又将该路径赋予match变量;

得到与标签体路径名匹配的Rule List,触发其begin方法

这部分内容是tomcat解析xml的重要部分

Rule.begin( String namespace, String name, Attributes attributes)

当遇到匹配的XML元素的开始时调用此方法。默认实现委托到不推荐使用的方法,而不使用命名空间和名称参数,以保留向后兼容性。

然后这个方法内调用Rule.begin(Attributes attributes) 开始匹配xml元素

 

3、Digester.characters(char[] buffer, int start, int length) 处理从XML元素的正文接收到的字符数据的通知。

buffer The characters from the XML document来自XML文档的字符

start Starting offset into the buffer开始偏移到缓冲区

length Number of characters from the buffer缓冲区中的字符数

bodyText.append(buffer, start, length);接受元素标签内的正文内容

 

4、Digester.endElement(String namespaceURI, String localName, String qName)处理到达的XML元素的结束标记的通知

 将bodyText中的变量值替换,如标签体中有${catalina.base},会使用System.getProperties()提取系统catalina.base的属性值

获取与标签路径名匹配的Rule,触发其body及end方法

matches再startElement中被复值了

List<Rule> rules = getRules().match(namespaceURI, match);

matches.push(rules);

// Parse system properties
        bodyText = updateBodyText(bodyText);
List<Rule> rules = matches.pop();
rule.body(namespaceURI, name, bodyText);
// Recover the body text from the surrounding element
  bodyText = bodyTexts.pop();

rule.end(namespaceURI, name);

// Recover the previous match expression
int slash = match.lastIndexOf('/');
if (slash >= 0) {
match = match.substring(0, slash);
} else {
match = "";
}

 

执行完endElement()方法后元素标签解析完毕需要取消注册此前缀映射

Digester.endPrefixMapping(String prefix)就是处理的地方

ArrayStack<String> stack = namespaces.get(prefix);
stack.pop();
            if (stack.empty())
                namespaces.remove(prefix);

 

5、Digester.endDocument()

 方法内容为:弹出所有还在栈中的对象,执行所有Rule的finish方法,执行clear方法清空相关堆栈(clear方法将Digester定义的变量设置为null,configure设置为false)

 


 

到这里xml解析完成。等有空再把上面缕缕

 

转载于:https://www.cnblogs.com/gne-hwz/p/7717079.html

at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFa ctory.getTomcatWebServer(TomcatServletWebServerFactory.java:440) [jar:rsrc:sprin g-boot-2.3.12.RELEASE.jar!/:na] at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFa ctory.getWebServer(TomcatServletWebServerFactory.java:193) [jar:rsrc:spring-boot -2.3.12.RELEASE.jar!/:na] at org.springframework.boot.web.servlet.context.ServletWebServerApplicat ionContext.createWebServer(ServletWebServerApplicationContext.java:178) [jar:rsr c:spring-boot-2.3.12.RELEASE.jar!/:na] at org.springframework.boot.web.servlet.context.ServletWebServerApplicat ionContext.onRefresh(ServletWebServerApplicationContext.java:158) [jar:rsrc:spri ng-boot-2.3.12.RELEASE.jar!/:na] at org.springframework.context.support.AbstractApplicationContext.refres h(AbstractApplicationContext.java:545) [jar:rsrc:spring-context-5.2.15.RELEASE.j ar!/:na] at org.springframework.boot.web.servlet.context.ServletWebServerApplicat ionContext.refresh(ServletWebServerApplicationContext.java:143) [jar:rsrc:spring -boot-2.3.12.RELEASE.jar!/:na] at org.springframework.boot.SpringApplication.refresh(SpringApplication. java:755) [jar:rsrc:spring-boot-2.3.12.RELEASE.jar!/:na] at org.springframework.boot.SpringApplication.refresh(SpringApplication. java:747) [jar:rsrc:spring-boot-2.3.12.RELEASE.jar!/:na] at org.springframework.boot.SpringApplication.refreshContext(SpringAppli cation.java:402) [jar:rsrc:spring-boot-2.3.12.RELEASE.jar!/:na] at org.springframework.boot.SpringApplication.run(SpringApplication.java :312) [jar:rsrc:spring-boot-2.3.12.RELEASE.jar!/:na] at org.springframework.boot.SpringApplication.run(SpringApplication.java :1247) [jar:rsrc:spring-boot-2.3.12.RELEASE.jar!/:na] at org.springframework.boot.SpringApplication.run(SpringApplication.java :1236) [jar:rsrc:spring-boot-2.3.12.RELEASE.jar!/:na] at com.kucun.DemoApplication.main(DemoApplication.java:41) [rsrc:./:na] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8. 0_31] at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[na:1.8. 0_31] at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[na: 1.8.0_31] at java.lang.reflect.Method.invoke(Unknown Source) ~[na:1.8.0_31] at org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader.main(JarRsrcLoa der.java:58) [kucun2.jar:na] Caused by: org.apache.catalina.LifecycleException: A child container failed duri ng start at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.ja va:928) ~[jar:rsrc:tomcat-embed-core-9.0.46.jar!/:na] at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java :829) ~[jar:rsrc:tomcat-embed-core-9.0.46.jar!/:na] at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183) [jar:rsrc:tomcat-embed-core-9.0.46.jar!/:na] at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase. java:1384) ~[jar:rsrc:tomcat-embed-core-9.0.46.jar!/:na] at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase. java:1374) ~[jar:rsrc:tomcat-embed-core-9.0.46.jar!/:na] at java.util.concurrent.FutureTask.run(Unknown Source) ~[na:1.8.0_31] at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineEx ecutorService.java:75) ~[jar:rsrc:tomcat-embed-core-9.0.46.jar!/:na] at java.util.concurrent.AbstractExecutorService.submit(Unknown Source) ~ [na:1.8.0_31] at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.ja va:909) ~[jar:rsrc:tomcat-embed-core-9.0.46.jar!/:na] ... 27 common frames omitted Caused by: java.util.concurrent.ExecutionException: org.apache.catalina.Lifecycl eException: Failed to initialize component [org.apache.catalina.webresources.Sta ndardRoot@b9f698] at java.util.concurrent.FutureTask.report(Unknown Source) ~[na:1.8.0_31] at java.util.concurrent.FutureTask.get(Unknown Source) ~[na:1.8.0_31] at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.ja va:916) ~[jar:rsrc:tomcat-embed-core-9.0.46.jar!/:na] ... 35 common frames omitted Caused by: org.apache.catalina.LifecycleException: Failed to initialize componen t [org.apache.catalina.webresources.StandardRoot@b9f698] at org.apache.catalina.util.LifecycleBase.handleSubClassException(Lifecy cleBase.java:440) [jar:rsrc:tomcat-embed-core-9.0.46.jar!/:na] at org.apache.catalina.util.LifecycleBase.init(LifecycleBase.java:139) [ jar:rsrc:tomcat-embed-core-9.0.46.jar!/:na] at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:173) [jar:rsrc:tomcat-embed-core-9.0.46.jar!/:na] at org.apache.catalina.core.StandardContext.resourcesStart(StandardConte xt.java:4830) ~[jar:rsrc:tomcat-embed-core-9.0.46.jar!/:na] at org.apache.catalina.core.StandardContext.startInternal(StandardContex t.java:4966) ~[jar:rsrc:tomcat-embed-core-9.0.46.jar!/:na] at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183) [jar:rsrc:tomcat-embed-core-9.0.46.jar!/:na] at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase. java:1384) ~[jar:rsrc:tomcat-embed-core-9.0.46.jar!/:na] at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase. java:1374) ~[jar:rsrc:tomcat-embed-core-9.0.46.jar!/:na] at java.util.concurrent.FutureTask.run(Unknown Source) ~[na:1.8.0_31] at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineEx ecutorService.java:75) ~[jar:rsrc:tomcat-embed-core-9.0.46.jar!/:na] at java.util.concurrent.AbstractExecutorService.submit(Unknown Source) ~ [na:1.8.0_31] at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.ja va:909) ~[jar:rsrc:tomcat-embed-core-9.0.46.jar!/:na] ... 35 common frames omitted Caused by: java.lang.Error: factory already defined at java.net.URL.setURLStreamHandlerFactory(Unknown Source) ~[na:1.8.0_31 ] at org.apache.catalina.webresources.TomcatURLStreamHandlerFactory.<init> (TomcatURLStreamHandlerFactory.java:130) ~[jar:rsrc:tomcat-embed-core-9.0.46.jar !/:na] at org.apache.catalina.webresources.TomcatURLStreamHandlerFactory.getIns tanceInternal(TomcatURLStreamHandlerFactory.java:53) ~[jar:rsrc:tomcat-embed-cor e-9.0.46.jar!/:na] at org.apache.catalina.webresources.TomcatURLStreamHandlerFactory.regist er(TomcatURLStreamHandlerFactory.java:77) ~[jar:rsrc:tomcat-embed-core-9.0.46.ja r!/:na] at org.apache.catalina.webresources.StandardRoot.registerURLStreamHandle rFactory(StandardRoot.java:700) ~[jar:rsrc:tomcat-embed-core-9.0.46.jar!/:na] at org.apache.catalina.webresources.StandardRoot.initInternal(StandardRo ot.java:682) ~[jar:rsrc:tomcat-embed-core-9.0.46.jar!/:na] at org.apache.catalina.util.LifecycleBase.init(LifecycleBase.java:136) [ jar:rsrc:tomcat-embed-core-9.0.46.jar!/:na] ... 45 common frames omitted 2025-06-20 10:58:03.399 INFO 15168 --- [ main] o.apache.catalina.core .StandardService : Stopping service [Tomcat] 2025-06-20 10:58:09.641 WARN 15168 --- [ main] ConfigServletWebServer ApplicationContext : Exception encountered during context initialization - cance lling refresh attempt: org.springframework.context.ApplicationContextException: Unable to start web server; nested exception is org.springframework.boot.web.ser ver.WebServerException: Unable to start embedded Tomcat 2025-06-20 10:58:09.656 INFO 15168 --- [ main] ConditionEvaluationRep ortLoggingListener : Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled. 2025-06-20 10:58:11.076 ERROR 15168 --- [ main] o.s.boot.SpringApplica tion : Application run failed org.springframework.context.ApplicationContextException: Unable to start web ser ver; nested exception is org.springframework.boot.web.server.WebServerException: Unable to start embedded Tomcat at org.springframework.boot.web.servlet.context.ServletWebServerApplicat ionContext.onRefresh(ServletWebServerApplicationContext.java:161) ~[jar:rsrc:spr ing-boot-2.3.12.RELEASE.jar!/:na] at org.springframework.context.support.AbstractApplicationContext.refres h(AbstractApplicationContext.java:545) ~[jar:rsrc:spring-context-5.2.15.RELEASE. jar!/:na] at org.springframework.boot.web.servlet.context.ServletWebServerApplicat ionContext.refresh(ServletWebServerApplicationContext.java:143) ~[jar:rsrc:sprin g-boot-2.3.12.RELEASE.jar!/:na] at org.springframework.boot.SpringApplication.refresh(SpringApplication. java:755) [jar:rsrc:spring-boot-2.3.12.RELEASE.jar!/:na] at org.springframework.boot.SpringApplication.refresh(SpringApplication. java:747) [jar:rsrc:spring-boot-2.3.12.RELEASE.jar!/:na] at org.springframework.boot.SpringApplication.refreshContext(SpringAppli cation.java:402) [jar:rsrc:spring-boot-2.3.12.RELEASE.jar!/:na] at org.springframework.boot.SpringApplication.run(SpringApplication.java :312) [jar:rsrc:spring-boot-2.3.12.RELEASE.jar!/:na] at org.springframework.boot.SpringApplication.run(SpringApplication.java :1247) [jar:rsrc:spring-boot-2.3.12.RELEASE.jar!/:na] at org.springframework.boot.SpringApplication.run(SpringApplication.java :1236) [jar:rsrc:spring-boot-2.3.12.RELEASE.jar!/:na] at com.kucun.DemoApplication.main(DemoApplication.java:41) [rsrc:./:na] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8. 0_31] at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[na:1.8. 0_31] at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[na: 1.8.0_31] at java.lang.reflect.Method.invoke(Unknown Source) ~[na:1.8.0_31] at org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader.main(JarRsrcLoa der.java:58) [kucun2.jar:na] Caused by: org.springframework.boot.web.server.WebServerException: Unable to sta rt embedded Tomcat at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.initiali ze(TomcatWebServer.java:142) ~[jar:rsrc:spring-boot-2.3.12.RELEASE.jar!/:na] at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.<init>(T omcatWebServer.java:104) ~[jar:rsrc:spring-boot-2.3.12.RELEASE.jar!/:na] at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFa ctory.getTomcatWebServer(TomcatServletWebServerFactory.java:440) ~[jar:rsrc:spri ng-boot-2.3.12.RELEASE.jar!/:na] at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFa ctory.getWebServer(TomcatServletWebServerFactory.java:193) ~[jar:rsrc:spring-boo t-2.3.12.RELEASE.jar!/:na] at org.springframework.boot.web.servlet.context.ServletWebServerApplicat ionContext.createWebServer(ServletWebServerApplicationContext.java:178) ~[jar:rs rc:spring-boot-2.3.12.RELEASE.jar!/:na] at org.springframework.boot.web.servlet.context.ServletWebServerApplicat ionContext.onRefresh(ServletWebServerApplicationContext.java:158) ~[jar:rsrc:spr ing-boot-2.3.12.RELEASE.jar!/:na] ... 14 common frames omitted Caused by: org.apache.catalina.LifecycleException: A child container failed duri ng start at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.ja va:928) ~[jar:rsrc:tomcat-embed-core-9.0.46.jar!/:na] at org.apache.catalina.core.StandardEngine.startInternal(StandardEngine. java:262) ~[jar:rsrc:tomcat-embed-core-9.0.46.jar!/:na] at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183) ~[jar:rsrc:tomcat-embed-core-9.0.46.jar!/:na] at org.apache.catalina.core.StandardService.startInternal(StandardServic e.java:433) ~[jar:rsrc:tomcat-embed-core-9.0.46.jar!/:na] at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183) ~[jar:rsrc:tomcat-embed-core-9.0.46.jar!/:na] at org.apache.catalina.core.StandardServer.startInternal(StandardServer. java:930) ~[jar:rsrc:tomcat-embed-core-9.0.46.jar!/:na] at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183) ~[jar:rsrc:tomcat-embed-core-9.0.46.jar!/:na] at org.apache.catalina.startup.Tomcat.start(Tomcat.java:486) ~[jar:rsrc: tomcat-embed-core-9.0.46.jar!/:na] at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.initiali ze(TomcatWebServer.java:123) ~[jar:rsrc:spring-boot-2.3.12.RELEASE.jar!/:na] ... 19 common frames omitted Caused by: java.util.concurrent.ExecutionException: org.apache.catalina.Lifecycl eException: A child container failed during start at java.util.concurrent.FutureTask.report(Unknown Source) ~[na:1.8.0_31] at java.util.concurrent.FutureTask.get(Unknown Source) ~[na:1.8.0_31] at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.ja va:916) ~[jar:rsrc:tomcat-embed-core-9.0.46.jar!/:na] ... 27 common frames omitted Caused by: org.apache.catalina.LifecycleException: A child container failed duri ng start at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.ja va:928) ~[jar:rsrc:tomcat-embed-core-9.0.46.jar!/:na] at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java :829) ~[jar:rsrc:tomcat-embed-core-9.0.46.jar!/:na] at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183) ~[jar:rsrc:tomcat-embed-core-9.0.46.jar!/:na] at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase. java:1384) ~[jar:rsrc:tomcat-embed-core-9.0.46.jar!/:na] at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase. java:1374) ~[jar:rsrc:tomcat-embed-core-9.0.46.jar!/:na] at java.util.concurrent.FutureTask.run(Unknown Source) ~[na:1.8.0_31] at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineEx ecutorService.java:75) ~[jar:rsrc:tomcat-embed-core-9.0.46.jar!/:na] at java.util.concurrent.AbstractExecutorService.submit(Unknown Source) ~ [na:1.8.0_31] at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.ja va:909) ~[jar:rsrc:tomcat-embed-core-9.0.46.jar!/:na] ... 27 common frames omitted Caused by: java.util.concurrent.ExecutionException: org.apache.catalina.Lifecycl eException: Failed to initialize component [org.apache.catalina.webresources.Sta ndardRoot@b9f698] at java.util.concurrent.FutureTask.report(Unknown Source) ~[na:1.8.0_31] at java.util.concurrent.FutureTask.get(Unknown Source) ~[na:1.8.0_31] at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.ja va:916) ~[jar:rsrc:tomcat-embed-core-9.0.46.jar!/:na] ... 35 common frames omitted Caused by: org.apache.catalina.LifecycleException: Failed to initialize componen t [org.apache.catalina.webresources.StandardRoot@b9f698] at org.apache.catalina.util.LifecycleBase.handleSubClassException(Lifecy cleBase.java:440) ~[jar:rsrc:tomcat-embed-core-9.0.46.jar!/:na] at org.apache.catalina.util.LifecycleBase.init(LifecycleBase.java:139) ~ [jar:rsrc:tomcat-embed-core-9.0.46.jar!/:na] at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:173) ~[jar:rsrc:tomcat-embed-core-9.0.46.jar!/:na] at org.apache.catalina.core.StandardContext.resourcesStart(StandardConte xt.java:4830) ~[jar:rsrc:tomcat-embed-core-9.0.46.jar!/:na] at org.apache.catalina.core.StandardContext.startInternal(StandardContex t.java:4966) ~[jar:rsrc:tomcat-embed-core-9.0.46.jar!/:na] at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183) ~[jar:rsrc:tomcat-embed-core-9.0.46.jar!/:na] at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase. java:1384) ~[jar:rsrc:tomcat-embed-core-9.0.46.jar!/:na] at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase. java:1374) ~[jar:rsrc:tomcat-embed-core-9.0.46.jar!/:na] at java.util.concurrent.FutureTask.run(Unknown Source) ~[na:1.8.0_31] at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineEx ecutorService.java:75) ~[jar:rsrc:tomcat-embed-core-9.0.46.jar!/:na] at java.util.concurrent.AbstractExecutorService.submit(Unknown Source) ~ [na:1.8.0_31] at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.ja va:909) ~[jar:rsrc:tomcat-embed-core-9.0.46.jar!/:na] ... 35 common frames omitted Caused by: java.lang.Error: factory already defined at java.net.URL.setURLStreamHandlerFactory(Unknown Source) ~[na:1.8.0_31 ] at org.apache.catalina.webresources.TomcatURLStreamHandlerFactory.<init> (TomcatURLStreamHandlerFactory.java:130) ~[jar:rsrc:tomcat-embed-core-9.0.46.jar !/:na] at org.apache.catalina.webresources.TomcatURLStreamHandlerFactory.getIns tanceInternal(TomcatURLStreamHandlerFactory.java:53) ~[jar:rsrc:tomcat-embed-cor e-9.0.46.jar!/:na] at org.apache.catalina.webresources.TomcatURLStreamHandlerFactory.regist er(TomcatURLStreamHandlerFactory.java:77) ~[jar:rsrc:tomcat-embed-core-9.0.46.ja r!/:na] at org.apache.catalina.webresources.StandardRoot.registerURLStreamHandle rFactory(StandardRoot.java:700) ~[jar:rsrc:tomcat-embed-core-9.0.46.jar!/:na] at org.apache.catalina.webresources.StandardRoot.initInternal(StandardRo ot.java:682) ~[jar:rsrc:tomcat-embed-core-9.0.46.jar!/:na] at org.apache.catalina.util.LifecycleBase.init(LifecycleBase.java:136) ~ [jar:rsrc:tomcat-embed-core-9.0.46.jar!/:na] ... 45 common frames omitted C:\Users\Administrator\Desktop>
最新发布
06-21
<think>我们遇到了一个SpringBoot启动失败的问题,关键错误信息是:Causedby:java.lang.Error:factoryalreadydefinedatjava.net.URL.setURLStreamHandlerFactory(UnknownSource)~[na:1.8.0_31]atorg.apache.catalina.webresources.TomcatURLStreamHandlerFactory.<init>(TomcatURLStreamHandlerFactory.java:130)这个错误表明Tomcat试图设置URLStreamHandlerFactory,但是JVM只允许设置一次,而之前已经设置过了。原因分析:在同一个JVM中,java.net.URL的setURLStreamHandlerFactory方法只能被调用一次。如果SpringBoot应用被打包成可执行jar,并且这个jar被其他类加载器加载(例如通过另一个SpringBoot应用加载),或者应用中存在多个Tomcat实例,则可能导致此问题。解决方案:1.确保没有多个Tomcat实例被初始化(比如重复的SpringBoot应用上下文)。2.避免在同一个JVM中多次启动SpringBoot应用(例如在测试中多次启动)。3.如果使用了自定义的类加载器,确保不会重复加载Tomcat相关类。4.在SpringBoot配置中禁用Tomcat的URL流处理工厂。具体步骤:方案一:升级SpringBoot版本这个问题在SpringBoot的较新版本中已经修复。尝试升级到SpringBoot2.4.x或更高版本。方案二:禁用Tomcat的URL流处理工厂在application.properties中添加:```propertiesserver.tomcat.register-url-protocols=false```方案:检查是否重复初始化确保在应用中只创建了一个SpringApplication实例。方案四:检查类路径确保类路径中没有重复的Tomcat嵌入包。方案五:使用自定义的TomcatServletWebServerFactory通过编程方式禁止Tomcat注册URL流处理工厂。```javaimportorg.apache.catalina.webresources.TomcatURLStreamHandlerFactory;importorg.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;importorg.springframework.boot.web.server.WebServerFactoryCustomizer;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;@ConfigurationpublicclassTomcatConfig{static{//防止TomcatURLStreamHandlerFactory被初始化多次TomcatURLStreamHandlerFactory.disable();}@BeanpublicWebServerFactoryCustomizer<TomcatServletWebServerFactory>tomcatCustomizer(){returnfactory->factory.addContextCustomizers(context->{//禁用资源注册context.setResources(null);});}}```方案六:检查打包方式如果是通过jarinjar的方式打包(例如使用SpringBootMavenPlugin),尝试改为war包或者可执行jar的单独运行。方案七:避免在测试中使用@SpringBootTest多次启动如果是在测试中遇到,确保使用@DirtiesContext注解来清理上下文。方案八:检查是否有其他库设置了URLStreamHandlerFactory查找项目中是否有其他代码调用了URL.setURLStreamHandlerFactory。示例代码:使用静态块禁用TomcatURLStreamHandlerFactory在SpringBoot主类中添加静态块:```javaimportorg.apache.catalina.webresources.TomcatURLStreamHandlerFactory;@SpringBootApplicationpublicclassDemoApplication{static{TomcatURLStreamHandlerFactory.disable();}publicstaticvoidmain(String[]args){SpringApplication.run(DemoApplication.class,args);}}```方案九:使用JavaAgent(高级)如果以上方法无效,考虑使用JavaAgent在类加载时修改URL类的行为(不推荐,除非必要)。方案十:检查JVM参数确保没有在JVM参数中设置自定义的URLStreamHandlerFactory。详细解释:Tomcat在初始化资源时,会尝试注册一个URLStreamHandlerFactory,用于处理"war"协议的URL。但是,JVM只允许设置一次。因此,如果应用中已经设置过(可能是另一个Tomcat实例,或者其他库设置过),就会抛出此错误。在SpringBoot中,通常是因为应用被打包成一个可执行jar,然后又被另一个应用作为依赖引入并执行,导致两个Tomcat实例尝试设置。或者,在同一个应用内,由于类加载器的原因,导致Tomcat类被加载多次。因此,最直接的解决方案是禁用Tomcat的资源注册功能,或者确保TomcatURLStreamHandlerFactory只被初始化一次。注意:禁用Tomcat的资源注册可能会影响静态资源的加载,但通常SpringBoot应用使用SpringMVC的ResourceHandlers,所以影响不大。如果问题依旧,请提供更多关于项目结构和打包方式的信息。§§相关问题§§:1.如何在SpringBoot中自定义Tomcat配置?2.如何解决SpringBoot应用在测试中多次启动导致的上下文冲突?3.SpringBoot不同版本中Tomcat自动配置有哪些变化?4.如何排查Java中的类加载器冲突问题?</think>## 解决 "factory already defined" 导致的 Tomcat 启动失败问题 从您提供的错误日志来看,核心问题是 `java.lang.Error: factory already defined`,这导致 Spring Boot 无法启动嵌入式 Tomcat 服务器。以下是完整的解决方案: ### 问题原因分析 1. **URLStreamHandlerFactory 重复注册**: - Tomcat 尝试注册自己的 `URLStreamHandlerFactory` - JVM 只允许注册一个全局 URLStreamHandlerFactory - 应用中其他组件(如另一个 Web 框架或库)已注册了工厂 2. **类加载器冲突**: - 多个类加载器加载了 Tomcat 相关类 - WAR 包部署和嵌入式 Tomcat 冲突 3. **依赖冲突**: - 多个版本的 Tomcat 相关库存在于类路径 - Spring Boot Starter Web 与其他 Web 容器冲突 4. **打包问题**: - 使用了非标准的打包方式 - 可执行 JAR 与外部 Tomcat 部署冲突 ### 解决方案 #### 1. 禁用 Tomcat 的 URLStreamHandlerFactory(推荐) 在 `application.properties` 中添加: ```properties # 禁用 Tomcat 的 URL 流处理器工厂 server.tomcat.register-url-protocols=false ``` 或在主配置类中添加: ```java import org.apache.catalina.webresources.TomcatURLStreamHandlerFactory; import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; import org.springframework.boot.web.server.WebServerFactoryCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class TomcatConfig { static { // 防止 TomcatURLStreamHandlerFactory 初始化 TomcatURLStreamHandlerFactory.disable(); } @Bean public WebServerFactoryCustomizer<TomcatServletWebServerFactory> tomcatCustomizer() { return factory -> factory.addContextCustomizers(context -> { // 禁用资源注册 context.setResources(null); }); } } ``` #### 2. 排除冲突依赖 在 `pom.xml` 中排除可能引起冲突的依赖: ```xml <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <!-- 排除 Tomcat 使用其他容器 --> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency> <!-- 使用 Jetty 替代 Tomcat --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jetty</artifactId> </dependency> <!-- 或者使用 Undertow --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-undertow</artifactId> </dependency> </dependencies> ``` #### 3. 修复类加载器问题 创建自定义类加载器配置: ```java import org.springframework.boot.loader.LaunchedURLClassLoader; import org.springframework.boot.loader.jar.JarFile; public class CustomClassLoader extends LaunchedURLClassLoader { public CustomClassLoader(JarFile jarFile) throws Exception { super(jarFile); } @Override protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { // 优先加载应用类 if (name.startsWith("com.kucun")) { return findClass(name); } // 避免重复加载 Tomcat 类 if (name.startsWith("org.apache.catalina")) { return getParent().loadClass(name); } return super.loadClass(name, resolve); } } ``` 在 `MANIFEST.MF` 中指定: ``` Main-Class: org.springframework.boot.loader.JarLauncher Class-Path: . Class-Loader: com.kucun.CustomClassLoader ``` #### 4. 升级 Spring Boot 版本 在 `pom.xml` 中升级到较新版本: ```xml <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.0</version> <!-- 或更高版本 --> </parent> <dependencies> <!-- 确保所有 starter 版本一致 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> ``` #### 5. 检查并修复打包问题 确保使用正确的打包插件: ```xml <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <mainClass>com.kucun.DemoApplication</mainClass> <executable>true</executable> <!-- 使用分层 JAR 优化 --> <layers> <enabled>true</enabled> </layers> </configuration> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> </plugins> </build> ``` #### 6. 检查运行时环境 创建环境检查类: ```java import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.stereotype.Component; @Component public class EnvChecker implements ApplicationRunner { @Override public void run(ApplicationArguments args) throws Exception { System.out.println("===== Environment Check ====="); System.out.println("ClassLoader: " + this.getClass().getClassLoader()); System.out.println("java.version: " + System.getProperty("java.version")); try { Class<?> factoryClass = Class.forName("java.net.URLStreamHandlerFactory"); System.out.println("URLStreamHandlerFactory class loaded by: " + factoryClass.getClassLoader()); } catch (ClassNotFoundException e) { System.out.println("URLStreamHandlerFactory not found"); } System.out.println("============================="); } } ``` ### 替代解决方案 如果上述方法无效,考虑以下替代方案: 1. **使用外部 Tomcat 部署**: ```bash # 打包为 WAR mvn clean package -DskipTests # 部署到外部 Tomcat cp target/kucun2.war $CATALINA_HOME/webapps/ ``` 2. **切换至 Spring Boot 3 和 Tomcat 10**: ```xml <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.0.0</version> </parent> ``` 3. **使用 GraalVM 原生编译**: ```xml <build> <plugins> <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> <version>0.9.10</version> <executions> <execution> <goals> <goal>compile</goal> </goals> </execution> </executions> </plugin> </plugins> </build> ``` ```bash mvn clean package -Pnative ``` ### 预防措施 1. **统一依赖管理**: ```xml <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.7.0</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> ``` 2. **定期清理依赖**: ```bash mvn dependency:tree -Dverbose -Dincludes=org.apache.tomcat mvn dependency:purge-local-repository ``` 3. **使用 Docker 容器化**: ```dockerfile FROM eclipse-temurin:17-jre VOLUME /tmp COPY target/*.jar app.jar ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"] ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值