RestTemplate proxy 设置方式

RestTemplate restTemplate = new RestTemplate(new SimpleClientHttpRequestFactory() {{
        setProxy(new java.net.Proxy(java.net.Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 8888)));
    }});
restTemplate.exchange的参数
url,HttpMethod,
FormEntity<Body,Header>,
ResponseBodyType,
Object... ---> queryString param

更多功能强大的proxy设置:
https://docs.spring.io/spring-boot/docs/current/reference/html/howto-http-clients.html


//简单
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setProxy(new Proxy(Type.HTTP, new InetSocketAddress(proxyHost, proxyPort)));
return new RestTemplate(factory);
static class ProxyCustomizer implements RestTemplateCustomizer {
//复杂
    @Override
    public void customize(RestTemplate restTemplate) {
        HttpHost proxy = new HttpHost("proxy.example.com");
        HttpClient httpClient = HttpClientBuilder.create()
                .setRoutePlanner(new DefaultProxyRoutePlanner(proxy) {
                    @Override
                    public HttpHost determineProxy(HttpHost target,
                            HttpRequest request, HttpContext context)
                            throws HttpException {
                        if (target.getHostName().equals("192.168.0.5")) {
                            return null;
                        }
                        return super.determineProxy(target, request, context);
                    }
                }).build();
        restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(httpClient));
    }

 

针对原生的一些代码可以这样, 也就是 java -Dhttp.proxyHost=xxxxx 等等,

https://docs.oracle.com/javase/jp/8/docs/technotes/guides/net/proxies.html

System.setProperty("http.proxyHost", proxyHost);
System.setProperty("http.proxyPort", "" + proxyPort);

 官网文档还是写得很好的.

2.1) HTTP
HTTPプロトコル・ハンドラが使用するプロキシを指定するため、次の3つのプロパティを設定できます。

http.proxyHost: プロキシ・サーバーのホスト名
http.proxyPort: ポート番号(デフォルト値は80)。
http.nonProxyHosts: プロキシを省略して、直接到達するホストのリスト。これは、「|」で区切られたパターンのリストです。パターンは、ワイルドカードを表す「*」ではじまるか終わることがあります。これらのいずれかのパターンに一致するすべてのホストに対して、プロキシを経由せずに接続が直接実行されます。
GetURLクラスのmainメソッドの実行を試みている場合の例をいくつか見てみましょう。

$ java -Dhttp.proxyHost=webcache.example.com GetURL
すべてのHTTP接続は、ポート80上で待機しているwebcache.example.comのプロキシ・サーバーを経由して接続されます(ポートの指定は行なっていないため、デフォルト・ポートが使用される)。

$ java -Dhttp.proxyHost=webcache.example.com -Dhttp.proxyPort=8080
-Dhttp.nonProxyHosts=”localhost|host.example.com” GetURL
2番目の例でも、プロキシ・サーバーは引き続きwebcache.example.com上に存在しますが、待機するポートが8080になります。また、localhostまたはhost.example.comへの接続時には、プロキシは使用されません。

すでに説明したように、これらのオプションを使って呼び出されたVMの寿命全体で、すべてのHTTP接続がこれらの設定の影響を受けます。ただし、System.setProperty()メソッドを使用すれば、動作を若干動的にできます。

次のコードにその方法を示します。

//Set the http proxy to webcache.example.com:8080

System.setProperty("http.proxyHost", "webcache.example.com");
System.setProperty("http.proxyPort", "8080");

// Next connection will be through proxy.
URL url = new URL("http://java.example.org/");
InputStream in = url.openStream();

// Now, let's 'unset' the proxy.
System.clearProperty("http.proxyHost");

// From now on http connections will be done directly.
これは、ほぼ良好に動作しますが、アプリケーションがマルチスレッドに対応している場合は扱いにくくなることがあります。システム・プロパティは「VM全体」の設定であるため、すべてのスレッドが影響を受けることに留意してください。このため、あるスレッド内のコードの副作用として、その他のスレッド内のコードが動作不能になる可能性があります。

2.2) HTTPS
HTTPS (HTTP over SSL)プロトコル・ハンドラは、独自のプロパティ・セットを保持します。

https.proxyHost
https.proxyPort
これらの動作は対応するHTTPの動作と正確に同じであることが推測できるため、詳細には触れません。ただし、この場合、デフォルトのポート番号は443になり、HTTPSプロトコル・ハンドラが使用する「非プロキシ・ホスト」リストのデフォルトのポート番号は、HTTPハンドラ(http.nonProxyHosts)と同じになります。

2.3) FTP
FTPプロトコル・ハンドラの設定は、HTTPの場合と同じ規則に従います。唯一の相違点は、各プロパティ名の接頭辞が「http.」ではなく「ftp.」になることです。

このため、システム・プロパティは次のようになります。

ftp.proxHost
ftp.proxyPort
ftp.nonProxyHosts
ここで、「非プロキシ・ホスト」リストに対して別個のプロパティが存在することに留意してください。また、HTTPの場合と同様、デフォルトのポート番号値は80になります。プロキシを経由するとき、FTPプロトコル・ハンドラは実際にはHTTPを使用してプロキシ・サーバーにコマンドを発行することに留意してください。デフォルトのポート番号が同じなのは、このためです。

簡潔な例を見てみることにしましょう。

$ java -Dhttp.proxyHost=webcache.example.com
-Dhttp.proxyPort=8080 -Dftp.proxyHost=webcache.example.com -Dftp.proxyPort=8080 GetURL
この例では、HTTPとFTPの両方のプロトコル・ハンドラが、webcache.example.com:8080にある同じプロキシ・サーバーを使用します。

2.4) SOCKS
RFC 1928に定義されているように、SOCKSプロトコルは、クライアント・サーバー・アプリケーションがTCPとUDPの両方のレベルでファイアウォールを安全にトラバースするためのフレームワークを提供します。この点では、このプロトコルは、より高レベルのプロキシ(HTTPまたはFTP固有のプロキシ)よりも高い汎用性を持ちます。Java SE 5.0は、クライアントTCPソケット用のSOCKSをサポートします。

SOCKS関連のシステム・プロパティには、次の2つがあります。

socksProxyHost - SOCKSプロキシ・サーバーのホスト名用
socksProxyPort - ポート番号用(デフォルト値は1080)
ここでは、接頭辞のあとにドット(「.」)を付けないことに留意してください。これには、歴史的な理由とともに、下位互換性を維持する目的があります。この方法でSOCKSプロキシが指定されると、すべてのTCP接続がこのプロキシを介して試みられます。

例:

$ java -DsocksProxyHost=socks.example.com GetURL
この場合、コードの実行中に、外向きのTCPソケットが、socks.example.com:1080でSOCKSプロキシ・サーバーを経由します。

では、SOCKSプロキシとHTTPプロキシの両方が定義されている場合はどうなるでしょうか。その場合は、HTTPやFTPなど、より高レベルのプロトコルの設定がSOCKS設定に優先されます。このため、この場合は、HTTP接続が確立されるとSOCKSプロキシの設定は無視され、HTTPプロキシへの連絡が行われます。例を見てみましょう。

$ java -Dhttp.proxyHost=webcache.example.com -Dhttp.proxyPort=8080
-DsocksProxyHost=socks.example.com GetURL
ここでは、HTTP設定が優先されるため、HTTP URLはwebcache.example.com:8080を経由します。では、FTP URLについてはどうでしょうか。FTPには特定のプロキシ設定が割り当てられておらず、FTPはTCPの最上位に位置するため、SOCKSプロキシ・サーバー(socks.example.com:1080)経由でFTP接続が試みられます。FTPプロキシが指定されている場合は、そのプロキシが代わりに使用されます。

3) Proxyクラス
すでに説明したとおり、システム・プロパティは強力ですが、柔軟性に欠けています。その「全部かゼロか」という動作は、大半の開発者には厳しすぎる制限に感じられます。このため、Java SE 5.0では、プロキシ設定に基づく接続を可能にする、より柔軟性の高い新規APIの導入が決定されました。

この新規APIのコアは、プロキシ定義を表すProxyクラスです。通常、プロキシ定義にはタイプ(HTTP、SOCKS)およびソケット・アドレスが含まれます。Java SE 5.0では、次の3つのタイプを指定可能です。

DIRECT - 直接接続(プロキシなし)を表します。
HTTP - HTTPプロトコルを使用するプロキシを表します。
SOCKS - SOCKS v4またはv5を使用するプロキシを表します。
このため、HTTPプロキシ・オブジェクトを作成するには、次の呼出しを実行します。

SocketAddress addr = new
InetSocketAddress("webcache.example.com", 8080);
Proxy proxy = new Proxy(Proxy.Type.HTTP, addr);
この新規プロキシ・オブジェクトはプロキシ定義を表すだけで、それ以上の意味はありません。このオブジェクトをどのように使用したらよいでしょうか。URLクラスに追加された新しいopenConnection()メソッドで、引数としてProxyを指定します。この場合の動作は、指定したプロキシ経由で接続を強制的に確立し、前述のシステム・プロパティを含む、その他の設定すべてを無視することを除き、引数を指定せずにopenConnection()を実行した場合と同じです。

このため、次のコードを追加して前述の例を完了させることができます。

URL url = new URL("http://java.example.org/");
URLConnection conn = url.openConnection(proxy);
使い方は、このように簡単です。

イントラネット上のURLなど、特定のURLへの直接接続を指定する場合にも、同じメカニズムを使用できます。このような場合に、DIRECTタイプが役立ちます。ただし、DIRECTタイプでは、プロキシ・インスタンスを作成する必要はありません。静的メンバーNO_PROXYを使用するだけです。

URL url2 = new URL("http://infos.example.com/");
URLConnection conn2 = url2.openConnection(Proxy.NO_PROXY);
これにより、その他のプロキシ設定を無視して、特定のURLを直接接続で確実に取得できます。これは、便利な方法です。

URLConnectionにSOCKSプロキシを強制的に経由させることもできます。

SocketAddress addr = new InetSocketAddress("socks.example.com", 1080);
Proxy proxy = new Proxy(Proxy.Type.SOCKS, addr);
URL url = new URL("ftp://ftp.gnu.org/README");
URLConnection conn = url.openConnection(proxy);
このFTP接続は、指定したSOCKSプロキシを介して試みられます。見ておわかりのように、これはきわめて直接的な方法です。

最後に、新たに導入されたソケット・コンストラクタを使って、個別のTCPソケット用のプロキシを指定することもできます。説明する順番は最後になりますが、これも重要な方法です。

SocketAddress addr = new InetSocketAddress("socks.example.com", 1080);
Proxy proxy = new Proxy(Proxy.Type.SOCKS, addr);
Socket socket = new Socket(proxy);
InetSocketAddress dest = new InetSocketAddress("server.example.org", 1234);
socket.connect(dest);
ここでは、ソケットは、指定したSOCKSプロキシ経由で、宛先アドレス(server.example.org:1234)への接続を試みます。

URLの場合は、グローバルな設定の影響を受けることなく、同じメカニズムを使って直接(プロキシを経由しない)接続を確実に試みることができます。

Socket socket = new Socket(Proxy.NO_PROXY);
socket.connect(new InetAddress("localhost", 1234));
Java SE 5.0では、この新規コンストラクタが受け入れるプロキシは、SOCKSまたはDIRECT (NO_PROXYインスタンス)の2種類のみです。

4) ProxySelector
これまでの説明からおわかりのように、Java SE 5.0では、開発者はプロキシを強力かつ柔軟に制御できます。それでも、プロキシ間で負荷分散を実行したり、宛先に応じてプロキシを変更したりするなど、これまで説明されたAPIでは実現が難しい状況で、使用するプロキシを動的に決定したい場面に遭遇することがあります。このような場合に、ProxySelectorが役立ちます。

簡潔に説明すると、ProxySelectorは、指定されたURLで使用すべきプロキシが存在する場合に、そのことをプロトコル・ハンドラに伝えるコードです。たとえば、次のコードを考えてみましょう。

URL url = new URL("http://java.example.org/index.html");
URLConnection conn = url.openConnection();
InputStream in = conn.getInputStream();
この時点で、HTTPプロトコル・ハンドラが呼び出され、proxySelectorへのクエリーを実行します。クエリーの内容は、次のようなものになります。

ハンドラ: やあ、java.example.orgまでたどり着きたいんだけど、プロキシを使うべきかな。
ProxySelector: どのプロトコルを使うつもりなんだい。
ハンドラ: もちろん、HTTPさ。
ProxySelector: ポートはデフォルト・ポートかい。
ハンドラ: 確認してみるからちょっと待って... そのとおり、デフォルト・ポートだ。
ProxySelector: わかった。じゃあ、プロキシにはwebcache.example.comを使って。ポートは8080でね。
ハンドラ: ありがとう。<休止>ねえ、webcache.example.com:8080が応答しないみたいなんだけど。他の方法はないの。
ProxySelector: それは困ったね。OK、じゃあwebcache2.example.comをポート8080で試してみて。
ハンドラ: 了解。今度はうまくいっているみたいだ。ありがとう。
ProxySelector: お安いご用さ。じゃあね。
もちろん、この会話はいくらか脚色してありますが、やり取りの内容は理解できるでしょう。

ProxySelectorのもっともすばらしい点は、プラグインが可能であることです。このため、デフォルトでは望みの機能を得られない場合、かわりを記述してそれをプラグインとして使用できます。

では、ProxySelectorとはどのようなものでしょうか。クラス定義を見てみることにしましょう。

public abstract class ProxySelector {
        public static ProxySelector getDefault();
        public static void setDefault(ProxySelector ps);
        public abstract List<Proxy> select(URI uri);
        public abstract void connectFailed(URI uri,
                SocketAddress sa, IOException ioe);
}
ここからわかるように、ProxySelectorはデフォルト実装の設定用と取得用の2つの静的メソッドを持つabstractクラスです。使用するプロキシを決定するため、またはプロキシへの到達が不可能のようであることを通知するため、プロトコル・ハンドラにより2つのインスタンス・メソッドが使用されます。独自のProxySelectorを提供する場合に実行すべきことは、このクラスを拡張し、これら2つのインスタンス・メソッドの実装を提供してから、新規クラスのインスタンスを引数として渡してProxySelector.setDefault()を呼び出すことです。この時点で、プロトコル・ハンドラは、HTTPやFTPと同様に、新規ProxySelectorへのクエリーを実行して使用するプロキシを決定します。

この種のProxySelectorの記述方法の詳細を説明する前に、デフォルトのProxySelectorについて説明しましょう。Java SE 5.0の提供するデフォルト実装では、下位互換性が維持されています。つまり、デフォルトのProxySelectorは、前述のシステム・プロパティをチェックして使用するプロキシを決定します。ただし、新しいオプション機能も存在します。最近のWindowsシステムやGnome 2.xプラットフォームでは、デフォルトのProxySelectorに対してシステムのプロキシ設定の使用を指示できます(最近のバージョンのWindowsおよびGnome 2.xでは、ユーザー・インタフェースからプロキシをグローバルに設定することが可能)。システム・プロパティjava.net.useSystemProxiesがtrueに設定されている場合(デフォルトでは互換性維持の目的でfalseに設定されている)、デフォルトのProxySelectorはこれらの設定の使用を試みます。このシステム・プロパティは、コマンド行で設定することも、JREインストール・ファイルlib/net.propertiesを編集して設定することもできます。このような方法で、所定のシステムでシステム・プロパティを一度だけ変更する必要があります。

それでは、新規ProxySelectorを記述およびインストールする方法について説明しましょう。

実現したいことは、次のとおりです。デフォルトのProxySelectorの動作にはかなり満足していますが、HTTPおよびHTTPSについては変更が必要です。ネットワーク上には、これらのプロトコルに対応したプロキシが複数存在するため、アプリケーションがこれらのプロキシを順番に試みるようにしたいと考えています。つまり、最初のプロキシが応答しない場合に、2番目のプロキシの使用を試み、2番目のプロキシが応答しない場合には3番目のプロキシの使用を試みる、という具合です。さらに、あるプロキシが頻繁に失敗するようであれば、それをリストから削除して最適化を行います。

実行する作業は、java.net.ProxySelectorをサブクラス化すること、およびselect()メソッドとconnectFailed()メソッドの実装を提供することだけです。

宛先への接続を試みる前に、プロトコル・ハンドラによりselect()メソッドが呼び出されます。渡される引数は、リソース(プロトコル、ホスト、およびポート番号)が記述されたURIです。そのあと、このメソッドにより、プロキシのリストが返されます。たとえば、次にコードを示します。

URL url = new URL("http://java.example.org/index.html");
InputStream in = url.openStream();
このコードにより、次の擬似呼出しがプロトコル・ハンドラ内でトリガーされます。

List<Proxy> l = ProxySelector.getDefault().select(new URI("http://java.example.org/"));
この実装で行うべきことは、URIからのプロトコルが本当にHTTP (またはHTTPS)であるかを確認し、そのとおりであればプロキシのリストを返し、そうでなければデフォルト・プロキシに委譲することだけです。この場合、使用するプロトコルがデフォルトになるため、コンストラクタ内に以前のデフォルトへの参照を格納する必要があります。

このため、コードの先頭部分は次のようになります。

public class MyProxySelector extends ProxySelector {
        ProxySelector defsel = null;
        MyProxySelector(ProxySelector def) {
                defsel = def;
        }
        
        public java.util.List<Proxy> select(URI uri) {
                if (uri == null) {
                        throw new IllegalArgumentException("URI can't be null.");
                }
                String protocol = uri.getScheme();
                if ("http".equalsIgnoreCase(protocol) ||
                        "https".equalsIgnoreCase(protocol)) {
                        ArrayList<Proxy> l = new ArrayList<Proxy>();
                        // Populate the ArrayList with proxies
                        return l;
                }
                if (defsel != null) {
                        return defsel.select(uri);
                } else {
                        ArrayList<Proxy> l = new ArrayList<Proxy>();
                        l.add(Proxy.NO_PROXY);
                        return l;
                }
        }
}
最初に、コンストラクタが以前のデフォルト・セレクタへの参照を維持することに留意してください。次に、仕様に準拠するために、select()メソッド内の不正な引数をチェックすることに注目してください。最後に、以前のデフォルトが存在する場合、必要に応じてそれを保留することに注目します。ArrayListの生成方法については、格別興味を引くものではないため、この例では詳しく説明しません。関心がある場合には、付録にコード全体が記載されているので、それを参照してください。

見てわかるとおり、connectFailed()メソッドの実装が含まれていないため、このクラスは完全なものではありません。次の手順で、このメソッドを実装します。

select()メソッドから返されたいずれかのプロキシへの接続が失敗した場合は常に、connectFailed()メソッドがプロトコル・ハンドラにより呼び出されます。渡される引数は、ハンドラが到達を試みたURI (select()の呼出し時に使用されたもの)、ハンドラがアクセスを試みたプロキシのSocketAddress、プロキシへの接続を試みたときにスローされたIOExceptionの3つです。この情報を使用して、次の操作を実行します。このプロキシがリスト内に存在し、かつ3回以上失敗した場合は、リストから削除して以降は使用されないようにします。コードは次のようになります。

public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
        if (uri == null || sa == null || ioe == null) {
            throw new IllegalArgumentException("Arguments can't be null.");
        }
        InnerProxy p = proxies.get(sa); 
        if (p != null) {
                if (p.failed() >= 3)
                   proxies.remove(sa);
        } else {
                if (defsel != null)
                    defsel.connectFailed(uri, sa, ioe);
        }
}
とても簡単ですね。再び、引数の有効性を確認する必要があります(これも仕様に準拠するため)。ここで考慮するのはSocketAddressのみです。SocketAddressがリスト内のプロキシのいずれかであればそれを処理し、そうでなければデフォルト・セレクタとして再度保留します。

これで実装がほぼ完成しました。アプリケーション内でさらに実行することは、登録だけです。それを行いましょう。

public static void main(String[] args) {
        MyProxySelector ps = new MyProxySelector(ProxySelector.getDefault());
        ProxySelector.setDefault(ps);
        // rest of the application
}
わかりやすくするために、いくらか簡略化して記述しています。特に、例外のキャッチについてあまり記述していないことにお気づきでしょう。自分でコードを記述する際には、省略してある部分を補ってください。

基盤となるプラットフォームやコンテナ(Webブラウザなど)との統合性を改善するため、Java Plug-inとJava Webstartの両方でデフォルトのProxySelectorをカスタムのProxySelectorに置き換えていることに留意してください。このため、ProxySelectorを操作する際には、デフォルトのProxySelectorは、通常、基盤となるプラットフォームおよびJVM実装に固有のものであることを念頭に置く必要があります。このため、カスタムのものを提供する場合、前述の例で示したように以前のものへの参照を保持して、必要に応じて使用するのはよい方法です。

 

posted @ 2019-02-20 09:55  multitude  阅读(7938)  评论(0编辑  收藏  举报