- 投稿日:2020-01-23T23:36:23+09:00
ArrayListのnewとclear()では挙動が異なる
地味にハマってしまったので。
はじめに
Javaでリストを扱う時、よくArrayListをnewして使うと思います。
hoge.javaList<String> list = new ArrayList<>();そのArrayListを使い回したいという時にGoogle先生に聞いてみると、
- newする
- ArrayList.clear()メソッドを使う
の2通りをオススメされると思います。そして、両者の違いについては特に触れられず、強いて言えば「実行速度が違うよ」と言っていくらかQiitaの記事がヒットすると思います。
しかし、実際のところは速度だけではなく、挙動にも異なる点があります。
newとclear()の違い
1次元リストから要素を2つずつ取り出して2次元リストを作る場合を想定します。
以下にサンプルコードを示します。
まずはnewした場合からです。sample.javaimport java.util.ArrayList; import java.util.Arrays; import java.util.List; public class TestArraylist { public static void main(String args[]) { // 出力用の2次元リスト List<ArrayList<String>> sampleLists = new ArrayList<>(); // sampleListsに要素を追加するための一時的なリスト ArrayList<String> listA = new ArrayList<>(); // 適当なサンプル List<String> listB = Arrays.asList("AA", "BB", "CC", "DD", "EE", "FF"); // 要素数が2のリストを作ってsampleListsに追加する for (int i = 0; i < listB.size(); i++) { if (i % 2 == 0) { listA.add(listB.get(i)); continue; } sampleLists.add(listA); listA.add(listB.get(i)); // new前の確認 System.out.println(sampleLists); // 一時的なリストをnewする listA = new ArrayList<>(); // new後の確認 System.out.println(sampleLists); } } }これを実行すると・・・
[[AA, BB]] [[AA, BB]] [[AA, BB], [CC, DD]] [[AA, BB], [CC, DD]] [[AA, BB], [CC, DD], [EE, FF]] [[AA, BB], [CC, DD], [EE, FF]]ちゃんと目的通りのリストが出来上がっていることがわかります。
一方、newの代わりにclearメソッドを使ったとします。上記サンプルの
listA = new ArrayList<>();
をclearメソッドを用いたコードlistA.clear();
に書き換えた時の出力は・・・[[AA, BB]] [[]] [[CC, DD], [CC, DD]] [[], []] [[EE, FF], [EE, FF], [EE, FF]] [[], [], []]このように、確かに追加したはずの要素がclearメソッドを呼び出すたびに要素0のリストとなってしまい、次にclearメソッドを呼び出すまで新たに追加した要素がすべてのリストに追加されてしまいます。
おわりに
ググってみると配列の要素を消すのはどちらを使ってもよいというページがたくさん出てくるので油断してしまいますが、実際は上記のような異なる挙動をしているので注意が必要です。
- 投稿日:2020-01-23T21:38:29+09:00
匿名クラス(Stream api 導入の為に)
目的
Stream apiを学ぶ上では匿名クラスの設計及び実装の考え方の理解が不可欠だと感じた.
メソッド参照,関数型インターフェース,ラムダ式,ジェネリクスを用いた設計(インスタンススコープ及びメソッドスコープ,PECS原則に基づいた設計)学習前の入口となる匿名内部クラスについて学ぶ.入れ子クラス
ブロック({})内で宣言されたクラスを「入れ子クラス」(Nasted Class)と呼ぶ
NastedClazzSample.javapublic class NastedClazzSample { static class StaticClazz {} //static member class class MemberClazz {} //menber class (inner class) void method() { class LocalClazz {} //local class (inner class) } }メンバー・クラスとして宣言された入れ子クラスにはアクセス修飾子を付けることが出来る
NastedClazzSample.javapublic class NastedClazzSample { static protected class StaticClazz {} // OK private class MemberClazz {} // OK void method() { public class LocalClazz {} // NG } }Error.javaローカル・クラス LocalClazz の修飾子が正しくありません。abstract または final だけが許可されています NastedClassSample.java /...(path) 行 11 Java 問題何故か.
ローカル・クラスのスコープはローカル変数と同様
例ではmethod()を介してLocalClazzにアクセス
つまりmethod()のアクセッサーと同様になる為記述が出来ない仕様になっている内部クラス
上記の通り,staticメンバー・クラス以外の入れ子クラス(非staticメンバー・クラス)を内部クラスと言う
内部クラスの特徴
- ①staticメンバを所持できない
- ②外部クラスで定義したインスタンス変数にアクセス可能
①staticメンバを所持できない
非staticなメンバはインスタンスと関連
ローカル変数はスタック領域で管理され,対象クラスがインスタンス化される時にヒープ領域に配置されるInnerClazzSample.javapublic class InnerClazzSample { public class Inner { String foo; } public static void main(String... args) { Inner inner = new InnerClazzSample().new Inner(); inner.foo = "bar"; System.out.println(inner.foo); } }実行結果.javabar
インスタンス化せずに使用した場合
InnerClazzSample.javapublic class InnerClazzSample { public class Inner { String foo; } public static void main(String... args) { Inner.foo = "bar"; } }Error.java非 static フィールド Inner.foo を static 参照できません InnerClazzSample.java /~(path) 行 9 Java 問題これはどういうことか.
まず,JVMはアプリケーションに使用されるクラスを読み込みメモリ上に展開する
この時staticメンバは非staticメンバとは異なるメモリ領域(クラス領域)に配置される
JVM起動時ブートストラップ・クラス・ローダーが呼び出されるが,すべてのクラスを読み込むのではなくアプリケーションからのクラス参照があった時にロードされる
また,-Xbootclasspathを変更するとロードできるクラス・セットの範囲を変更できるstaticメンバーはクラスと関連している.
メモリに展開されるタイミングはJVMの実装に左右される.②外部クラスで定義したインスタンス変数にアクセス可能
Outer.javapublic class Outer { private int foo; public class Inner { public int bar; private void method() { this.foo = Outer.this.bar; // OK } } private void method() { this.foo = bar; // NG } }また,非static内部クラスのインスタンス優先順位は
- 呼び出し元ローカル変数
- 内部クラスのインスタンス変数
- 外部クラスのインスタンス変数
Outer.javapublic class Outer { String foo = "outerclass-a"; String bar = "outerclass-b"; String baz = "outerclass-c"; public class Inner { String foo = "innerclass-a"; String bar = "innerclass-b"; public void thread() { String foo = "thread-c"; System.out.println(foo); System.out.println(bar); System.out.println(baz); System.out.println(this.foo); System.out.println(Outer.this.foo); } } public static void main(String... args) { new Outer().new Inner().thread(); } }実行結果.javathread-c innerclass-b outerclass-c innerclass-a outerclass-a匿名クラス(無名クラス,匿名内部クラス)
入れ子クラスを利用する際,名前を持たないクラスとして利用することができる
そのような入れ子クラスを「匿名クラス」という主に以下の目的の為に使用される
- ① 処理の再利用性が低くその場でのみ必要となる場合
- ② 処理コードが短いがカスタマイズが豊富にある場合
匿名クラス-構文1.textnew 型名() { 抽象メソッドの実装 }匿名クラス-構文2.text型名 インスタンス名 = new 型名() { 抽象メソッドの実装 }まずは匿名クラスを利用せず,インターフェースの抽象メソッドの実装を行う.
Sample.javainterface FooInterface { //interface void method(); //abstract method } class BarClazz implements FooInterface { //implemented class public void method() { System.out.println("Did Implementation"); //implemented abstract method } } public class Sample{ //runner public static void main(String... args) { new BarClazz().method(); //new instance } }匿名クラスを利用して抽象メソッドの実装を行うと
Sample.javainterface FooInterface { void method(); } //nothing inplemented class public class Sample { public static void main(String... args) { FooInterface foo = new FooInterface() { //anonymous class public void method() { System.out.println("Did Implementation"); //implemented abstract method } }; //semi-coron foo.method(); //instance.method() } }実行結果.javaDid Implementation実用例を考えてみる
① 処理の再利用性が低くその場でのみ必要となる場合
顧客情報を管理するクラスを定義
ManagementClientInformation.javapublic class ManagementClientInformation { public static class Client { private int id; private String name; Client(int id, String name) { this.id = id; this.name = name; } public int getId() { return this.id; } public String getName() { return this.name; } } public static void main(String... args) { List<Client> clientList = new ArrayList<>(); clientList.add(new Client(1, "matsuda")); clientList.add(new Client(2, "tanaka")); clientList.add(new Client(3, "neko")); clientList.stream() .forEach(x -> System.out.println(x.getId() + " " + x.getName())); } }実行結果.java1 matsuda 2 tanaka 3 neko
匿名クラスの実用例
以下の事象について考えてみる
あなたはシステム・エンジニアです
保守・改修中に顧客からの要望がありました
ある画面について顧客idの逆順に並び替えてほしいとの要望が
尚,システム影響懸念・肥大化の為Clientクラスに手を加えることは禁止とする⇒匿名クラスを使用
ManagementClientInformation.javapublic class ManagementClientInformation { public static class Client { private int id; private String name; Client(int id, String name) { this.id = id; this.name = name; } public int getId() { return this.id; } public String getName() { return this.name; } } public static void main(String... args) { List<Client> clientList = new ArrayList<>(); clientList.add(new Client(1, "matsuda")); clientList.add(new Client(2, "tanaka")); clientList.add(new Client(3, "neko")); Collections.sort(clientList, new Comparator<Client>() { public int compare(Client c1, Client c2) { return Integer.compare(c2.getId(), c1.getId()); } }); clientList.stream() .forEach(x -> System.out.println(x.getId() + " " + x.getName())); } }実行結果.java3 neko 2 tanaka 1 matsuda終わりに
初めてStream apiに触れた時,内容が全く分かりませんでした.
もんもんしているうちにメソッド参照⇒ラムダ式⇒ジェネリクス設計と逆順に学習していくうちに,少しずつ理解が深まっていきました.
基礎的な部分をしっかりと理解した上で応用したいと思います.
- 投稿日:2020-01-23T21:38:29+09:00
匿名クラス(stream api 導入を目指して)
目的
stream apiを学ぶ上では匿名クラスの設計及び実装の考え方の理解が不可欠だと感じた.
メソッド参照,関数型インターフェース,ラムダ式,ジェネリクスを用いた設計(インスタンススコープ及びメソッドスコープ,PECS原則に基づいた設計)学習前の入口となる匿名内部クラスについて学ぶ.入れ子クラス
ブロック({})内で宣言されたクラスを「入れ子クラス」(Nasted Class)と呼ぶ
NastedClazzSample.javapublic class NastedClazzSample { static class StaticClazz {} //static member class class MemberClazz {} //menber class (inner class) void method() { class LocalClazz {} //local class (inner class) } }メンバー・クラスとして宣言された入れ子クラスにはアクセス修飾子を付けることが出来る
NastedClazzSample.javapublic class NastedClazzSample { static protected class StaticClazz {} // OK private class MemberClazz {} // OK void method() { public class LocalClazz {} // NG } }Error.javaローカル・クラス LocalClazz の修飾子が正しくありません。abstract または final だけが許可されています NastedClassSample.java /...(path) 行 11 Java 問題何故か.
ローカル・クラスのスコープはローカル変数と同様
例ではmethod()を介してLocalClazzにアクセス
つまりmethod()のアクセッサーと同様になる為記述が出来ない仕様になっている内部クラス
上記の通り,staticメンバー・クラス以外の入れ子クラス(非staticメンバー・クラス)を内部クラスと言う
内部クラスの特徴
- ①staticメンバを所持できない
- ②外部クラスで定義したインスタンス変数にアクセス可能
①staticメンバを所持できない
非staticなメンバはインスタンスと関連
ローカル変数はスタック領域で管理され,対象クラスがインスタンス化される時にヒープ領域に配置されるInnerClazzSample.javapublic class InnerClazzSample { public class Inner { String foo; } public static void main(String... args) { Inner inner = new InnerClazzSample().new Inner(); inner.foo = "bar"; System.out.println(inner.foo); } }実行結果.javabar
インスタンス化せずに使用した場合
InnerClazzSample.javapublic class InnerClazzSample { public class Inner { String foo; } public static void main(String... args) { Inner.foo = "bar"; } }Error.java非 static フィールド Inner.foo を static 参照できません InnerClazzSample.java /~(path) 行 9 Java 問題これはどういうことか.
まず,JVMはアプリケーションに使用されるクラスを読み込みメモリ上に展開する
この時staticメンバは非staticメンバとは異なるメモリ領域(クラス領域)に配置される
JVM起動時ブートストラップ・クラス・ローダーが呼び出されるが,すべてのクラスを読み込むのではなくアプリケーションからのクラス参照があった時にロードされる
また,-Xbootclasspathを変更するとロードできるクラス・セットの範囲を変更できるstaticメンバーはクラスと関連している.
メモリに展開されるタイミングはJVMの実装に左右される.②外部クラスで定義したインスタンス変数にアクセス可能
Outer.javapublic class Outer { private int foo; public class Inner { public int bar; private void method() { this.foo = Outer.this.bar; // OK } } private void method() { this.foo = bar; // NG } }また,非static内部クラスのインスタンス優先順位は
- 呼び出し元ローカル変数
- 内部クラスのインスタンス変数
- 外部クラスのインスタンス変数
Outer.javapublic class Outer { String foo = "outerclass-a"; String bar = "outerclass-b"; String baz = "outerclass-c"; public class Inner { String foo = "innerclass-a"; String bar = "innerclass-b"; public void thread() { String foo = "thread-c"; System.out.println(foo); System.out.println(bar); System.out.println(baz); System.out.println(this.foo); System.out.println(Outer.this.foo); } } public static void main(String... args) { new Outer().new Inner().thread(); } }実行結果.javathread-c innerclass-b outerclass-c innerclass-a outerclass-a匿名クラス(無名クラス,匿名内部クラス)
入れ子クラスを利用する際,名前を持たないクラスとして利用することができる
そのような入れ子クラスを「匿名クラス」という主に以下の目的の為に使用される
- ① 処理の再利用性が低くその場でのみ必要となる場合
- ② 処理コードが短いがカスタマイズが豊富にある場合
匿名クラス-構文1.textnew 型名() { 抽象メソッドの実装 }匿名クラス-構文2.text型名 インスタンス名 = new 型名() { 抽象メソッドの実装 }まずは匿名クラスを利用せず,インターフェースの抽象メソッドの実装を行う.
Sample.javainterface FooInterface { //interface void method(); //abstract method } class BarClazz implements FooInterface { //implemented class public void method() { System.out.println("Did Implementation"); //implemented abstract method } } public class Sample{ //runner public static void main(String... args) { new BarClazz().method(); //new instance } }匿名クラスを利用して抽象メソッドの実装を行うと
Sample.javainterface FooInterface { void method(); } //nothing inplemented class public class Sample { public static void main(String... args) { FooInterface foo = new FooInterface() { //anonymous class public void method() { System.out.println("Did Implementation"); //implemented abstract method } }; //semi-coron foo.method(); //instance.method() } }実行結果.javaDid Implementation匿名クラスの実用例
匿名クラスの実用例を考えてみる
① 処理の再利用性が低くその場でのみ必要となる場合
顧客情報を管理するクラスを定義
ManagementClientInformation.javapublic class ManagementClientInformation { public static class Client { private int id; private String name; Client(int id, String name) { this.id = id; this.name = name; } public int getId() { return this.id; } public String getName() { return this.name; } } public static void main(String... args) { List<Client> clientList = new ArrayList<>(); clientList.add(new Client(1, "matsuda")); clientList.add(new Client(2, "tanaka")); clientList.add(new Client(3, "neko")); clientList.stream() .forEach(x -> System.out.println(x.getId() + " " + x.getName())); } }実行結果.java1 matsuda 2 tanaka 3 neko
以下の事象について考えてみる
あなたはシステム・エンジニアです
保守・改修中に顧客からの要望がありました
ある画面について顧客idの逆順に並び替えてほしいとの要望が
尚,システム影響懸念・肥大化の為Clientクラスに手を加えることは禁止とする⇒匿名クラスを使用
ManagementClientInformation.javapublic class ManagementClientInformation { public static class Client { private int id; private String name; Client(int id, String name) { this.id = id; this.name = name; } public int getId() { return this.id; } public String getName() { return this.name; } } public static void main(String... args) { List<Client> clientList = new ArrayList<>(); clientList.add(new Client(1, "matsuda")); clientList.add(new Client(2, "tanaka")); clientList.add(new Client(3, "neko")); Collections.sort(clientList, new Comparator<Client>() { public int compare(Client c1, Client c2) { return Integer.compare(c2.getId(), c1.getId()); } }); clientList.stream() .forEach(x -> System.out.println(x.getId() + " " + x.getName())); } }実行結果.java3 neko 2 tanaka 1 matsuda終わりに
初めてStream apiに触れた時,内容が全く分かりませんでした.
もんもんしているうちにメソッド参照⇒ラムダ式⇒ジェネリクス設計と逆順に学習していくうちに,少しずつ理解が深まっていきました.
基礎的な部分をしっかりと理解した上で応用したいと思います.
- 投稿日:2020-01-23T21:26:33+09:00
Android StudioでJavaのversionで躓いたのでまとめる
はじめに
ここ数ヶ月、実務インターンをするようになってから、インターン先が変わるたびに環境構築をしているのですが、適当にやっているせいでJDKのバージョンが原因なエラーに幾度となく躓いてきました。
毎回調べて、
ああ...JDKのversionが原因か。で、どうするんだっけ。と調べるのが不毛なため備忘録として記事にしました。
この記事は自分のユースケースにおける対処法なため、他の方には当てはまらないかもしれませんが、随時updateしていければと思います。
躓いた具体的なユースケース
まず、
% java -vesionとターミナルで打つと、ご自身のJDKのバージョンがいくつか確認できるのでご参考までに。
How to resolve java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException
この時の自分のJDKは
java 12.0.2 2019-07-16でした。
Java 9で「モジュール」の機能が追加された際に、標準ライブラリからいくつかのモジュールが非推奨となりました。
Java 9以降でJAXBを使用するには、外部JARが必要 #49
この機能追加により、
JAXB
なるモジュールも非推奨となり、上記のエラーが出るようになったみたいです。Java 8以下では再現せず、Java 9以上の環境で再現するとのこと。解決策
Java 9, 10であれば、実行時のオプションで
--add-modules java.xml.bindを指定してあげれば一旦は凌げます。
毎回オプションを追加するのが面倒臭いとか、そもそもJava 10より上のJDK環境の場合は以下の強引な解決策を試してみてください。
強引な解決策
Android StudioのJDKを使用する
まず、知っておくべきことが
Android Studioは、Javaの実行環境そのものを提供してくれています。
そのため、Android Studioがapkを作る時に使っているJavaのPathのメモ の記事でも言及されているように、普段我々がGUIでAndroid StudioでRunした際には、 Android StudioのJava環境でapplication moduleをビルドし、apkを作成しています。
逆に、
Android Studioの下部のTerrminalや、MacやWindowsのアプリケーションとして開いているターミナルでは、OSの実行環境が使用されます。
したがって、GUIではちゃんとビルドできるのに、
% ./gradlew assemble等のコマンドでビルドしようとすると失敗するのはそのせいです。
GUIでビルドができるなら、Android Studioの実行環境でTerminalも動かしてしまえば良いということで、
.bashrc
や.zshrc
に環境変数のPathを通してあげればよさそうです。% export PATH=$PATH:/Applications/"Android Studio.app"/Contents/jre/jdk/Contents/Home/bin % export JAVA_HOME=/Applications/"Android Studio.app"/Contents/jre/jdk/Contents/Homeすると、Android Studioのバージョンにもよりますが、適切なJavaのバージョンでの実行環境でTerminalからも起動できるようになります。
ちなみにAndroid Studio 3.6-RC1では
1.8.0_212-release
でした。異なるversionのJDKをダウンロードする
異なるversionといえども上記のAndroid Studioの実行環境から、
1.8.0
系が良さそうです。Java 8ですね。Oracle社HP からinstallします。
installの手順は沢山記事があるのでそちらを参考願います。
最近からサインインが必要になったためOracleのアカウントを作らないと行けなかったはずです。
また、表示名が
Java SE Development Kit 8u241
みたいな形で、8u***
となっているかもしれませんが、それが1.8.0
系です。installができたら、自分がインストールしたJDKのバージョン一覧をみてみましょう。
% /usr/libexec/java_home -V確認できたら、そのJDK versionを環境変数に設定します。
% export JAVA_HOME=`/usr/libexec/java_home -v {java_version}
{java_version}
には先ほど確認したversionを入れましょう。あとは無事設定できてるかを
% java -versionで確認して、ちゃんと設定できていたらOKです。
参考文献
https://qiita.com/mas0061/items/2fe9333f045800d00b5c
- 投稿日:2020-01-23T16:58:49+09:00
[java] HttpURLConnection POST
使い方
以下の通りである。
try { //コネクション URL url = new URL("URL"); httpURLConnection_ = (HttpURLConnection) url.openConnection(); httpURLConnection_.setDoOutput(true); //POST可能にする httpURLConnection_.connect(); //送信したいデータ String param = "param1=1"; //リクエストボディに送信したいデータを書き込む OutputStream os = httpURLConnection_.getOutputStream(); BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os, "UTF-8")); writer.write(param); writer.flush(); writer.close(); //クローズ処理 os.close(); } catch (IOException e) { e.printStackTrace(); }サーバ側で値を取得するときは以下のようにする。(サーバはHttpServletを継承したjavaファイル)
public class Server extends HttpServlet{ public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException{ request.getParameter("param1"); } }感想
見かけは難しく見えたが、予想より簡単に実装できた。結構使うらしいので覚えておきたい。
参考URL
- 投稿日:2020-01-23T15:23:55+09:00
[Java]Jerjey(Jax-rs)でmultipart/form-dataの受け取り
帰ったら書きます。
- 投稿日:2020-01-23T15:23:55+09:00
Jerjey(Jax-rs)でmultipart/form-dataの受け取り
はじめに
[Android]端末からサーバへ画像のアップロードにて受け取る側がServletになっていました。本当はJerjeyで受け取りたかったのですが、勝手がわからず妥協していました。今回その方法がなんとなくわかったので紹介させていただきます。
環境
- Eclipse 4.13.0
- java 11
- Jerjey 2.29
- 入れ方はこちら
multipart/form-dataを対応させる
以下をダウンロードしてください。
この2つにパスを通します。
Applicationクラスを作成
このままでは、multipart/form-dataがまだ使えないので対応させたいプロジェクトのパッケージ内に
[ApplicationConfig.java]を作成し、以下をコピペしてください。ApplicationConfig.javapackage com.Sample.api; import org.glassfish.jersey.media.multipart.MultiPartFeature; import org.glassfish.jersey.server.ResourceConfig; public class ApplicationConfig extends ResourceConfig { public ApplicationConfig() { packages("pakage").register(MultiPartFeature.class); } }"pakage"には使用するパッケージ名を各自入れてください。
次に[Web.xml]を変更します。Web.xml<servlet> <servlet-name>jersey-app</servlet-name> <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class> <init-param> <param-name>javax.ws.rs.Application</param-name> <!--ここの"pakage.ApplicationConfig"は上で作成したクラスのパスです。--> <param-value>pakage.ApplicationConfig</param-value> </init-param> <init-param> <param-name>jersey.config.server.provider.classnames</param-name> <param-value>org.glassfish.jersey.media.multipart.MultiPartFeature</param-value> </init-param> </servlet>これでmultipart/form-data対応が完了しました。
実装
以下クラスを作成してください。
Upload.javaimport static java.nio.file.StandardCopyOption.*; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Paths; import javax.ws.rs.Consumes; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; import org.glassfish.jersey.media.multipart.FormDataContentDisposition; import org.glassfish.jersey.media.multipart.FormDataParam; @Path("/Upload") public class Upload { @POST @Path("/File) @Consumes(MediaType.MULTIPART_FORM_DATA) public String upload(@FormDataParam(value = "file") InputStream fileStream, @FormDataParam(value = "file") FormDataContentDisposition fileDisposition) { try { Files.copy(fileStream, Paths.get("保存先フォルダ",fileDisposition.getFileName()), REPLACE_EXISTING); return "END"; } catch (IOException e) { throw new WebApplicationException(e, 500); } } }これでhttp://localhost/XX/api/Upload/File
(XXはパッケージ名)にファイルを送れば保存されるはずです。参考
JAX-RSでファイルアップロード!
http://blog.yumix.net/entry/2012/12/17/002515Jerseyの設定1(web.xmlとかApplicationクラスとか)
https://edgegram.hatenablog.jp/entry/2015/11/25/160433java - JAX-RS HTTPマルチパートリクエスト
https://tutorialmore.com/questions-1757453.htm
- 投稿日:2020-01-23T09:25:23+09:00
spring-batchでreaderの値に応じてwriterを動的に切り替える
たとえば、readerが整数を読み込み、偶数・奇数で異なるwriterを呼び出す、という例を考える。
import org.springframework.batch.core.Job; import org.springframework.batch.core.Step; import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing; import org.springframework.batch.core.configuration.annotation.JobBuilderFactory; import org.springframework.batch.core.configuration.annotation.StepBuilderFactory; import org.springframework.batch.item.ItemReader; import org.springframework.batch.item.ItemWriter; import org.springframework.batch.item.support.ClassifierCompositeItemWriter; import org.springframework.batch.item.support.ListItemReader; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.context.annotation.Bean; @SpringBootApplication @EnableBatchProcessing public class App { @Bean public Job job(JobBuilderFactory jobs, StepBuilderFactory steps) { return jobs .get("job") .start(step1(steps)) .build(); } ItemWriter<Integer> evenWriter() { return items -> items.forEach(i -> System.out.println("偶数" + i)); } ItemWriter<Integer> oddWriter() { return items -> items.forEach(i -> System.out.println("奇数" + i)); } public Step step1(StepBuilderFactory stepBuilderFactory) { ItemReader<Integer> reader = new ListItemReader<Integer>(IntStream.range(0, 100).boxed().collect(Collectors.toList())); ClassifierCompositeItemWriter<Integer> compositeWriter = new ClassifierCompositeItemWriter<Integer>(); compositeWriter.setClassifier(number -> { if (number % 2 == 0) { return evenWriter(); } return oddWriter(); }); return stepBuilderFactory .get("step1") .<Integer, Integer>chunk(4) .reader(reader) .writer(compositeWriter) .build(); } public static void main(String[] args) { new SpringApplicationBuilder(App.class).run(args); } }
ClassifierCompositeItemWriter
は自身がwriterで、指定されたルーティングパターンを基に呼び出すwriterを切り替える。これを実行すると以下のようになる。
偶数0 偶数2 奇数1 奇数3 ...
chunk(4)
なので、4件ごとに偶数・奇数writerがそれぞれ1度ずつ呼び出されている。