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

时间:2014-10-14 14:38:47

标签: java url uri protocols

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

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

我得到以下例外:

  

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

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

修改

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

第1步:在java.protocol.handler.pkgs

中添加自定义URI

第2步:从网址触发自定义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"));

}

}

当我运行此代码时,我在控制台中打印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$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)

请告知如何使这项工作。

3 个答案:

答案 0 :(得分:59)

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

    请注意,协议为always小写。


  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,请将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);
    

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

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

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

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

答案 1 :(得分:3)

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

必须为您的URLStreamHandler班级Handler命名,其所映射的协议是该班级的最后一段&#39;封装

所以,com.foo.myproto.Handler - &gt; myproto:urls, 如果您将包com.foo添加到&#34; url流源包列表中,并且#34;用于查找未知协议。您可以通过系统属性"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);
    }
}

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

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

}

答案 2 :(得分:1)

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

Classloader以不同方式搜索类。

stacktrace(URLClassPath)是这样的:

  1. 加载资源。
  2. 我加载了每个协议吗?不!
  3. 加载所有protocoll-Handler,我找不到文件«your java.protocol.handler.pkgs-package».CustomURI.Handler
  4. 班级是资源!我加载了每个协议吗?不!
  5. 加载所有protocoll-Handler,我找不到文件«your java.protocol.handler.pkgs-package».CustomURI.Handler
  6. 班级是资源!我加载了每个协议吗?不!
  7. 加载所有protocoll-Handler,我找不到文件«your java.protocol.handler.pkgs-package».CustomURI.Handler
  8. 班级是资源!我加载了每个协议吗?不!
  9. 加载所有protocoll-Handler,我找不到文件«your java.protocol.handler.pkgs-package».CustomURI.Handler
  10. 班级是资源!我加载了每个协议吗?不!
  11. 加载所有protocoll-Handler,我找不到文件«your java.protocol.handler.pkgs-package».CustomURI.Handler

    ...... StackOverflowException !!!