java:解决URL.setURLStreamHandlerFactory只能被jvm调用一次的问题

本文介绍如何通过自定义URLStreamHandlerFactory解决Java URL协议扩展问题。面对URL.setURLStreamHandlerFactory方法的独占性限制,文章推荐使用Apache Commons Sandbox提供的commons-jnet库,利用Java反射技术确保自定义工厂注册成功。

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

如果你能找到这篇博客,你肯定是为实现URL协议扩展时自定义协议的StreamHandlerFactory注册问题而头痛。
一般而言,URL 的格式是: protocol://[authority]hostname:port/resource?queryString
常见协议头(protocol)有http,https,file。对应不同的协议,java都有提供默认URLStreamHandler对象来解析这些协议,如下图,这些位于rt.jar包中每一个package都对应一种协议,package下都有一个继承自URLStreamHandler的Handler类用于对应协议解析
这里写图片描述
如果要实现自己的协议,就需要自己写一个URLStreamHandler,如何写URLStreamHandler与具体项目需求相关,不是本文要讨论的重点。当我们想让自己写的URLStreamHandler生效,就需要将它注册到URL中,这篇文章《Java URL协议扩展实现》详细描述了两种机制,来实现URL协议扩展。
第一种方法就是用URL.setURLStreamHandlerFactory方法将自己的URLStreamHandlerFactory注册到URL类中。我打算采用的就是这种方式,因为这种方式相比jvm参数方式更加可控。
然而,根据URL.setURLStreamHandlerFactory方法的说明以及其代码可知,这个方法具有独占性,在JVM运行时只能被调用一次。(现在看来,这应该算是java的一个设计缺陷)
一般情况下,我们不一定能保证在自己调用URL.setURLStreamHandlerFactory时是第一次,所以调用很有可能失败。
怎么解决这个问题呢?Apache Commons Sandbox提供了一个解决方法,就是commons-jnet,它基本原理就是使用java reflect技术,强行改变URL中的私有成员变量factory(类型为URLStreamHandlerFactory)来保setURLStreamHandlerFactory能被成功调用,并且不破坏原有的factory。
common-jnet代码非常少,只有4个类,没有提供jar包,只是提供源码,从svn上checkout出来加入自己的项目代码就可以使用了

svn checkout http://svn.apache.org/repos/asf/commons/sandbox/jnet/trunk commons-jnet

具体的使用方式,common-jnet的官网上说明得非常明白也非常简单。
http://commons.apache.org/sandbox/commons-jnet/

找到common-jnet之前就发现org.eclipse.osgi中的EquinoxFactoryManager就是用相同的办法解决这个问题的,只是其中的代码混在一起不好摘出来。

参见
EquinoxFactoryManager.installURLStreamHandlerFactory方法和
EquinoxFactoryManager.forceURLStreamHandlerFactory方法的源码

参考资料:

《Java URL协议扩展实现》
apache.sandbox.commons-jnet

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
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

10km

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值