- 投稿日:2020-08-05T23:37:08+09:00
[Java]MinecraftのModを作成しよう 1.14.4【5. 防具の追加】
(この記事は一連の解説記事の一つになります)
先頭記事:入門編
前の記事:4. ツールの追加
次の記事:6. レシピの追加防具の追加
4. ツールの追加では剣を含むツール類の追加をしました。武器を手にした次に欲しいのはやはり防具ですね!今回は防具類の追加をしていきましょう。
ItemList.java//... public class ItemList { public static Item ExampleHelmet = new ArmorItem(ArmorMaterial.IRON, EquipmentSlotType.HEAD, new Item.Properties().group(ExampleItemGroup.DEFAULT)) .setRegistryName(new ResourceLocation(ExampleMod.MOD_ID, "example_helmet")); public static Item ExampleChestplate = new ArmorItem(ArmorMaterial.IRON, EquipmentSlotType.CHEST, new Item.Properties().group(ExampleItemGroup.DEFAULT)) .setRegistryName(new ResourceLocation(ExampleMod.MOD_ID, "example_chestplate")); public static Item ExampleLeggings = new ArmorItem(ArmorMaterial.IRON, EquipmentSlotType.LEGS, new Item.Properties().group(ExampleItemGroup.DEFAULT)) .setRegistryName(new ResourceLocation(ExampleMod.MOD_ID, "example_leggings")); public static Item ExampleBoots = new ArmorItem(ArmorMaterial.IRON, EquipmentSlotType.FEET, new Item.Properties().group(ExampleItemGroup.DEFAULT)) .setRegistryName(new ResourceLocation(ExampleMod.MOD_ID, "example_boots")); @SubscribeEvent public static void registerItems(RegistryEvent.Register<Item> event) { event.getRegistry().registerAll( ExampleHelmet, ExampleChestplate, ExampleLeggings, ExampleBoots ); } }今回は別途クラスを作らず、
ItemList
でArmorItem
クラスを直接宣言します。
引数は順に、材質、スロット箇所、アイテムプロパティです。
細かい設定をしていきます。
\src\main\resources\assets\example_mod ├ blockstates ├ lang │ └ en_us.json │ └ ja_jp.json ├ models │ ├ block │ └ item │ ├ example_helmet.json │ ├ example_chestplate.json │ ├ example_leggings.json │ └ example_boots.json └ textures ├ blocks └ items ├ example_helmet.png ├ example_chestplate.png ├ example_leggings.png └ example_boots.pngen_us.json{ "item.example_mod.example_helmet": "Example Helmet", "item.example_mod.example_chestplate": "Example Chestplate", "item.example_mod.example_leggings": "Example Leggings", "item.example_mod.example_boots": "Example Boots" }ja_jp.json{ "item.example_mod.example_helmet": "例のヘルメット", "item.example_mod.example_chestplate": "例のチェストプレート", "item.example_mod.example_leggings": "例のレギンス", "item.example_mod.example_boots": "例のブーツ" }example_helmet.json{ "parent": "item/generated", "textures": { "layer0": "example_mod:items/example_helmet" } }example_chestplate.json{ "parent": "item/generated", "textures": { "layer0": "example_mod:items/example_chestplate" } }example_leggings.json{ "parent": "item/generated", "textures": { "layer0": "example_mod:items/example_leggings" } }example_boots.json{ "parent": "item/generated", "textures": { "layer0": "example_mod:items/example_boots" } }ゲームを起動してみます。
防具の追加ができ…てません!!
着てみたら見た目が鉄防具になってしまいました(それ以外にもステータスや修理材料など実はいろいろと鉄防具っぽいことになっています)。これはArmorItem
の材質としてArmorMaterial.IRON
を渡しているせいです。
ツールの時にも行ったように、新しい独自のマテリアルを定義しましょう。
\src\main\java\jp\koteko\example_mod\ ├ items │ └ ExampleArmorMaterial.java ├ lists ├ ExampleItemGroup.java └ ExampleMod.javaExampleArmorMaterial.javapackage jp.koteko.example_mod.items; import jp.koteko.example_mod.ExampleMod; import jp.koteko.example_mod.lists.ItemList; import net.minecraft.inventory.EquipmentSlotType; import net.minecraft.item.IArmorMaterial; import net.minecraft.item.crafting.Ingredient; import net.minecraft.util.LazyLoadBase; import net.minecraft.util.SoundEvent; import net.minecraft.util.SoundEvents; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import java.util.function.Supplier; public enum ExampleArmorMaterial implements IArmorMaterial { EXAMPLE("example", 66, new int[]{3, 6, 8, 3}, 30, SoundEvents.ITEM_ARMOR_EQUIP_DIAMOND, 3.0F, () -> { return Ingredient.fromItems(ItemList.ExampleIngot); }); private static final int[] MAX_DAMAGE_ARRAY = new int[]{13, 15, 16, 11}; private final String name; private final int maxDamageFactor; private final int[] damageReductionAmountArray; private final int enchantability; private final SoundEvent soundEvent; private final float toughness; private final LazyLoadBase<Ingredient> repairMaterial; private ExampleArmorMaterial(String nameIn, int maxDamageFactorIn, int[] damageReductionAmountsIn, int enchantabilityIn, SoundEvent equipSoundIn, float toughnessIn, Supplier<Ingredient> repairMaterialSupplier) { this.name = nameIn; this.maxDamageFactor = maxDamageFactorIn; this.damageReductionAmountArray = damageReductionAmountsIn; this.enchantability = enchantabilityIn; this.soundEvent = equipSoundIn; this.toughness = toughnessIn; this.repairMaterial = new LazyLoadBase<>(repairMaterialSupplier); } public int getDurability(EquipmentSlotType slotIn) { return MAX_DAMAGE_ARRAY[slotIn.getIndex()] * this.maxDamageFactor; } public int getDamageReductionAmount(EquipmentSlotType slotIn) { return this.damageReductionAmountArray[slotIn.getIndex()]; } public int getEnchantability() { return this.enchantability; } public SoundEvent getSoundEvent() { return this.soundEvent; } public Ingredient getRepairMaterial() { return this.repairMaterial.getValue(); } @OnlyIn(Dist.CLIENT) public String getName() { return ExampleMod.MOD_ID + ":" + this.name; } public float getToughness() { return this.toughness; } }
name
: 内部名
maxDamageFactor
: 基礎耐久係数
MAX_DAMAGE_ARRAY
: 部位ごとの耐久値
damageReductionAmountArray
: 部位ごとの防御力
enchantability
: エンチャント効率
soundEvent
: 装着時の音
toughness
: 防具強度1
repairMaterial
: 修理素材[各部位の耐久値] = [maxDamageFactor]×[MAX_DAMAGE_ARRAY[部位]]
ダイヤモンドより強い性能にしてみました。防具のマテリアルをこれに替えます。
ItemList.java//... public class ItemList { public static Item ExampleHelmet = new ArmorItem(ExampleArmorMaterial.EXAMPLE, EquipmentSlotType.HEAD, new Item.Properties().group(ExampleItemGroup.DEFAULT)) .setRegistryName(new ResourceLocation(ExampleMod.MOD_ID, "example_helmet")); public static Item ExampleChestplate = new ArmorItem(ExampleArmorMaterial.EXAMPLE, EquipmentSlotType.CHEST, new Item.Properties().group(ExampleItemGroup.DEFAULT)) .setRegistryName(new ResourceLocation(ExampleMod.MOD_ID, "example_chestplate")); public static Item ExampleLeggings = new ArmorItem(ExampleArmorMaterial.EXAMPLE, EquipmentSlotType.LEGS, new Item.Properties().group(ExampleItemGroup.DEFAULT)) .setRegistryName(new ResourceLocation(ExampleMod.MOD_ID, "example_leggings")); public static Item ExampleBoots = new ArmorItem(ExampleArmorMaterial.EXAMPLE, EquipmentSlotType.FEET, new Item.Properties().group(ExampleItemGroup.DEFAULT)) .setRegistryName(new ResourceLocation(ExampleMod.MOD_ID, "example_boots")); //... }ゲームを起動します。
怪物が誕生しました。テクスチャが設定されていませんね。\src\main\resources\assets\example_mod ├ blockstates ├ lang ├ models └ textures ├ blocks ├ items └ models └ armor ├ example_layer_1.png └ example_layer_2.png
\assets\example_mod\textures\models\armor
フォルダを作り、[マテリアルの内部名]_layer_1.png
、[マテリアルの内部名]_layer_2.png
を配置します。バニラの防具のテクスチャなどを参考にしましょう。
ダイヤっぽい色合いでややわかりづらいですが、きちんとテクスチャが反映されました。また、耐久値が66*13=858になっていることが確認できます。今度こそ防具の追加ができました!
参考
Minecraft 1.14.4 Forge Modの作成 その7 【防具の追加】
次の記事
- 投稿日:2020-08-05T21:58:12+09:00
Mockito 3 + JUnit 5 で基本的なモック化とテストをするサンプルコード
概要
- Mockito 3 + JUnit 5 で基本的なモック化とテストをするサンプルコードを示す
- 今回の動作確認環境: Java 14 (AdoptOpenJDK 14.0.2+12) + Gradle 6.5.1 + Mockito 3.4.6 + JUnit 5.6.2 + macOS Catalina
サンプルコード
ソースコード一覧
├── build.gradle └── src ├── main │ └── java │ ├── Client.java │ └── Worker.java └── test └── java └── ClientTest.javabuild.gradle
ビルド・テスト実行用 Gradle 設定ファイル。
plugins { id 'java' } repositories { jcenter() } dependencies { // Junit 5 を導入 // https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter testImplementation 'org.junit.jupiter:junit-jupiter:5.6.2' // Mockito 3 を導入 // https://mvnrepository.com/artifact/org.mockito/mockito-core testImplementation 'org.mockito:mockito-core:3.4.6' // Mockito による JUnit 5 Extension ライブラリを導入 // https://mvnrepository.com/artifact/org.mockito/mockito-junit-jupiter testImplementation 'org.mockito:mockito-junit-jupiter:3.4.6' } test { // JUnit platform を使う設定 useJUnitPlatform() // テスト時の出力設定 testLogging { // テスト時の標準出力と標準エラー出力を表示する showStandardStreams true // イベントを出力する (TestLogEvent) events 'started', 'skipped', 'passed', 'failed' // 例外発生時の出力設定 (TestExceptionFormat) exceptionFormat 'full' } }src/main/java/Worker.java
モック化の対象となるクラス。
public class Worker { // 引数あり・戻り値ありメソッド public int ariari(int x) { throw new IllegalStateException("環境依存で発生するみたいな例外"); } // 引数なし・戻り値なしメソッド public void nasinasi() { throw new IllegalStateException("環境依存で発生するみたいな例外"); } }src/main/java/Client.java
テスト対象のクラス。
public class Client { private Worker worker; // 引数あり・戻り値ありメソッドを呼び出す public int callAriari(int x) { return worker.ariari(x * 2); // 2倍にした値を Worker#ariari に渡す } // 引数なし・戻り値なしメソッドを呼び出す public int callNasinasi(int x) { worker.nasinasi(); // Worker#nasinasi を呼び出す return x * 2; // 2倍にした値を返す } }src/test/java/ClientTest.java
テストクラス。
Worker クラスを Mockito 3 でモック化し、Client クラスのテストを JUnit 5 で実施する。import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; @ExtendWith(MockitoExtension.class) // Mockito による JUnit 5 エクステンション public class ClientTest { // モックを注入するオブジェクト @InjectMocks private Client client = new Client(); // モック化するオブジェクト @Mock private Worker mockedWorker; @Test public void 引数あり_戻り値あり() { // モックの振る舞い: Worker#ariari に 2 が渡されたときに 6 を返す doReturn(6).when(mockedWorker).ariari(2); // テスト: Client#callAriari に 1 を渡すとモックの Worker#ariari に 2 を渡して 6 が返ってくる assertEquals(6, client.callAriari(1)); } @Test public void 引数なし_戻り値なし() { // モックの振る舞い: Worker#nasinasi を呼び出したときに何もしない doNothing().when(mockedWorker).nasinasi(); // テスト: Client#callNasinasi に 1 を渡すとモックの Worker#nasinasi を実行して 2 が返ってくる assertEquals(2, client.callNasinasi(1)); } @Test public void 例外発生() { // モックの振る舞い: Worker#ariari に 4 が渡されたときに例外を投げる doThrow(new IllegalArgumentException("モック例外")).when(mockedWorker).ariari(4); // テスト: Client#callAriari に 2 を渡すとモックの Worker#ariari に 4 を渡して例外が投げられる IllegalArgumentException e = assertThrows(IllegalArgumentException.class, () -> client.callAriari(2)); // テスト: 例外に想定したメッセージが含まれている assertEquals("モック例外", e.getMessage()); } @Test public void 順次呼び出し() { // モックの振る舞い: Worker#ariari に 6 が渡されたときに例外を2回投げたあと 18 を返す doThrow(new IllegalArgumentException("モック例外1回目")) .doThrow(new IllegalArgumentException("モック例外2回目")) .doReturn(18) .when(mockedWorker).ariari(6); // テスト: Client#callAriari に 3 を渡すとモックの Worker#ariari に 6 を渡して例外が投げられる IllegalArgumentException e1 = assertThrows(IllegalArgumentException.class, () -> client.callAriari(3)); assertEquals("モック例外1回目", e1.getMessage()); // テスト: Client#callAriari に 3 を渡すとモックの Worker#ariari に 6 を渡して例外が投げられる IllegalArgumentException e2 = assertThrows(IllegalArgumentException.class, () -> client.callAriari(3)); assertEquals("モック例外2回目", e2.getMessage()); // テスト: Client#callAriari に 3 を渡すとモックの Worker#ariari に 6 を渡して 18 が返ってくる assertEquals(18, client.callAriari(3)); } }テスト実行
Gradle で test タスクを実行した例。
$ gradle test > Task :test ClientTest > 引数なし_戻り値なし() STARTED ClientTest > 引数なし_戻り値なし() PASSED ClientTest > 引数あり_戻り値あり() STARTED ClientTest > 引数あり_戻り値あり() PASSED ClientTest > 例外発生() STARTED ClientTest > 例外発生() PASSED ClientTest > 順次呼び出し() STARTED ClientTest > 順次呼び出し() PASSED BUILD SUCCESSFUL in 2s 3 actionable tasks: 3 executed参考資料
- 投稿日:2020-08-05T21:27:12+09:00
==(同一性)とequalsメソッド(同値性)の違い、オーバーライドしよう
javaには二つの「同じ」がある。
==(同一性)
==演算子は、同一であることを判定する。つまり、複数の変数が同じインスタンスを参照していることを判定する。
A a = new A(); // A型というインスタンスを生成 A b = a; // aのインスタンスごとそのままbへコピー System.out.println(a == b);true例題
public class Sample { private int num; public Sample(int num) { this.num = num; } }public class Main { public static void main(String[] args) { Sample s0 = new Sample(5); // Sample型のインスタンス生成 Sample s1 = s0; // s1にコピー s0 = new Sample(5); // 新たにSample型インスタンス生成 System.out.println(s0 == s1); } }false
Sample s1 = s0;
でコピーした時点では、同じインスタンスを持っていたが、次の行でs0
が新たにインスタンスを生成し、s0
とs1
は違うインスタンスになった。equalsメソッド(同値性)
equalsメソッドは、同値であることを判定する。つまり、インスタンスはことなるが、同じ値を持っているか判定する。
デフォルトのObjectクラスに定義されているequalsメソッドは、
public boolean equals(Object obj){ return (this == obj); }と定義されており、同一性を確認する仕様になっているため、
オーバーライドして使用することを前提としている。
さらに!!
equalsメソッドをオーバーライドする時は、セットでhashCodeもオーバーライドしないといけない。
※ hashCodeとは、インスタンスが持っている値をハッシュ関数に変換して、確認できるメソッドequals
通常、このメソッドをオーバーライドする場合は、hashCodeメソッドを常にオーバーライドして、等価なオブジェクトは等価なハッシュ・コードを保持する必要があるというhashCodeメソッドの汎用規約に従う必要があることに留意してください。例題
public class Sample { private int num; private String name; public Sample(int num, String name) { this.num = num; this.name = name; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((name == null) ? 0 : name.hashCode()); result = prime * result + num; return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Sample other = (Sample) obj; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; if (num != other.num) return false; return true; } }public class Main { public static void main(String[] args) { // 引数(num, name)が同じ Sample a = new Sample(5, "xxx"); Sample b = new Sample(5, "xxx"); System.out.println(a.equals(b)); // それぞれのhashCodeを出力 System.out.println(a.hashCode()); System.out.println(b.hashCode()); } }true 3694926 3694926equalsメソッドとhashCodeメソッドをオーバーライドしている。
もし、numかnameが一つでも違えば以下のようになる。
public class Main { public static void main(String[] args) { // 引数(name)が違う Sample a = new Sample(5, "xxx"); Sample b = new Sample(5, "yyy"); System.out.println(a.equals(b)); // それぞれのhashCodeを出力 System.out.println(a.hashCode()); System.out.println(b.hashCode()); } }false 3694926 3725709equals判定は
false
になり、hashCodeも違う値が出力されている。
- 投稿日:2020-08-05T18:02:30+09:00
【Java】電卓プログラムの作成1
目的
Javaの学習を独学で初めておよそ1ヶ月が経ち、Javaを使用してGUIアプリケーションを作成したいと思い、電卓アプリを作成してみる。今回は、まず電卓アプリの見た目を作成していく。
参考
▼webサイト
・Let'sプログラミング-Swingを使ってみよう
・Javaで始めるプログラミング-Chapter12電卓を作ってみる
・クラスBorderLayout使用環境
OS:Mac
言語:JavaSE1.8
Eclipse成果物
プログラム作成時に参考にさせて頂いたWebサイト
Javaで始めるプログラミング作成したプログラム
18〜21行目:フレームの生成(フレームのタイトルをつけたり、フレームの大きさを決めるなど)22〜25行目:テキストボックスの生成
27〜51行目:テンキーの生成。
4×4のグリッドに数値等のボタンを配置。実行結果
- 投稿日:2020-08-05T15:52:05+09:00
Mavenでjettyを使って、動作確認をする。
はじめに
eclipseを使って動的ファイルの動作確認をしていると、その端末でしか動かなったりする。なので、jettyを使って、動作確認をする。
あと個人的にeclipseでmavenプロジェクトはディレクトリ構成ちがったりめんどくさいので。手順
pom.xml編集
以下のプラグインを追加する。
<plugin> <groupId>org.mortbay.jetty</groupId> <artifactId>maven-jetty-plugin</artifactId> <version>6.1.10</version> <configuration> <scanIntervalSecounds>10</scanIntervalSecounds> <connectors> <connector implementation="org.mortbay.jetty.nio.SelectChannelConnector"> <port>8080</port> <maxIdleTime>60000</maxIdleTime> </connector> </connectors> </configuration> </plugin>jettyを実行する。
コマンドライで以下を実行する。
mvn jetty:runローカルホストに接続する。
http://localhost:8080
にアクセスする。最後に
jettyがうまく動かない場合あった。pom.xmlが悪いのか端末のが悪いのか、はたまたプラグインが悪いのか切り分けに時間がかかった。結果的にビルド用にコンテナ立てて都度確認した方が確実だし、依存性の問題がないので良いと思う。
- 投稿日:2020-08-05T11:15:26+09:00
[自分用メモ]修飾子について軽く勉強してみた
自己紹介
Javaを勉強したての社会人一年目です。
昨日書いた以下の記事のコメントで、「アクセス修飾子」「class設計」というワードが出てきたので、それらについて勉強してまとめようとこの記事を書いています。
https://qiita.com/N46_myHearter/items/891b660b8748171779b1Javaに関するアドバイス等募集中です。
修飾子とは
クラスやメソッド、変数に指定することができ、指定する修飾子によって様々な効果があるみたい。
自分は、Javaで修飾子だとpublic private等のアクセス修飾子ぐらいしかまだ知らなかったのですが、修飾子ってむっちゃあるんですね笑笑アクセス修飾子、abstract、static、finalまでは知ってたが、transient、volatile、synchronized、native、strictfp....って何や!?笑笑
アクセス修飾子
public、protected、privateがあり、アクセス修飾子を設計しているクラスやその変数、メソッドがどこからアクセス可能かを示しているみたい。
- public すべてのクラスからアクセス可能
- protected 現在のクラスとそのサブクラスからアクセス可能
- private 現在のクラスからのみアクセス可能
アクセスできる範囲は、public > protected > privateな感じかな。
abstract修飾子
abstractが設定されているクラス・メソッドは、抽象クラス・メソッドと言うみたい。。。(昨日勉強した抽象がさっそくでてきた...)
親クラスでabstractを設定している場合、その子クラスはオーバーライドしないといけない。抽象メソッド
本体がなく、メソッド名や引数、戻り値だけで構成されたメソッド。抽象クラス
抽象メソッドを含むクラス。
インスタンス化が出来なくなり、継承したサブクラスの作成が必要になる。static修飾子
staticが設定されていると、インスタンス化せずにアクセス可能になる(C言語でも静的ななんちゃらであったから分かる気がする...笑笑)。
変数、メソッドに指定可能。
オーバーライドはできない。
クラス名.メンバー名でアクセス可能のこと。インスタンス化
クラスをアクセス可能にして、利用できるようにすること(クラスを初期化するってことかな!?)。final修飾子
finalが設定されていると、メンバーの上書き、サブクラスでのオーバーライド、extends句での継承等を禁止にするみたい。
クラスや変数、メソッドに指定でき、指定したものによって、意味合いが変わるみたい。クラスに指定した場合
指定したクラスは継承不可になる。変数に指定した場合
指定した変数は定数(変数宣言時、初期化必須となり、以後変数の
値を変えることは不可)になる。メソッドに指定した場合
指定したメソッドはオーバーライド不可になる。オーバーライド
親クラスで定義しているのを、子クラスで再定義すること(継承みたいだね)。transient修飾子
transientが設定されていると、シリアライズの対象外にするみたいです(シリアライズって何や...笑笑)。
変数に指定可能。シリアライズ
オブジェクトをバイト配列に変換すること。volatile修飾子
volatileが設定されていると、キャッシュの対象外にするみたいです。
変数に指定可能。synchronized修飾子
synchronizedが設定されていると、メソッドを同期実行する。
メソッドに指定可能。同期実行
ほぼ同時に複数の呼び出しがあったときに、先に呼び出した方を優先して、その処理が終わるまで後から呼び出した方は待機させること。native修飾子
nativeが設定されていると、Java以外の言語で実装されているということになる(Javaプログラム上で別の言語を使えるってことかな)。
メソッドに指定可能。strictfp修飾子
strictfpが設定されていると、浮動小数点を厳密に処理することが可能みたい。
クラスやメソッドに指定可能。おわり
あくまでも、Java初心者が勉強したことをメモしてるので、内容薄いです笑笑
修飾子多いので、まずアクセス修飾子からもっと勉強していこうと思っています(他は後回しww)。
- 投稿日:2020-08-05T11:07:03+09:00
Java画像を最小化する方法
この記事では、Spring BootベースのJavaアプリケーションを例に、Javaイメージを最小化するための一般的なコツをいくつか紹介します。
本ブログは英語版からの翻訳です。オリジナルはこちらからご確認いただけます。一部機械翻訳を使用しております。翻訳の間違いがありましたら、ご指摘いただけると幸いです。
背景
コンテナ技術の普及に伴い、コンテナベースのアプリケーションが増えています。コンテナは頻繁に使用されていますが、ほとんどのコンテナユーザーは、コンテナイメージのサイズという単純だが重要な問題を無視しているかもしれません。この記事では、コンテナイメージを簡素化する必要性について簡単に説明し、Spring Boot ベースの Java アプリケーションを例に、Java イメージを最小化するための一般的なトリックをいくつか紹介します。
コンテナイメージを簡素化する必要性
コンテナイメージの簡素化は非常に必要です。これについては、セキュリティとアジリティの両面から説明します。
セキュリティについて
イメージから不要なコンポーネントを削除することで、攻撃面やセキュリティリスクを減らすことができます。Dockerでは、Seccompを使ってコンテナ内の操作を制限したり、AppArmorを使ってコンテナのセキュリティポリシーを設定したりすることができます。ただし、これらを利用するにはセキュリティ分野での十分な習熟度が必要です。
敏捷性
コンテナイメージを簡略化することで、コンテナのデプロイ速度を向上させることができます。アクセス トラフィックが突然バーストしたと仮定して、突然増加した圧力に対処するためにコンテナの数を増やす必要があるとします。一部のホストにターゲットイメージが含まれていない場合は、まずイメージを引っ張ってからコンテナを起動する必要があります。この場合、イメージを小さくすることで処理を高速化し、スケールアップの期間を短縮することができます。また、小さい画像の方がより早く構築でき、ストレージや伝送コストを節約することができます。
よくあるコツ
Java アプリケーションをコンテナ化するには、以下の手順を実行します。
1、Java ソース・コードをコンパイルし、JAR パッケージを生成します。
2、JAR パッケージとサードパーティ製 JAR 依存関係を適切な位置に移動します。
このセクションで使用する例は、Spring Boot ベースの Java アプリケーションである spring-boot-docker です。この例で使用した最適化されていないdockerfileは以下の通りです。FROM maven:3.5-jdk-8 COPY src /usr/src/app/src COPY pom.xml /usr/src/app RUN mvn -f /usr/src/app/pom.xml clean package ENTRYPOINT ["java","-jar","/usr/src/app/target/spring-boot-docker-1.0.0.jar"]アプリケーションはMavenを使って作成したもので、dockerfileのベースイメージにはmaven:3.5.5-jdk-8を指定しています。このイメージのサイズは635MBです。この方法で作成した最終的なイメージのサイズは719MBとかなり大きいです。理由は、ベースイメージが大きいことと、最終イメージをビルドするためにMavenが多くのJARパッケージをダウンロードするためです。
マルチステージビルド
Javaアプリケーションを実行するには、Javaランタイム環境(JRE)だけが必要です。MavenやJava Development Kit (JDK)のコンパイル、デバッグ、実行ツールは必要ありません。したがって、簡単な最適化方法は、Javaソースコードをコンパイルして作成するイメージと、Javaアプリケーションを実行するイメージを分離することです。そのためには、Docker 17.05のリリース前に2つのdockerfileファイルを維持する必要があり、イメージ構築の複雑さが増します。Docker 17.05からは、マルチステージビルド機能により、1つのdockerfile内で複数のFROM文を使用できるようになりました。それぞれのFROM文で異なるベースイメージを指定し、全く新しいイメージ構築プロセスを開始することができます。前のイメージ構築ステージの製品を別のステージにコピーし、必要な内容だけを最終イメージに残すように選択することができます。最適化されたdockerfileは以下のようになります。
FROM maven:3.5-jdk-8 AS build COPY src /usr/src/app/src COPY pom.xml /usr/src/app RUN mvn -f /usr/src/app/pom.xml clean package FROM openjdk:8-jre ARG DEPENDENCY=/usr/src/app/target/dependency COPY --from=build ${DEPENDENCY}/BOOT-INF/lib /app/lib COPY --from=build ${DEPENDENCY}/META-INF /app/META-INF COPY --from=build ${DEPENDENCY}/BOOT-INF/classes /app ENTRYPOINT ["java","-cp","app:app/lib/*","hello.Application"]dockerfileでは、最初のステージのビルドイメージとして
maven:3.5-jdk-8
を使用し、Javaアプリケーションを実行するベースイメージとしてopenjdk:8-jre
を使用しています。第一段階でコンパイルした.class
ファイルのみを、サードパーティのJARの依存関係とともに最終イメージにコピーしています。最適化の結果、イメージのサイズは459MBに縮小されました。ベースイメージとしてディストロレスイメージを使用する
多段ビルドによって最終イメージのサイズは小さくなっていますが、それでも459MBは大きすぎます。総合的に分析した結果,ベースとなる
openjdk:8-jre
のサイズは443MBと大きすぎることがわかりました.そこで,次の最適化のステップとして,ベース画像のサイズを小さくすることにしました.この問題を解決するために開発されたのが、GoogleのオープンソースプロジェクトであるDistrolessです。Distrolessイメージは、アプリケーションとその実行時の依存関係だけを含んでいます。これらのイメージには、パッケージマネージャ、シェル、その他標準的なLinuxディストリビューションに含まれると思われるプログラムは含まれていません。現在、Distroless は Java、Python、Node.js、.NET などの環境で動作するアプリケーション用のベースイメージを提供しています。
Distrolessイメージを使用したdockerfileファイルは以下のようになります。
FROM maven:3.5-jdk-8 AS build COPY src /usr/src/app/src COPY pom.xml /usr/src/app RUN mvn -f /usr/src/app/pom.xml clean package FROM gcr.io/distroless/java ARG DEPENDENCY=/usr/src/app/target/dependency COPY --from=build ${DEPENDENCY}/BOOT-INF/lib /app/lib COPY --from=build ${DEPENDENCY}/META-INF /app/META-INF COPY --from=build ${DEPENDENCY}/BOOT-INF/classes /app ENTRYPOINT ["java","-cp","app:app/lib/*","hello.Application"]この dockerfile と以前のものとの唯一の違いは、アプリケーションを実行するためのベースイメージが
openjdk:8-jre
(443 MB) からgcr.io/distroless/java
(119 MB) に変更されたことです。その結果、最終的なイメージのサイズは135MBになります。ディストロレスイメージを使うことの唯一の不便な点は、イメージにシェルが含まれていないことです。docker attachを使ってアプリケーションの標準入力、標準出力、標準エラー(またはこれら3つの組み合わせ)を実行中のコンテナにアタッチしてデバッグすることはできません。 distrolessのdebugイメージはbusyboxシェルを提供します。しかし、このイメージをリパッケージしてコンテナをデプロイしなければならず、非デバッグイメージに基づいてデプロイされたコンテナには役に立ちません。セキュリティの観点から見ると、攻撃者はシェルを介して攻撃できないので、これは利点になるかもしれません。
アルパインイメージをベースイメージとして使用する
docker attach を使用する必要があり、画像サイズを最小限に抑えたい場合は、ベース画像としてアルプスの画像を使用することができます。アルパイン画像は信じられないほど小さいのが特徴で、ベース画像のサイズは4MB程度しかありません。
アルペン画像を使用したdockerfileは以下のようになります。
FROM maven:3.5-jdk-8 AS build COPY src /usr/src/app/src COPY pom.xml /usr/src/app RUN mvn -f /usr/src/app/pom.xml clean package FROM openjdk:8-jre-alpine ARG DEPENDENCY=/usr/src/app/target/dependency COPY --from=build ${DEPENDENCY}/BOOT-INF/lib /app/lib COPY --from=build ${DEPENDENCY}/META-INF /app/META-INF COPY --from=build ${DEPENDENCY}/BOOT-INF/classes /app ENTRYPOINT ["java","-cp","app:app/lib/*","hello.Application"]
openjdk:8-jre-alpine
はalpineをベースに構築されたもので、Javaランタイムを含んでいます。この dockerfile でビルドされたイメージのサイズは 99.2 MB で、 distroless イメージをベースにビルドされたイメージよりも小さくなっています。実行中のコンテナにアタッチするには、
docker exec -ti <container_id> sh
コマンドを実行してください。Distroless と Alpine の比較
Distroless と Alpineはどちらも非常に小さなベース画像を提供することができます。本番環境ではどちらを使うべきでしょうか?セキュリティを第一に考えるのであれば、パッケージ化されたアプリケーションが実行できるのはバイナリファイルだけなので、distroless をお勧めします。イメージのサイズを重視するのであれば、alpine をお勧めします。
その他のコツ
前述のコツに加えて、以下のような操作を行うことで、さらにイメージサイズをシンプルにすることができます。
1、dockerfile内の複数の命令を1つにまとめます。これにより、画像のレイヤー数が減り、画像サイズが小さくなります。
2、安定した大きなコンテンツをイメージの下層に配置し、頻繁に変化する小さなコンテンツを上層に配置します。この方法では、直接画像サイズを小さくすることはできません。しかし、イメージキャッシュの仕組みをフルに活用して、イメージの構築とコンテナのデプロイを高速化します。
Dockerfilesを最適化するためのヒントについては、Dockerfilesを書くためのベストプラクティスを参照してください。概要
1、一連の最適化により、Javaアプリケーションの画像サイズは719MBから約100MBに縮小されました。アプリケーションが他の環境で動作する場合も、同様の原理で最適化することができます。
2、Javaイメージの場合、Googleが提供する別のツールであるjibは、複雑なイメージ構築プロセスを自動的に処理し、簡略化されたJavaイメージを提供することができます。これを使えば、dockerfileを書く必要はなく、Dockerをインストールする必要すらありません。
3、デバッグに不便なdistro-lessなどのコンテナについては、そのログを一元的に保存しておけば、問題のトレースやトラブルシューティングが容易になります。詳しくは、コンテナのログ処理のための技術的なベストプラクティスの記事を参照してください。アリババクラウドは日本に2つのデータセンターを有し、世界で60を超えるアベラビリティーゾーンを有するアジア太平洋地域No.1(2019ガートナー)のクラウドインフラ事業者です。
アリババクラウドの詳細は、こちらからご覧ください。
アリババクラウドジャパン公式ページ
- 投稿日:2020-08-05T09:21:24+09:00
Calendar 関連
Calendar c = Calendar.getInstance(); c.add(Calendar.YEAR, -1); c.set(Calendar.HOUR_OF_DAY, 0); c.set(Calendar.MINUTE, 0); c.set(Calendar.SECOND, 0); c.getTime();Date.from(LocalDate.now().minusYear(1L).atStartOfDay(ZoneId.systemDefault().toInstant());
- 投稿日:2020-08-05T05:08:00+09:00
[Java]MinecraftのModを作成しよう 1.14.4【4. ツールの追加】
(この記事は一連の解説記事の一つになります)
先頭記事:入門編
前の記事:3. クリエイティブタブの追加
次の記事:5. 防具の追加ツールの追加
1. アイテムの追加では何の機能も持たない単純なアイテムを追加しました。今度は剣やピッケルといった役割を持ったツールを追加してみましょう。
\src\main\java\jp\koteko\example_mod\ ├ items │ ├ ItemExampleAxe.java │ ├ ItemExampleHoe.java │ ├ ItemExamplePickaxe.java │ ├ ItemExampleShovel.java │ └ ItemExampleSword.java ├ lists │ └ ItemList.java ├ ExampleItemGroup.java └ ExampleMod.java
items
フォルダを作り、各アイテムのクラスを作ります。
今の時点では継承元のクラスとほぼ変わらないため、新たにクラスを作る必要はほとんどないですが、あとで色々とカスタマイズすることを想定して別クラスとしました。ItemExampleAxe.javapackage jp.koteko.example_mod.items; import net.minecraft.item.AxeItem; import net.minecraft.item.ItemTier; public class ItemExampleAxe extends AxeItem { public ItemExampleAxe(Properties properties) { super(ItemTier.IRON, 5, -3.0F, properties); } }ItemExampleHoe.javapackage jp.koteko.example_mod.items; import net.minecraft.item.HoeItem; import net.minecraft.item.ItemTier; public class ItemExampleHoe extends HoeItem { public ItemExampleHoe(Properties properties) { super(ItemTier.IRON, 0, properties); } }ItemExamplePickaxe.javapackage jp.koteko.example_mod.items; import net.minecraft.item.ItemTier; import net.minecraft.item.PickaxeItem; public class ItemExamplePickaxe extends PickaxeItem { public ItemExamplePickaxe(Properties properties) { super(ItemTier.IRON, 1, -2.8F, properties); } }ItemExampleShovel.javapackage jp.koteko.example_mod.items; import net.minecraft.item.ItemTier; import net.minecraft.item.ShovelItem; public class ItemExampleShovel extends ShovelItem { public ItemExampleShovel(Properties properties) { super(ItemTier.IRON, 1.5F, -3.0F, properties); } }ItemExampleSword.javapackage jp.koteko.example_mod.items; import net.minecraft.item.ItemTier; import net.minecraft.item.SwordItem; public class ItemExampleSword extends SwordItem { public ItemExampleSword(Properties properties) { super(ItemTier.IRON, 3, -2.4F, properties); } }継承元のコンストラクタに渡す引数は順に以下の通りです。
Properties
だけインスタンス化時に渡して、あとはここで決めてしまうようにしました。後述の「発展」の項も参照してください。
IItemTier tier
: アイテムティア(いわゆるレア度・グレード的なもの)。
int attackDamageIn
あるいはfloat attackDamageIn
: 攻撃力。なぜかツールによってint, floatが異なる。Hoeには存在しない。
float attackSpeedIn
: 攻撃速度。
Properties builder
: アイテムプロパティ。今までクリエイティブタブの設定に使ってきたやつ。
アイテムリストに追加して登録していきます。
ItemList.javapackage jp.koteko.example_mod.lists; import jp.koteko.example_mod.ExampleItemGroup; import jp.koteko.example_mod.ExampleMod; import jp.koteko.example_mod.items.*; import net.minecraft.item.Item; import net.minecraft.util.ResourceLocation; import net.minecraftforge.event.RegistryEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; @Mod.EventBusSubscriber(modid = ExampleMod.MOD_ID, bus = Mod.EventBusSubscriber.Bus.MOD) public class ItemList { public static Item ExampleIngot = new Item(new Item.Properties().group(ExampleItemGroup.DEFAULT)) .setRegistryName(new ResourceLocation(ExampleMod.MOD_ID, "example_ingot")); public static Item ExampleAxe = new ItemExampleAxe(new Item.Properties().group(ExampleItemGroup.DEFAULT)) .setRegistryName(new ResourceLocation(ExampleMod.MOD_ID, "example_axe")); public static Item ExampleHoe = new ItemExampleHoe(new Item.Properties().group(ExampleItemGroup.DEFAULT)) .setRegistryName(new ResourceLocation(ExampleMod.MOD_ID, "example_hoe")); public static Item ExamplePickaxe = new ItemExamplePickaxe(new Item.Properties().group(ExampleItemGroup.DEFAULT)) .setRegistryName(new ResourceLocation(ExampleMod.MOD_ID, "example_pickaxe")); public static Item ExampleShovel = new ItemExampleShovel(new Item.Properties().group(ExampleItemGroup.DEFAULT)) .setRegistryName(new ResourceLocation(ExampleMod.MOD_ID, "example_shovel")); public static Item ExampleSword = new ItemExampleSword(new Item.Properties().group(ExampleItemGroup.DEFAULT)) .setRegistryName(new ResourceLocation(ExampleMod.MOD_ID, "example_sword")); @SubscribeEvent public static void registerItems(RegistryEvent.Register<Item> event) { event.getRegistry().registerAll( ExampleIngot, ExampleAxe, ExampleHoe, ExamplePickaxe, ExampleShovel, ExampleSword ); } }
続いて設定をしていきます。
\src\main\resources\assets\example_mod ├ blockstates ├ lang │ └ en_us.json │ └ ja_jp.json ├ models │ ├ block │ └ item │ ├ example_axe.json │ ├ example_hoe.json │ ├ example_pickaxe.json │ ├ example_shovel.json │ └ example_sword.json └ textures ├ blocks └ items ├ example_axe.png ├ example_hoe.png ├ example_pickaxe.png ├ example_shovel.png └ example_sword.pngen_us.json{ "item.example_mod.example_axe": "Example Axe", "item.example_mod.example_hoe": "Example Hoe", "item.example_mod.example_pickaxe": "Example Pickaxe", "item.example_mod.example_shovel": "Example Shovel", "item.example_mod.example_sword": "Example Sword" }ja_jp.json{ "item.example_mod.example_axe": "例の斧", "item.example_mod.example_hoe": "例の鍬", "item.example_mod.example_pickaxe": "例のピッケル", "item.example_mod.example_shovel": "例のスコップ", "item.example_mod.example_sword": "例の剣", }
(なんかいわくつきの武器みたいな名前になってしまった…)example_axe.json{ "parent": "item/handheld", "textures": { "layer0": "example_mod:items/example_axe" } }example_hoe.json{ "parent": "item/handheld", "textures": { "layer0": "example_mod:items/example_hoe" } }example_pickaxe.json{ "parent": "item/handheld", "textures": { "layer0": "example_mod:items/example_pickaxe" } }example_shovel.json{ "parent": "item/handheld", "textures": { "layer0": "example_mod:items/example_shovel" } }example_sword.json{ "parent": "item/handheld", "textures": { "layer0": "example_mod:items/example_sword" } }
parent
にはitem/handheld
を指定することで手に持つタイプのアイテムとなります。テクスチャも配置したらゲームを起動します。
(クリエイティブで撮ったので耐久が減ってません)ツールの追加ができました!
発展
Q. なんか攻撃力と攻撃速度の設定が値と違うんだけど?
Q. なんで攻撃速度マイナスなの?
A. 攻撃力は、[IItemTier
の値]+[各ツールでの値]+1、攻撃速度は4.0+[各ツールでの値]で求まるようです。
今回追加したツールクラスの継承元クラスの1つSwordItem.java
を見てみましょう。SwordItem.java//... public class SwordItem extends TieredItem { private final float attackDamage; private final float attackSpeed; public SwordItem(IItemTier tier, int attackDamageIn, float attackSpeedIn, Item.Properties builder) { super(tier, builder); this.attackSpeed = attackSpeedIn; this.attackDamage = (float)attackDamageIn + tier.getAttackDamage(); } //... }
(float)attackDamageIn + tier.getAttackDamage()
と計算しているのが分かります。
(なお実際GUI上での表示が+1されている理由がわかるコードは見つけられませんでしたが、バニラのアイテムもこのような式で求められる値に合致していました。表示上+1されているのか、内部計算的にも+1されているのかは不明です。)
(攻撃速度についても同様に、4.0のオフセットから計算している部分が見つけられていません。)続いて
ItemTier
を見てみましょう。ItemTier.java//... public enum ItemTier implements IItemTier { WOOD(0, 59, 2.0F, 0.0F, 15, () -> { return Ingredient.fromTag(ItemTags.PLANKS); }), STONE(1, 131, 4.0F, 1.0F, 5, () -> { return Ingredient.fromItems(Blocks.COBBLESTONE); }), IRON(2, 250, 6.0F, 2.0F, 14, () -> { return Ingredient.fromItems(Items.IRON_INGOT); }), DIAMOND(3, 1561, 8.0F, 3.0F, 10, () -> { return Ingredient.fromItems(Items.DIAMOND); }), GOLD(0, 32, 12.0F, 0.0F, 22, () -> { return Ingredient.fromItems(Items.GOLD_INGOT); }); //... private ItemTier(int harvestLevelIn, int maxUsesIn, float efficiencyIn, float attackDamageIn, int enchantabilityIn, Supplier<Ingredient> repairMaterialIn) { this.harvestLevel = harvestLevelIn; this.maxUses = maxUsesIn; this.efficiency = efficiencyIn; this.attackDamage = attackDamageIn; this.enchantability = enchantabilityIn; this.repairMaterial = new LazyLoadBase<>(repairMaterialIn); } //... }このようにTierごとに値が定まっています。
例えば、今回追加した
Example Sword
では、ItemTier.IRON
なので、
攻撃力
[IItemTierの値]+[各ツールでの値]+1 = 2 + 3 + 1 = 6
攻撃速度
4.0+[各ツールでの値] = 4.0 - 2.4 = 1.6
となります。
Q. 石とか鉄とかじゃなくて、ちゃんと固有の素材にしたい
Q. 耐久値の設定ってどこ?
A. 新しくItemTier
を作りましょう。
上で触れたItemTier
のEnumを新しく定義します。\src\main\java\jp\koteko\example_mod\ ├ items │ └ ExampleItemTier.java ├ lists ├ ExampleItemGroup.java └ ExampleMod.javaExampleItemTier.javapackage jp.koteko.example_mod.items; import jp.koteko.example_mod.lists.ItemList; import net.minecraft.item.IItemTier; import net.minecraft.item.crafting.Ingredient; import net.minecraft.util.LazyLoadBase; import java.util.function.Supplier; public enum ExampleItemTier implements IItemTier { EXAMPLE(4, 3122, 10.0F, 4.0F, 25, () -> { return Ingredient.fromItems(ItemList.ExampleIngot); }); private final int harvestLevel; private final int maxUses; private final float efficiency; private final float attackDamage; private final int enchantability; private final LazyLoadBase<Ingredient> repairMaterial; private ExampleItemTier(int harvestLevelIn, int maxUsesIn, float efficiencyIn, float attackDamageIn, int enchantabilityIn, Supplier<Ingredient> repairMaterialIn) { this.harvestLevel = harvestLevelIn; this.maxUses = maxUsesIn; this.efficiency = efficiencyIn; this.attackDamage = attackDamageIn; this.enchantability = enchantabilityIn; this.repairMaterial = new LazyLoadBase<>(repairMaterialIn); } public int getMaxUses() { return this.maxUses; } public float getEfficiency() { return this.efficiency; } public float getAttackDamage() { return this.attackDamage; } public int getHarvestLevel() { return this.harvestLevel; } public int getEnchantability() { return this.enchantability; } public Ingredient getRepairMaterial() { return this.repairMaterial.getValue(); } }引数は順に、採取レベル、耐久値、効率、攻撃力、エンチャント効率、修理素材(のリスト的なもの)です。
ダイヤモンドのDIAMOND(3, 1561, 8.0F, 3.0F, 10, ...)
より良い値にしてみました。ツールクラスの
Tier
を変更します。ItemExampleSword.javapackage jp.koteko.example_mod.items; import net.minecraft.item.SwordItem; public class ItemExampleSword extends SwordItem { public ItemExampleSword(Properties properties) { super(ExampleItemTier.EXAMPLE, 3, -2.4F, properties); } }
きちんと攻撃力、耐久値が伸びています。(F3でデバック表示をしてF3+Hで耐久値などが表示できます。)
Q. 雷や炎の出るぼくのかんがえたさいきょうのぶきがつくりたい!
A. 私もまだ難しくてわからないので一緒に勉強しましょう。(解説予定なし)次の記事
参考