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

use*_*944 33 java url uri protocols

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

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

我得到以下例外:

java.net.MalformedURLException:未知协议:来自java.net.URL的java.net.URL.(未知来源)中的CustomURI.(来自未知来源)位于com.demo.TestDemo的java.net.URL.(未知来源).主(TestDemo.java:14)

如果我URI从浏览器触发它然后它按预期工作但如果我尝试Java Program从那时调用它我得到上述异常.

编辑:

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

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

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

}

}
Run Code Online (Sandbox Code Playgroud)

当我运行这段代码时,我CustomURI:在我的控制台中打印(来自add方法)但是当我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)
Run Code Online (Sandbox Code Playgroud)

请告知如何使这项工作.

Bal*_*usC 62

  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!");
        }
    
    }
    
    Run Code Online (Sandbox Code Playgroud)

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


  2. 创建一个URLStreamHandler返回它的自定义实现openConnection().

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

    如有必要,请不要忘记覆盖并实现其他方法.


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

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

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


  4. 最后在应用程序启动时注册它 URL#setURLStreamHandlerFactory()

    URL.setURLStreamHandlerFactory(new CustomURLStreamHandlerFactory());
    
    Run Code Online (Sandbox Code Playgroud)

    请注意,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;
        }
    });
    
    Run Code Online (Sandbox Code Playgroud)

    如果您已经使用Java 8,请URLStreamHandlerFactory使用lambda 替换功能接口以进一步缩小:

    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);
    
    Run Code Online (Sandbox Code Playgroud)

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

URLConnection connection = new URL("CustomURI:blabla").openConnection();
connection.connect();
// ...
Run Code Online (Sandbox Code Playgroud)

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

URLConnection connection = new URL("customuri:blabla").openConnection();
connection.connect();
// ...
Run Code Online (Sandbox Code Playgroud)

  • 如果我们将所有必要的类放入"默认包"中,我们是否需要调用`URL.setURLStreamHandlerFactory()`?(哪一个是顺便?) (2认同)

Aja*_*jax 5

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

必须命名您的URLStreamHandlerHandler,并且映射到的协议是该类包的最后一部分。

因此,com.foo.myproto.Handler-> myproto:urls,前提是您将包添加com.foo到“ URL流源包”列表中以查找未知协议。您可以通过系统属性"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);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,这是实现抽象处理程序的实际类(对于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);
    }

}
Run Code Online (Sandbox Code Playgroud)