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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26363573/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 02:22:04  来源:igfitidea点击:

Registering and using a custom java.net.URL protocol

javaurluriprotocols

提问by user182944

I was trying to invoke a custom urlfrom my java program, hence I used something like this:

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

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

I got the below exception:

我得到以下异常:

java.net.MalformedURLException: unknown protocol: CustomURI at java.net.URL.(Unknown Source) at java.net.URL.(Unknown Source) at java.net.URL.(Unknown Source) at com.demo.TestDemo.main(TestDemo.java:14)

java.net.MalformedURLException: 未知协议:CustomURI at java.net.URL.(Unknown Source) at java.net.URL.(Unknown Source) at java.net.URL.(Unknown Source) at com.demo.TestDemo。主要(TestDemo.java:14)

If I trigger the URIfrom a browser then it works as expected but if I try to invoke it from the Java Programthen I am getting the above exception.

如果我URI从浏览器触发,那么它会按预期工作,但如果我尝试从浏览器调用它,Java Program则会出现上述异常。

EDIT:

编辑:

Below are the steps I tried (I am missing something for sure, please let me know on that):

以下是我尝试过的步骤(我确实遗漏了一些东西,请告诉我):

Step 1: Adding the Custom URI in java.protocol.handler.pkgs

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

Step 2: Triggering the Custom URI from URL

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

Code:

代码:

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"));

}

}

When I run this code, I am getting the CustomURI:printed in my console (from the add method) but then I am getting this exception when the URLis initialized with CustomURI:as a constructor:

当我运行这段代码时,我CustomURI:在我的控制台中打印出来(来自 add 方法)但是当我作为构造函数URL初始化时我得到这个异常CustomURI:

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.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)

Please advice how to make this work.

请建议如何使这项工作。

采纳答案by BalusC

  1. Create a custom URLConnectionimplementation which performs the job in connect()method.

    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!");
        }
    
    }
    

    Don't forget to override and implement other methods like getInputStream()accordingly. More detail on that cannot be given as this information is missing in the question.


  2. Create a custom URLStreamHandlerimplementation which returns it in openConnection().

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

    Don't forget to override and implement other methods if necessary.


  3. Create a custom URLStreamHandlerFactorywhich creates and returns it based on the protocol.

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

    Note that protocols are alwayslowercase.


  4. Finally register it during application's startup via URL#setURLStreamHandlerFactory()

    URL.setURLStreamHandlerFactory(new CustomURLStreamHandlerFactory());
    

    Note that the Javadocexplicitly says that you can set it at most once. So if you intend to support multiple custom protocols in the same application, you'd need to generify the custom URLStreamHandlerFactoryimplementation to cover them all inside the createURLStreamHandler()method.


    Alternatively, if you dislike the Law of Demeter, throw it all together in anonymous classes for code minification:

    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;
        }
    });
    

    If you're on Java 8 already, replace the URLStreamHandlerFactoryfunctional interface by a lambda for further minification:

    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);
    
  1. 创建URLConnectionconnect()方法中执行作业的自定义实现。

    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明确指出您最多可以设置一次。因此,如果您打算在同一应用程序中支持多个自定义协议,则需要泛化自定义URLStreamHandlerFactory实现以将它们全部包含在createURLStreamHandler()方法中。


    或者,如果您不喜欢 Demeter 法则,请将其全部放在匿名类中以进行代码精简:

    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,请用URLStreamHandlerFactorylambda替换功能接口以进一步缩小:

    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);
    


Now you can use it as follows:

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

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

Or with lowercased protocol as per the spec:

或者按照规范使用小写协议:

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

回答by Grim

You made a recursion/endless-loop.

你做了一个递归/无限循环。

The Classloader searching for the class in different ways.

类加载器以不同的方式搜索类。

The stacktrace (URLClassPath) is like this:

堆栈跟踪(URLClassPath)是这样的:

  1. Load a resource.
  2. Did i load every protocol? No!
  3. Load all protocoll-Handlers, i cant find the File ?your java.protocol.handler.pkgs-package?.CustomURI.Handler.
  4. The class is a resource! Did i load every protocol? No!
  5. Load all protocoll-Handlers, i cant find the File ?your java.protocol.handler.pkgs-package?.CustomURI.Handler.
  6. The class is a resource! Did i load every protocol? No!
  7. Load all protocoll-Handlers, i cant find the File ?your java.protocol.handler.pkgs-package?.CustomURI.Handler.
  8. The class is a resource! Did i load every protocol? No!
  9. Load all protocoll-Handlers, i cant find the File ?your java.protocol.handler.pkgs-package?.CustomURI.Handler.
  10. The class is a resource! Did i load every protocol? No!
  11. Load all protocoll-Handlers, i cant find the File ?your java.protocol.handler.pkgs-package?.CustomURI.Handler.

    ...... StackOverflowException!!!

  1. 加载资源。
  2. 我是否加载了每个协议?不!
  3. 加载所有协议处理程序,我找不到文件?your java.protocol.handler.pkgs-package?.CustomURI.Handler
  4. 课堂是一种资源!我是否加载了每个协议?不!
  5. 加载所有协议处理程序,我找不到文件?your java.protocol.handler.pkgs-package?.CustomURI.Handler
  6. 课堂是一种资源!我是否加载了每个协议?不!
  7. 加载所有协议处理程序,我找不到文件?your java.protocol.handler.pkgs-package?.CustomURI.Handler
  8. 课堂是一种资源!我是否加载了每个协议?不!
  9. 加载所有协议处理程序,我找不到文件?your java.protocol.handler.pkgs-package?.CustomURI.Handler
  10. 课堂是一种资源!我是否加载了每个协议?不!
  11. 加载所有协议处理程序,我找不到文件?your java.protocol.handler.pkgs-package?.CustomURI.Handler

    ...... StackOverflowException!!!

回答by Ajax

If you don't want to take over the one-and-only URLStreamHandlerFactory, you can actually use a hideous, but effective naming convention to get in on the default implementation.

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

You mustname your URLStreamHandlerclass Handler, and the protocol it maps to is the last segment of that class' package.

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

So, com.foo.myproto.Handler->myproto:urls, provided you add your package com.footo the list of "url stream source packages" for lookup on unknown protocol. You do this via system property "java.protocol.handler.pkgs"(which is a | delimited list of package names to search).

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

Here is an abstract class that performs what you need: (don't mind the missing StringTo<Out1<String>>or StringURLConnection, these do what their names suggest and you can use whatever abstractions you prefer)

这是一个执行您需要的抽象类:(不要介意缺少的StringTo<Out1<String>>or 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);
    }
}

Then, here is the actual class implementing the abstract handler (for dynamic:urls:

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

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);
    }

}