20211013のJavaに関する記事は6件です。

Spring Boot 2.5でdata.sqlを使ってデータ初期化を行う際の注意点

前提 先日、以下を使って簡単なアプリを作ろうとしました。 Spring Boot 2.5 JPA H2 Database data.sql Item.java package com.example.domain; import java.io.Serializable; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @NoArgsConstructor @AllArgsConstructor @Entity @Table(name="items") public class Item implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; @Column(nullable = false) private String name; @Column(nullable = false) private Integer price; } data.sql INSERT INTO items(name, price) VALUES('りんご', 100); INSERT INTO items(name, price) VALUES('ばなな', 50); INSERT INTO items(name, price) VALUES('ぶどう', 200); 起こった事象 Item.javaからJPAで自動生成された「items」というテーブルにただただデータを突っ込んでいくだけのDMLなのですが、以下のエラーが発生しました。 Caused by: org.h2.jdbc.JdbcSQLSyntaxErrorException: テーブル "items" が見つかりません Table "items" not found; SQL statement: INSERT INTO items(name, price) VALUES('りんご', 100) [42102-200] at org.h2.message.DbException.getJdbcSQLException(DbException.java:453) ~[h2-1.4.200.jar:1.4.200] at org.h2.message.DbException.getJdbcSQLException(DbException.java:429) ~[h2-1.4.200.jar:1.4.200] at org.h2.message.DbException.get(DbException.java:205) ~[h2-1.4.200.jar:1.4.200] at org.h2.message.DbException.get(DbException.java:181) ~[h2-1.4.200.jar:1.4.200] at org.h2.command.Parser.readTableOrView(Parser.java:7628) ~[h2-1.4.200.jar:1.4.200] at org.h2.command.Parser.readTableOrView(Parser.java:7599) ~[h2-1.4.200.jar:1.4.200] at org.h2.command.Parser.parseInsert(Parser.java:1747) ~[h2-1.4.200.jar:1.4.200] at org.h2.command.Parser.parsePrepared(Parser.java:954) ~[h2-1.4.200.jar:1.4.200] at org.h2.command.Parser.parse(Parser.java:843) ~[h2-1.4.200.jar:1.4.200] at org.h2.command.Parser.parse(Parser.java:815) ~[h2-1.4.200.jar:1.4.200] at org.h2.command.Parser.prepareCommand(Parser.java:738) ~[h2-1.4.200.jar:1.4.200] at org.h2.engine.Session.prepareLocal(Session.java:657) ~[h2-1.4.200.jar:1.4.200] at org.h2.engine.Session.prepareCommand(Session.java:595) ~[h2-1.4.200.jar:1.4.200] at org.h2.jdbc.JdbcConnection.prepareCommand(JdbcConnection.java:1235) ~[h2-1.4.200.jar:1.4.200] at org.h2.jdbc.JdbcStatement.executeInternal(JdbcStatement.java:212) ~[h2-1.4.200.jar:1.4.200] at org.h2.jdbc.JdbcStatement.execute(JdbcStatement.java:201) ~[h2-1.4.200.jar:1.4.200] at net.sf.log4jdbc.StatementSpy.execute(StatementSpy.java:839) ~[log4jdbc-remix-0.2.7.jar:na] at com.zaxxer.hikari.pool.ProxyStatement.execute(ProxyStatement.java:94) ~[HikariCP-4.0.3.jar:na] at com.zaxxer.hikari.pool.HikariProxyStatement.execute(HikariProxyStatement.java) ~[HikariCP-4.0.3.jar:na] at org.springframework.jdbc.datasource.init.ScriptUtils.executeSqlScript(ScriptUtils.java:261) ~[spring-jdbc-5.3.10.jar:5.3.10] ... 28 common frames omitted INSERTするにも、「items」テーブルがないから無理よとのことだったのですが、JPAで自動生成してくれるはずなのに!と途方に暮れていたらどうやらSpring Boot 2.5からdata.sqlが読み込まれるタイミングが変わったようです。 By default, data.sql scripts are now run before Hibernate is initialized. This aligns the behavior of basic script-based initialization with that of Flyway and Liquibase. If you want to use data.sql to populate a schema created by Hibernate, set spring.jpa.defer-datasource-initialization to true. While mixing database initialization technologies is not recommended, this will also allow you to use a schema.sql script to build upon a Hibernate-created schema before it’s populated via data.sql. Spring Boot 2.5 Release Notes リリースノートには「Hibernateの初期化される前にdata.sql実行されるから気をつけや〜!」と書かれております。 解決方法 Hibernateの初期化前に実行されるように変更するには、application.propertiesにspring.jpa.defer-datasource-initialization=trueを追加するといいみたいです。 application.properties spring.jpa.defer-datasource-initialization=true よかったよかった〜!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

FileMaker ServerからJDBCでいろいろアクセスしてみる

前回の記事を書いた以降に、わかったことを書きます。 なお、今回は可能ならSpringのJDBC Template、必要に応じて生のJDBCを、JDBCドライバも前回拡張したものではなく素のドライバを使用します。 オブジェクトフィールドの読み書き 前回の調査で漏れてましたがオブジェクトフィールドを実際に読み書きしてみたら、うまく行かず諦めてました。 JDBCドライバのPreparedStatementのgetBlob/setBlob等のメソッドを呼び出すと例外が発生し、"This method is not yet implemented."と言われる。 ODBCとJDBCガイドに「CLOB、ARRAY および REF データ型を含む列の更新」は「FileMaker でサポートされません。」との記述を見て「できない」と早合点。 うまく動かないと、悪い方向に迷走しがちですよね。 しばらく経って、後述する問題でJDBCドライバ内部を見てたら、PreparedStatementのtype 1からあるgetBytes/setBytesは実装されてるではないですか! またSQLリファレンスガイドを見ると、GetAsやPutAsというのがあるそうじゃないですか! こいつ、、、動くぞ、、、ってことで試してみました。 オブジェクトフィールドの書き込み オブジェクトフィールドに書き込むデータをバイト配列にして、パラメータとして渡します。 SQL文でプレースホルダを? as 'ファイル名'とするとファイル名も反映されます。 val image = JdbcApplication::class.java.getResourceAsStream("/ideon-gage.png").readAllBytes() jdbcTemplate.update("""insert into "テーブル" ("テキスト", "数字", "日付", "時刻", "タイムスタンプ", "オブジェクト") values (?, ?, ?, ?, ?, ? as 'ideon-gage.png') """, "伝説巨神イデオン", 123, Date.valueOf("1980-5-8"), Time.valueOf("18:45:00"), Timestamp.valueOf("1981-1-30 19:15:00"), image) これを実行するとFileMaker側の画面に が表示されます。 オブジェクトファイル名は GetContainerAttribute ( オブジェクト ; "filename" )で計算した値です。 なおinsert into "テーブル" ("オブジェクト") values (PutAs(?, 'PNGf'))だとオブジェクトファイル名はUntitled.pngのようになります。PutAs(? as 'ファイル名', 'PNGf')やPutAs(?, 'PNGf') as 'ファイル名'は構文エラーになり、PutAsでファイル名を反映させる方法が判りませんでした。ファイルフォーマットなら自動判定されるようなので、PutAsを使うメリットは不明です。 そしてオブジェクトフィールドの更新もできます。 jdbcTemplate.update("""update "テーブル" set "オブジェクト" = ? as 'ideon-sword.png' where "テキスト" = '伝説巨神イデオン' """, image) オブジェクトフィールドの読み込み オブジェクトフィールド名をGetAsで括って指定します。 GetAsの第2引数はファイルのタイプだそうですが、横着してDEFAULTでもよさそうです。 val rows = jdbcTemplate.query("""select "テキスト", "数字", "日付", "時刻", "タイムスタンプ", GetAs("オブジェクト", DEFAULT) from "テーブル"""", ColumnMapRowMapper()) ちなみにSQL リファレンスガイドに従ってGetAs("オブジェクト", 'DEFAULT')だと構文エラーになります。 これを実行してIDEで取得したデータを見ると オブジェクトフィールドのデータがバイト配列で取得できています。 自動生成された値の取得 主キーの値を自動生成にする手法はよく使ってきました。 FileMakerにも、PostgreSQLなどと同様にシリアル番号を自動生成する機能があります。 しかし、MyBatisのgeneratedKeyで生成された値を取得することはできませんでした。 これもFileMaker® ODBC と JDBC ガイドガイドに「自動生成キーの検索」は「FileMaker でサポートされません。」との記述を見て「できない」と早合点。 そしてJDBCドライバ内部を見てたら、Statement#getGeneratedKeysは実装されてる訳です。 val statement = connection.prepareStatement("""insert into "テーブル" ("テキスト") values (?) """) statement.setString(1, "もうれつ") statement.executeUpdate() val mapper = ColumnMapRowMapper() var rowNum = 0 val generatedKeys = statement.generatedKeys while (generatedKeys.next()) { println(mapper.mapRow(generatedKeys, rowNum++)) } JDBC TemplateはPrepareStatementオブジェクトを隠蔽するので、生のJDBCとColumnMapRowMapperを組合せた。 すると {ROWID=2140} のような結果が得られました。 SQL リファレンスガイドによるとROWIDはレコード固有のID番号だそうなので、これを使ってselect 〜 from 〜 where ROWID = ?すれば、insertしたレコードを取得して、自動生成された値を取得することができますね。 コネクションが枯渇する Spring Bootで検証用のWebアプリケーションを作成して、しばらく評価していたところ、1周間に1回から、日に数回の頻度のバラツキで 11:04:14.077 DEBUG [l-1 housekeeper] c.z.h.p.HikariPool : HikariPool-1 - Add connection elided, waiting 0, queue 1 11:04:14.079 DEBUG [nnection closer] c.z.h.p.PoolBase : HikariPool-1 - Closing connection jp.co.supportas.filemaker.Connection@4dcdd962: (connection has passed maxLifetime) 11:04:21.737 DEBUG [l-1 housekeeper] c.z.h.p.HikariPool : HikariPool-1 - Add connection elided, waiting 0, queue 2 11:04:21.737 DEBUG [nnection closer] c.z.h.p.PoolBase : HikariPool-1 - Closing connection jp.co.supportas.filemaker.Connection@a264bce: (connection has passed maxLifetime) 11:04:24.386 DEBUG [l-1 housekeeper] c.z.h.p.HikariPool : HikariPool-1 - Add connection elided, waiting 0, queue 3 11:04:24.386 DEBUG [nnection closer] c.z.h.p.PoolBase : HikariPool-1 - Closing connection jp.co.supportas.filemaker.Connection@4b1b41f5: (connection has passed maxLifetime) 11:04:25.133 DEBUG [l-1 housekeeper] c.z.h.p.HikariPool : HikariPool-1 - Before cleanup stats (total=1, active=0, idle=1, waiting=0) 11:04:25.133 DEBUG [l-1 housekeeper] c.z.h.p.HikariPool : HikariPool-1 - After cleanup stats (total=1, active=0, idle=1, waiting=0) 11:04:25.133 DEBUG [l-1 housekeeper] c.z.h.p.HikariPool : HikariPool-1 - Fill pool skipped, pool is at sufficient level. 11:04:25.339 DEBUG [l-1 housekeeper] c.z.h.p.HikariPool : HikariPool-1 - Add connection elided, waiting 0, queue 4 11:04:25.339 DEBUG [nnection closer] c.z.h.p.PoolBase : HikariPool-1 - Closing connection jp.co.supportas.filemaker.Connection@7a1a6ebf: (connection has passed maxLifetime) 11:04:25.346 DEBUG [o-auto-1-exec-7] c.z.h.p.HikariPool : HikariPool-1 - Add connection elided, waiting 1, queue 5 11:04:55.136 DEBUG [l-1 housekeeper] c.z.h.p.HikariPool : HikariPool-1 - Before cleanup stats (total=0, active=0, idle=0, waiting=1) 11:04:55.136 DEBUG [l-1 housekeeper] c.z.h.p.HikariPool : HikariPool-1 - After cleanup stats (total=0, active=0, idle=0, waiting=1) 11:04:55.136 DEBUG [l-1 housekeeper] c.z.h.p.HikariPool : HikariPool-1 - Fill pool skipped, pool is at sufficient level. 11:04:55.351 DEBUG [o-auto-1-exec-7] c.z.h.p.HikariPool : HikariPool-1 - Timeout failure stats (total=0, active=0, idle=0, waiting=0) 11:04:55.374 ERROR [o-auto-1-exec-7] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException: ### Error querying database. Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Failed to obtain JDBC Connection; nested exception is java.sql.SQLTransientConnectionException: HikariPool-1 - Connection is not available, request timed out after 30006ms. ### The error may exist in jp/co/supportas/filemaker/mybatis/RecordMapper.java (best guess) ### The error may involve jp.co.supportas.filemaker.mybatis.RecordMapper.records ### The error occurred while executing a query ### Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Failed to obtain JDBC Connection; nested exception is java.sql.SQLTransientConnectionException: HikariPool-1 - Connection is not available, request timed out after 30006ms.] with root cause java.sql.SQLTransientConnectionException: HikariPool-1 - Connection is not available, request timed out after 30006ms. at com.zaxxer.hikari.pool.HikariPool.createTimeoutException(HikariPool.java:695) ~[HikariCP-3.4.5.jar:na] at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:197) ~[HikariCP-3.4.5.jar:na] at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:162) ~[HikariCP-3.4.5.jar:na] とコネクションプールに使用しているHikariでコネクションが取得できないとのエラーが発生し、アプリを再起動しないとFileMaker Serverにアクセスできない現象に遭遇しました。 各コネクションがmaxLifetimeの期間が過ぎ、再接続を行う中でAdd connection elided, waiting 0, queue 1が出力されてから約10秒の間にmaximumPoolSizeで指定していた5つ全て接続完了待ちになっていました。 スレッドダンプを取得すると "HikariPool-1 connection adder" #27 daemon prio=5 os_prio=0 cpu=8.87ms elapsed=39174.54s tid=0x00007f2eb000a000 nid=0x2b runnable [0x00007f2ee0e19000] java.lang.Thread.State: RUNNABLE at java.net.SocketInputStream.socketRead0(java.base@11.0.12/Native Method) at java.net.SocketInputStream.socketRead(java.base@11.0.12/SocketInputStream.java:115) at java.net.SocketInputStream.read(java.base@11.0.12/SocketInputStream.java:168) at java.net.SocketInputStream.read(java.base@11.0.12/SocketInputStream.java:140) at sun.security.ssl.SSLSocketInputRecord.read(java.base@11.0.12/SSLSocketInputRecord.java:478) at sun.security.ssl.SSLSocketInputRecord.readHeader(java.base@11.0.12/SSLSocketInputRecord.java:472) at sun.security.ssl.SSLSocketInputRecord.decode(java.base@11.0.12/SSLSocketInputRecord.java:160) at sun.security.ssl.SSLTransport.decode(java.base@11.0.12/SSLTransport.java:111) at sun.security.ssl.SSLSocketImpl.decode(java.base@11.0.12/SSLSocketImpl.java:1426) at sun.security.ssl.SSLSocketImpl.readHandshakeRecord(java.base@11.0.12/SSLSocketImpl.java:1336) at sun.security.ssl.SSLSocketImpl.startHandshake(java.base@11.0.12/SSLSocketImpl.java:450) at sun.security.ssl.SSLSocketImpl.startHandshake(java.base@11.0.12/SSLSocketImpl.java:421) at com.filemaker.jdbc.FMStream.establishSSL(Unknown Source) - locked <0x0000000718180fb8> (a com.filemaker.jdbc.FMStream) at com.filemaker.jdbc1.CommonJ1Connection.openConnection(Unknown Source) at com.filemaker.jdbc.Driver.connect(Unknown Source) at com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:138) JDBCドライバがSocketでFileMaker Serverに接続し、SSLのハンドシェイクで応答待ちのまま10時間以上経っているのが見つかりました。 Hikari + PostgreSQLの組合せで今まで気にしたことがなかったですが、FileMakerではソケット通信のタイムアウトを指定する必要があるようです。 jdbc:filemaker://${ホスト名}/${ファイル名}?&socketTimeout=10000 ここではタイムアウトを10秒にしていますが、FileMaker Serverへのselect等で応答にもっと時間がかかることが想定される場合は、延ばす必要があります。 ちなみにHikariのプロパティで接続タイムアウト(connectionTimeout)を指定できますが、JDBCドライバではこの値を使ってません。自分が使っているCentOSの環境でFileMaker Serverが起動していないと、2分程度接続待ちしてエラーが発生します。この待ち時間はOSの設定によるものです。 所感 まだプロトタイプの開発段階ですが、いろいろなことが見えてきました。 接続タイムアウトの件は、FMStreamというクラスのコンストラクタでSocket接続を行っているため、前回のようなアプローチで拡張することができません(コンストラクタは書き足せても上書きはできない)。仕方がないのでAspectJを使ってコンストラクタを上書きして、接続タイムアウトの指定が反映されるようにしました。 これから本番開発に進みそうで、FileMaker ServerとのJDBC接続にはまだ何かが潜んでいるかもしれませんが、できないことができるようになるのも開発の醍醐味ですよね。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Spring BootとApacheをAJPで接続する(secretRequired対応版)

ちょっと古いサイトを見て設定したらTomcatを9.0.34で追加されたSecretに 手順が対応していなくて起動しなかったのでメモ。 Caused by: java.lang.IllegalArgumentException: The AJP Connector is configured with secretRequired="true" but the secret attribute is either null or "". This combination is not valid. secretRequiredはデフォルトtrueになっているが、今回Apacheとはlocalhost通信するのでsetSecretRequired(false)してsecretを使わない設定に変えると起動するようになる。 @Bean public WebServerFactoryCustomizer<TomcatServletWebServerFactory> servletContainer() { Connector connector = new Connector("org.apache.coyote.ajp.AjpNio2Protocol"); connector.setPort(8009); AjpNio2Protocol protocol = (AjpNio2Protocol) connector.getProtocolHandler(); protocol.setSecretRequired(false); // secretを使わない return factory -> factory.addAdditionalTomcatConnectors(connector); } Secretを使いたい場合はTomcat側をsetSecretすると protocol.setSecret("mysecret"); Apache側の設定にも同じsecretを設定しないと接続できなくなり、よりセキュアになる。 ProxyPass / ajp://host.docker.internal:8009/ secret=mysecret
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

MacOSのProcessingでP3Dを使いたい!

きっかけ Processingで、以下のように書いた void setup() { size(600, 600, P3D); } void draw() { background(255, 0, 0); } そしたら、こんな感じの謎のwarningを吐かれ、正常に動作しなかった (ちなみに自分のwarningメッセージをメモしてなかったので、同じ状況である他の人のメッセージを拝借した) WARNING: NSWindow drag regions should only be invalidated on the Main Thread! This will throw an exception in the future. Called from ( 0 AppKit 0x00007fff3105d2e3 -[NSWindow(NSWindow_Theme) _postWindowNeedsToResetDragMarginsUnlessPostingDisabled] + 386 1 AppKit 0x00007fff3105a68c -[NSWindow _initContent:styleMask:backing:defer:contentView:] + 1488 2 AppKit 0x00007fff3105a0b6 -[NSWindow initWithContentRect:styleMask:backing:defer:] + 45 3 libnativewindow_macosx.jnilib 0x000000012d0c53fe Java_jogamp_nativewindow_macosx_OSXUtil_CreateNSWindow0 + 398 4 ??? 0x0000000114224a88 0x0 + 4632758920 ) 調べたところによると、どうやらJOGLのバージョンが問題らしい 解決までの手順 Applicationフォルダ内のProcessing.appを右クリックし、「パッケージの内容を表示」を選択する Contents/Java/core/library/を開くと、色々入っている この中の、4つの.jarファイル gluegen-rt.jar gluegen-rt-natives-macosx-universal.jar jogl-all.jar jogl-all-natives-macosx-universal.jar を新たなバージョンに置き換えたい 置き換えるファイルのダウンロード JOGL(https://jogamp.org/deployment/archive/rc/ )のv2.4.0を選ぶ jar/を選択するとファイルがたくさんあるので、そこから欲しい4つのファイル gluegen-rt.jar gluegen-rt-natives-macosx-universal.jar jogl-all.jar jogl-all-natives-macosx-universal.jar をダウンロードし、それぞれをContents/Java/core/library/に入っている既存のファイルと置き換えれば解決! 参考 Twitter(@ Hau_Kun さん): https://twitter.com/hau_kun/status/1332983730739105792 GitHubのissue: https://github.com/processing/processing/issues/5676
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Java PDFでビューアプレファレンス(ViewerPreference)の設定

はじめに ビューアプレファレンスとは、利用者がPDFファイルを閲覧する時に、自分の好みによって画面をどう表示させるかについては、様々なビューア表示設定できる機能です。例えば、タイトルを非表示にしたり、ウィンドウに合わせたりすることで、気持ちがよくなるでしょう。さあ、今回はSpire.PDF for Javaを利用してPDFでビューア表示設定を紹介していきたいと思います。 Spire.PDF for Javaとは? Spire.PDF for Javaは、開発者がJavaプラットホームでPDFの文書ファイルを迅速かつ高品質で作成・編集・変換・印刷するために設計された専門的なJava PDFライブラリです。 中には、商用版と無料版のFree.Spire.PDF for javaがあります。Spire.PDF for Javaは商用版ではありますが、基本的な機能を搭載しているので、無料試用で日常の仕事にはもう結構だと思います。 下準備 1.E-iceblueの公式サイトからSpire.PDF for Javaをダウンロードしてください。 2.IDEを起動して新規プロジェクトを作成してから、インストールされたファイルにある相応しいSpire.PDF.jarを参照に追加してください。 Mavenの側なら? もしMavenの利用者だったら、pom.xmlファイルに下記のようなコードを追加することで、JARを簡単にインポートできます。 <repositories> <repository> <id>com.e-iceblue</id> <url>http://repo.e-iceblue.cn/repository/maven-public/</url> </repository> </repositories> <dependencies> <dependency> <groupId>e-iceblue</groupId> <artifactId>spire.PDF</artifactId> <version>4.8.7</version> </dependency> </dependencies> サンプルコード import com.spire.pdf.*; public class ViewerPreference { public static void main(String[] args) { //PDFファイルをロードします。 PdfDocument pdf = new PdfDocument(); pdf.loadFromFile("Test.pdf"); //ウィンドウを真ん中に移動します。 pdf.getViewerPreferences().setCenterWindow(true); //タイトルを非表示にします。 pdf.getViewerPreferences().setDisplayTitle(false); //ウィンドウに合わせて自動調整します。 pdf.getViewerPreferences().setFitWindow(true); //メニューバーを非表示にします。 pdf.getViewerPreferences().setHideMenubar(true); //ツールバーを非表示にします。 pdf.getViewerPreferences().setHideToolbar(true); //片面で表示にします。 pdf.getViewerPreferences().setPageLayout(PdfPageLayout.Single_Page); //全画面表示にします。 pdf.getViewerPreferences().setPageMode(PdfPageMode.Full_Screen); //印刷時の拡大縮小を設定します。 pdf.getViewerPreferences().setPrintScaling(PrintScalingMode.App_Default); //保存します。 pdf.saveToFile("ViewerPreference.pdf"); pdf.close(); } } 実行結果 以下の関連記事もご参照 PDF文書変換機能:Convert PDF to Word in Java PDF文書の作成法:Create a PDF Document in Java PDF文書を結合:Merge PDF Documents in Java 最後に ここまで読んでくださってありがとうございます!もしSpire.PDF for Javaを利用している時にご不明なところがございましたら、ぜひご連絡ください
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

module-info.javaの書き方

module-info.javaの書き方 問題点と解決策 モジュールの名前に、パッケージと同じ(またはほぼ同じ)名前を付けた例が散見された。 間違いではないが非常に紛らわしく混乱した。 どこにどのレベルの名称を書くのか、構文レベルで事前把握しておくが吉。 ModuleDeclaration {Annotation} [open] module Identifier {. Identifier} { {ModuleDirective} } ModuleDirective requires {RequiresModifier} ModuleName ; exports PackageName [to ModuleName {, ModuleName}] ; opens PackageName [to ModuleName {, ModuleName}] ; uses TypeName ; provides TypeName with TypeName {, TypeName} ; RequiresModifier (one of) transitive static 提供側 単位 利用側 単位 exports パッケージ requires モジュール opens パッケージ - provides タイプ uses タイプ
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む