山崎屋の技術メモ

IT業界で働く中で、気になること、メモしておきたいことを書いていきます。

Java 8 ソースコードを入手する

Windows での話です。

Java のソースが JDK に含まれていなくて困った。

ソースのダウンロード方法について、いくつか紹介されているサイトを見たが、古い情報もあり、入手するまで 1 時間程度さまよってしまった。なんか、国とか権利の関係で、Oracle のサイトにそのまま置けないみたい。

いつまでこの方法で入手できるか不明だが、2017 年 8 月時点での、入手方法をメモしておく。

入手方法

Java SE - Downloads | Oracle Technology Network | Oracle

上記 URL の画面で、JDK の Download ボタンを押す。

遷移先の画面で、対象のOSを選択する。
「Accept License Agreement」(ライセンスの許諾)を選択し、Linux 版の JDK をダウンロードする。

f:id:yyama1556:20170811095953p:plain

解凍すると src.zip があるので、これがソース。


分かってしまえば簡単だが、なんとかもっとダウンロードしやすくしてもらえないものか。

おしまい。

新・解きながら学ぶJava

新・解きながら学ぶJava

徹底攻略 Java SE 8 Silver 問題集[1Z0-808]対応

徹底攻略 Java SE 8 Silver 問題集[1Z0-808]対応

改訂2版 パーフェクトJava

改訂2版 パーフェクトJava

Javaの絵本 第3版 Javaが好きになる新しい9つの扉

Javaの絵本 第3版 Javaが好きになる新しい9つの扉

Eclipse 4.7 Oxygen のインストール

2017 年 6 月 に Eclipse の新バージョンが公開されました。 バージョンは 4.7 でコードネームは「Oxygen」。発音は「オキシゲン」あたりでいいのではないでしょうか。

さっそくインストールしてみます。OS は Windows7 です。

Eclipse 4.6 Neon のインストールの記事は↓です。
Eclipse4.6 Neon インストール - 山崎屋の技術メモ

ダウンロード

日本語パッケージを使いたいのであれば Pleiades のページでダウンロードできるものを使えばいいですが、私は Eclipse の日本語化をしない主義なので、オフィシャルページからダウンロードします。オフィシャルのほうがファイルサイズは小さいです。

www.eclipse.org

私のメインは WEB システム開発ということもあり、「Eclipse IDE for Java EE Developers」のパッケージをダウンロードします。

64 bit バージョンが欲しいのであれば、下図の赤枠のところをクリックします。

f:id:yyama1556:20170805115312p:plain

次のページに進むのでダウンロードボタンを押しましょう。

f:id:yyama1556:20170805115519p:plain

マイナーバージョンにより異なると思いますが、サイズは 328 M でした。ダウンロード時間は約 2 分。

大きいファイルのダウンロードでは、中身が壊れることもあるので、できればハッシュ値を求め、サイトに記載のハッシュ値と一致するか確認したほうが安全です。Dos 窓を立ち上げて CertUtil コマンドを使います。

サンプル

C:\opt>CertUtil -hashfile eclipse-jee-oxygen-R-win32-x86_64.zip SHA512
SHA512 ハッシュ (ファイル eclipse-jee-oxygen-R-win32-x86_64.zip):
43 83 8d 48 8c 2f 1f 64 c3 6f 0a 0c 06 72 b2 55 4a 46 ec a6 a4 62 d6 a0 a1 4c 53 40 dd 10 bc 38 27 38 58 28 14 9d 27 7d 61 fc d7 af e9 53 8b 4a f6 8c 86 0b ec 61 f3 90 64 14 80 ce 4a e1 04 52
CertUtil: -hashfile コマンドは正常に完了しました。

ダウンロード直前の画面の「SHA-512」ボタンを押すと、正しいハッシュ値が表示されるので、CertUtil コマンド結果と比較し、一致していればOKです。下図、矢印の 43838d488... が正しいハッシュ値です。

f:id:yyama1556:20170805121911p:plain

ファイルの解凍

ダウンロードしたファイル(eclipse-jee-oxygen-R-win32-x86_64.zip)をお好みのフォルダに解凍します。私は「C:\opt」以下に解凍しました。

解凍後の eclipse フォルダ内には次のようなファイルがあります。
f:id:yyama1556:20170805124622p:plain

実行する

eclipse.exe をダブルクリックすると、ワークスペース(作業フォルダ)の場所を求めるダイアログが表示されます。入力して OK ボタンを押すと、無事に立ち上がりました。

f:id:yyama1556:20170805125313p:plain

簡単ですね!

インストール後の各種設定などは↓を参考にしてください。

yyama1556.hateblo.jp


以上です。

JavaデベロッパーのためのEclipse完全攻略[4.x対応版]

JavaデベロッパーのためのEclipse完全攻略[4.x対応版]

Eclipse 4.4 完全攻略 (完全攻略シリーズ)

Eclipse 4.4 完全攻略 (完全攻略シリーズ)

Eclipse パーフェクトマニュアルベストセレクション

Eclipse パーフェクトマニュアルベストセレクション

実践 デバッグ技法 ―GDB、DDD、Eclipseによるデバッギング

実践 デバッグ技法 ―GDB、DDD、Eclipseによるデバッギング

Java8 での日付妥当性チェック

Java8 の LocalDate と DateTimeFormatter を使用した日付妥当性チェックについてです。

2014 年に公開された Java8 を使用した日付妥当性チェックですが、すでに多くのサイトでサンプルが掲載されています。ただ、少し気になる実装も紹介されており、自分なりの正しい日付チェック方法を残しておきます。

	public static boolean isCorrectDate(String dateStr) {
		if (dateStr == null || dateStr.length() != 10) {
			return false;
		}
		DateTimeFormatter df = DateTimeFormatter.ofPattern("uuuu/MM/dd").withResolverStyle(ResolverStyle.STRICT);
		try {
			LocalDate.parse(dateStr, df);
		} catch (DateTimeParseException e) {
			return false;
		}
		return true;
	}

最初に引数の null チェックと 10 文字であるかどうかのチェックを行います。10 文字であるかどうかのチェックを行わない場合、「-99999/01/01」が妥当な日付と判断されてしまいます(「99999/01/01」は期待通りに、正しくない日付と判断します。不思議。。。)。マイナスの年を妥当な日付と判断することが要件に添っている状況は稀だと思うので、マイナスは問答無用で正しくない日付と判断してしまいます。

使用例を以下に示します。

	public static void main(String... args) {
		System.out.println(isCorrectDate("0000/01/01"));  // true
		System.out.println(isCorrectDate("9999/12/31"));  // true
		System.out.println(isCorrectDate("2017/02/28"));  // true
		System.out.println(isCorrectDate("2017/02/29"));  // false  うるう年でないので 2/29 は存在しない。
		System.out.println(isCorrectDate("2020/02/28"));  // true
		System.out.println(isCorrectDate("2020/02/29"));  // true 2020 年はうるう年。東京オリンピックの年。
		System.out.println(isCorrectDate("02020/02/29")); // false 年が 5 桁。
		System.out.println(isCorrectDate("2020/2/29"));   // false 月が 1 桁。
		System.out.println(isCorrectDate("2020/02/1"));   // false 日が 1 桁。
		System.out.println(isCorrectDate("202a/02/01"));  // false 年にアルファベット混入。
		System.out.println(isCorrectDate("2020/0a/01"));  // false 月にアルファベット混入。
		System.out.println(isCorrectDate("2020/01/0a"));  // false 日にアルファベット混入。
	}


でわ。

Java8ではじめる「ラムダ式」 (I・O BOOKS)

Java8ではじめる「ラムダ式」 (I・O BOOKS)

Javaによる関数型プログラミング ―Java 8ラムダ式とStream

Javaによる関数型プログラミング ―Java 8ラムダ式とStream

わかりすぎるJava8の教科書 (SCC Books 376)

わかりすぎるJava8の教科書 (SCC Books 376)

現場で使える[最新]Java SE 7/8 速攻入門

現場で使える[最新]Java SE 7/8 速攻入門

【Spring Framework】bean名による@Autowired

前回の記事で Spring Framework による簡単な DI を説明した。

yyama1556.hateblo.jp

これはプロパティの型を手掛かりに Spring が DI してくれていて、"byType" によるインジェクションという。

では、プロパティの型と同じクラスが2つ以上存在した場合はどちらをDIしてくれるのだろうか。

今回は bean の名前によるインジェクションである "byName" によるインジェクションを紹介する。

"byType" なのにプロパティの型と同じクラスが2つ以上存在した場合の挙動

フォルダ構成はこう。
f:id:yyama1556:20160810085417p:plain

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="
      http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans.xsd
      http://www.springframework.org/schema/context
      http://www.springframework.org/schema/context/spring-context.xsd">
	<context:component-scan base-package="org.yyama.bean" />
</beans>

[org.yyama.bean]パッケージ配下のクラスをSpringコンテキストの管理対象としている。

Mainクラス

package org.yyama;

import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.yyama.bean.Fuga;

public class Main {
	public static void main(String... args) {
		ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
		Fuga fuga = ctx.getBean(Fuga.class);
		fuga.proc();
		ctx.close();
	}
}

Spring コンテナから Fuga のインスタンス fuga を取得し、fuga.proc() メソッドを実行している。

Fuga クラス

package org.yyama.bean;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class Fuga {
	@Autowired()
	HogeInterface hoge;

	public void proc() {
		hoge.print();
	}
}

ここの hoge プロパティには HogeInterface をインプリメントした実装クラスがインジェクションされるのだが、今回はHogeInterface をインプリメントしたクラスが2つ存在する。その場合どのような挙動になるのかを実験する。

HogeInterfaceインターフェース

package org.yyama.bean;

public interface HogeInterface {
	public void print();
}

HogeImplAクラス

package org.yyama.bean;

import org.springframework.stereotype.Component;

@Component
public class HogeImplA implements HogeInterface {
	@Override
	public void print() {
		System.out.println("HogeImplAのproc()");
	}
}

print() メソッドで "HogeImplAのproc()" と出力している。

HogeImplBクラス

package org.yyama.bean;

import org.springframework.stereotype.Component;

@Component
public class HogeImplB implements HogeInterface {
	@Override
	public void print() {
		System.out.println("HogeImplBのproc()");
	}
}

print() メソッドで "HogeImplBのproc()" と出力している。

Mainクラスを実行すると・・・

エラーが出力された。簡単に訳すと「fugaの生成に失敗した。hogeフィールドの依存性が満たされない。[org.yyama.bean.HogeInterface]が定義されているが、これにひとつだけ一致することを期待していたが、2つ見つかった。hogeImplAとhogeImplBだ。」のように出力されている。

8 10, 2016 8:55:40 午前 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
情報: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@4d405ef7: startup date [Wed Aug 10 08:55:40 JST 2016]; root of context hierarchy
8 10, 2016 8:55:40 午前 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
情報: Loading XML bean definitions from class path resource [applicationContext.xml]
8 10, 2016 8:55:41 午前 org.springframework.context.support.ClassPathXmlApplicationContext refresh
警告: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'fuga': Unsatisfied dependency expressed through field 'hoge': No qualifying bean of type [org.yyama.bean.HogeInterface] is defined: expected single matching bean but found 2: hogeImplA,hogeImplB; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [org.yyama.bean.HogeInterface] is defined: expected single matching bean but found 2: hogeImplA,hogeImplB
Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'fuga': Unsatisfied dependency expressed through field 'hoge': No qualifying bean of type [org.yyama.bean.HogeInterface] is defined: expected single matching bean but found 2: hogeImplA,hogeImplB; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [org.yyama.bean.HogeInterface] is defined: expected single matching bean but found 2: hogeImplA,hogeImplB
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:569)
	at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:349)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1214)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:543)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
	at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:776)
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:861)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:541)
	at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139)
	at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:83)
	at org.yyama.Main.main(Main.java:8)
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [org.yyama.bean.HogeInterface] is defined: expected single matching bean but found 2: hogeImplA,hogeImplB
	at org.springframework.beans.factory.config.DependencyDescriptor.resolveNotUnique(DependencyDescriptor.java:172)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1065)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1019)
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:566)
	... 15 more

byName によるインジェクションに修正する。

Fuga クラス、HogeImplA クラス、HogeImplB クラスを修正する。

Fuga クラス

package org.yyama.bean;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

@Component
public class Fuga {
	@Autowired()
	@Qualifier("hogeA")
	HogeInterface hoge;

	public void proc() {
		hoge.print();
	}
}

"@Autowired()" の下に、"@Qualifier("hogeA")" を追加した。これで [hogeA] という名前の bean をインジェクションするようSpring に要求している。

HogeImplAクラス

package org.yyama.bean;

import org.springframework.stereotype.Component;

@Component("hogeA")
public class HogeImplA implements HogeInterface {
	@Override
	public void print() {
		System.out.println("HogeImplAのproc()");
	}
}

"@Component" アノテーションに引数を追加している。これでこのクラスのインスタンスは "hogeA" という名前で Spring に管理される。

HogeImplB クラス

package org.yyama.bean;

import org.springframework.stereotype.Component;

@Component("hogeB")
public class HogeImplB implements HogeInterface {
	@Override
	public void print() {
		System.out.println("HogeImplBのproc()");
	}
}

もうお分かりだろうが、"@Component" アノテーションに引数を追加している。これでこのクラスのインスタンスは "hogeB" という名前で Spring に管理される。

これで "HogeInterface" をインプリメントした実装クラスは Spring コンテナ内に2つ存在するが、名前が異なるので見分けがつくということだ。

実行結果

8 10, 2016 9:11:18 午前 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
情報: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@4d405ef7: startup date [Wed Aug 10 09:11:18 JST 2016]; root of context hierarchy
8 10, 2016 9:11:18 午前 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
情報: Loading XML bean definitions from class path resource [applicationContext.xml]
HogeImplAのproc()
8 10, 2016 9:11:18 午前 org.springframework.context.support.ClassPathXmlApplicationContext doClose
情報: Closing org.springframework.context.support.ClassPathXmlApplicationContext@4d405ef7: startup date [Wed Aug 10 09:11:18 JST 2016]; root of context hierarchy

Fuga クラスで "@Qualifier("hogeA")" と指定しているので、Fuga クラスの hoge プロパティには "hogeA" という名前の beanがインジェクションされたことがわかる。

今日はここまで。


Spring 関連記事へのリンク集つくりました。


【Spring MVC】Model に登録されているオブジェクトの一覧を表示する

前回の記事で、サーバ側(Controller)で設定した値を JSP で表示するサンプルを掲載した。その際に Spring で用意されている、 Model オブジェクトに JSP で使用したいオブジェクトをセットした。

yyama1556.hateblo.jp

今回はこの Model に登録されているオブジェクトの一覧を表示する方法、および、識別名をキーにオブジェクトの存在有無を確認するサンプルをメモしておく。

オブジェクトの一覧を表示する

難しいことは何もないので、まずソース(抜粋)。

	@RequestMapping(value = "/", method = RequestMethod.GET)
	public String home(Model model) {
		// (1) String
		model.addAttribute("att1", "ほげ");

		// (2) List
		List<String> list = new ArrayList<>();
		list.add("リスト1");
		list.add("リスト2");
		list.add("リスト3");
		model.addAttribute("att2", list);

		// (3) 独自クラス
		ClassA classA = new ClassA();
		classA.setName("山崎屋");
		classA.setAge(19);
		model.addAttribute("att3", classA);

		// (4) model に登録されているオブジェクトをすべて表示
		model.asMap().entrySet().stream().forEach(s -> System.out.println(s));

		return "home";
	}

(1)~(3) でそれぞれ String/List/独自クラス のオブジェクトを生成し、model に addAttribute している。

そして (4) で model に登録されているオブジェクトの一覧を標準出力に出力している。Stream を使用しているので、見慣れていない人は何をしているのか分かりにくいかも知れないが、ポイントは「model.asMap()」の部分。ここで、model に登録されているオブジェクトをマップにして返している。

Map のキーが識別名(att1、att2、att3)の文字列で、Map の値が登録したオブジェクトである。標準出力にはこのオブジェクトの toString() の戻り値が表示される。

このメソッドを実行した際の標準出力はこちら。

att1=ほげ
att2=[リスト1, リスト2, リスト3]
att3=org.yyama.web.HomeController$ClassA@20a4e442

ちなみに、Stream を使用しない場合、 (4)は次のようになる。

		// (4) model に登録されているオブジェクトをすべて表示
		for (Map.Entry<String, Object> e : model.asMap().entrySet()) {
			System.out.println(e);
		}

model への登録有無を確認する。

model オブジェクトには containsAttribute というインスタンスメソッドが用意されており、第一引数に識別名を文字列で渡してあげることにより、その識別名が登録されているか否か、bool 値を返してくれる。

サンプルソースはこちら。

	@RequestMapping(value = "/", method = RequestMethod.GET)
	public String home(Model model) {
		// (1) String
		model.addAttribute("att1", "ほげ");

		// (2) List
		List<String> list = new ArrayList<>();
		list.add("リスト1");
		list.add("リスト2");
		list.add("リスト3");
		model.addAttribute("att2", list);

		// (3) 独自クラス
		ClassA classA = new ClassA();
		classA.setName("山崎屋");
		classA.setAge(19);
		model.addAttribute("att3", classA);

		// (5) model への登録有無を確認
		System.out.println(model.containsAttribute("att1")); // true
		System.out.println(model.containsAttribute("att2")); // true
		System.out.println(model.containsAttribute("att3")); // true
		System.out.println(model.containsAttribute("att4")); // false

		return "home";
	}

コメントで記載している値が標準出力に出力される。

↓↓↓ Spring Framework 関連の記事をまとめました。
yyama1556.hateblo.jp

今日はここまで。

わかりやすいJava EE ウェブシステム入門

わかりやすいJava EE ウェブシステム入門