注册和使用自定义 java.net.URL 协议

2022-09-01 01:20:54

我试图从我的java程序中调用一个,因此我使用了这样的东西:custom url

URL myURL;
try {
   myURL = new URL("CustomURI:");
   URLConnection myURLConnection = myURL.openConnection();
   myURLConnection.connect();
} catch (Exception e) {
   e.printStackTrace();
}

我得到了以下异常:

java.net.MalformedURLException: unknown protocol: CustomURI at java.net.URL.(未知来源)在java.net.URL。(未知来源)在java.net.URL。(未知来源)在com.demo.TestDemo.main(TestDemo.java:14)

如果我从浏览器触发,那么它按预期工作,但如果我尝试从调用它,那么我就会得到上述异常。URIJava Program

编辑:

以下是我尝试过的步骤(我肯定错过了一些东西,请让我知道):

步骤 1:在 java.protocol.handler.pkgs 中添加自定义 URI

步骤 2:从 URL 触发自定义 URI

法典:

public class CustomURI {

public static void main(String[] args) {

    try {
        add("CustomURI:");
        URL uri = new URL("CustomURI:");
        URLConnection uc = uri.openConnection();            
        uc.connect();
    } catch (Exception e) {
        e.printStackTrace();
    }

}

public static void add( String handlerPackage ){

    final String key = "java.protocol.handler.pkgs";

    String newValue = handlerPackage;
    if ( System.getProperty( key ) != null )
    {
        final String previousValue = System.getProperty( key );
        newValue += "|" + previousValue;
    }
    System.setProperty( key, newValue );
    System.out.println(System.getProperty("java.protocol.handler.pkgs"));

}

}

当我运行此代码时,我正在我的控制台(从 add 方法)中获取打印的代码,但是当 使用 构造函数初始化时,我会收到此异常:CustomURI:URLCustomURI:

Exception in thread "main" java.lang.StackOverflowError
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Unknown Source)
at java.net.URL.getURLStreamHandler(Unknown Source)
at java.net.URL.<init>(Unknown Source)
at java.net.URL.<init>(Unknown Source)
at sun.misc.URLClassPath$FileLoader.getResource(Unknown Source)
at sun.misc.URLClassPath.getResource(Unknown Source)
at java.net.URLClassLoader$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at java.net.URL.getURLStreamHandler(Unknown Source)
at java.net.URL.<init>(Unknown Source)
at java.net.URL.<init>(Unknown Source)

请建议如何使这项工作。


答案 1
  1. 创建一个自定义 URLConnection 实现,用于在 方法中执行作业。connect()

    public class CustomURLConnection extends URLConnection {
    
        protected CustomURLConnection(URL url) {
            super(url);
        }
    
        @Override
        public void connect() throws IOException {
            // Do your job here. As of now it merely prints "Connected!".
            System.out.println("Connected!");
        }
    
    }
    

    不要忘记覆盖和实现其他方法,例如相应的方法。由于问题中缺少此信息,因此无法提供有关此的更多详细信息。getInputStream()


  2. 创建一个自定义 URLStreamHandler 实现,该实现在 中返回该实现。openConnection()

    public class CustomURLStreamHandler extends URLStreamHandler {
    
        @Override
        protected URLConnection openConnection(URL url) throws IOException {
            return new CustomURLConnection(url);
        }
    
    }
    

    如有必要,不要忘记重写和实现其他方法。


  3. 创建一个自定义 URLStreamHandlerFactory,该站点根据协议创建并返回它。

    public class CustomURLStreamHandlerFactory implements URLStreamHandlerFactory {
    
        @Override
        public URLStreamHandler createURLStreamHandler(String protocol) {
            if ("customuri".equals(protocol)) {
                return new CustomURLStreamHandler();
            }
    
            return null;
        }
    
    }
    

    请注意,协议始终是小写的。


  4. 最后在应用程序启动期间通过URL#setURLStreamHandlerFactory()注册它

    URL.setURLStreamHandlerFactory(new CustomURLStreamHandlerFactory());
    

    请注意,Javadoc 明确指出您最多可以设置一次。因此,如果您打算在同一应用程序中支持多个自定义协议,则需要生成自定义实现以在方法中涵盖所有协议。URLStreamHandlerFactorycreateURLStreamHandler()


    或者,如果您不喜欢德墨忒耳定律,请将其全部放在匿名类中以进行代码缩小:

    URL.setURLStreamHandlerFactory(new URLStreamHandlerFactory() {
        public URLStreamHandler createURLStreamHandler(String protocol) {
            return "customuri".equals(protocol) ? new URLStreamHandler() {
                protected URLConnection openConnection(URL url) throws IOException {
                    return new URLConnection(url) {
                        public void connect() throws IOException {
                            System.out.println("Connected!");
                        }
                    };
                }
            } : null;
        }
    });
    

    如果您已经在使用 Java 8,请将函数接口替换为 lambda 以进行进一步缩小:URLStreamHandlerFactory

    URL.setURLStreamHandlerFactory(protocol -> "customuri".equals(protocol) ? new URLStreamHandler() {
        protected URLConnection openConnection(URL url) throws IOException {
            return new URLConnection(url) {
                public void connect() throws IOException {
                    System.out.println("Connected!");
                }
            };
        }
    } : null);
    

现在,您可以按如下方式使用它:

URLConnection connection = new URL("CustomURI:blabla").openConnection();
connection.connect();
// ...

或者根据规范使用小写协议:

URLConnection connection = new URL("customuri:blabla").openConnection();
connection.connect();
// ...

答案 2

如果您不想接管唯一的URLStreamHandlerFactory,您实际上可以使用一个可怕但有效的命名约定来了解默认实现。

你必须命名你的类,它映射到的协议是该类包的最后一段。URLStreamHandlerHandler

因此,->,前提是您将包添加到“url 流源包”列表中,以便在未知协议上进行查找。您可以通过系统属性(这是要搜索的包名称的|分隔列表)执行此操作。com.foo.myproto.Handlermyproto:urlscom.foo"java.protocol.handler.pkgs"

这是一个抽象类,可以执行您需要的内容:(不要介意缺少的或,这些类按照其名称所暗示的方式进行,您可以使用您喜欢的任何抽象)StringTo<Out1<String>>StringURLConnection

public abstract class AbstractURLStreamHandler extends URLStreamHandler {

    protected abstract StringTo<Out1<String>> dynamicFiles();

    protected static void addMyPackage(Class<? extends URLStreamHandler> handlerClass) {
        // Ensure that we are registered as a url protocol handler for JavaFxCss:/path css files.
        String was = System.getProperty("java.protocol.handler.pkgs", "");
        String pkg = handlerClass.getPackage().getName();
        int ind = pkg.lastIndexOf('.');
        assert ind != -1 : "You can't add url handlers in the base package";
        assert "Handler".equals(handlerClass.getSimpleName()) : "A URLStreamHandler must be in a class named Handler; not " + handlerClass.getSimpleName();

        System.setProperty("java.protocol.handler.pkgs", handlerClass.getPackage().getName().substring(0, ind) +
            (was.isEmpty() ? "" : "|" + was ));
    }


    @Override
    protected URLConnection openConnection(URL u) throws IOException {
        final String path = u.getPath();
        final Out1<String> file = dynamicFiles().get(path);
        return new StringURLConnection(u, file);
    }
}

然后,下面是实现抽象处理程序的实际类(对于 url:dynamic:

package xapi.dev.api.dynamic;

// imports elided for brevity

public class Handler extends AbstractURLStreamHandler {

    private static final StringTo<Out1<String>> dynamicFiles = X_Collect.newStringMap(Out1.class,
        CollectionOptions.asConcurrent(true)
            .mutable(true)
            .insertionOrdered(false)
            .build());

    static {
        addMyPackage(Handler.class);
    }

    @Override
    protected StringTo<Out1<String>> dynamicFiles() {
        return dynamicFiles;
    }

    public static String registerDynamicUrl(String path, Out1<String> contents) {
        dynamicFiles.put(path, contents);
        return path;
    }

    public static void clearDynamicUrl(String path) {
        dynamicFiles.remove(path);
    }

}