フィールド・アノテーション

#Sylvesterはこちらからダウンロードできます。

メソッドにアノテーションを設定するのはできたので今度はフィールドに対して設定できるようにしてみる。

とりあえずこんな感じのWebアプリを作る。

  • テキストボックスに入力した文字列を出力する
  • クライアントスコープ(複数のユーザーで共有できない)のアクセスカウンタ

今回はクライアント側から。

アクションクラス

こんな感じにシンプルに実装。

public class Input {

	@Attribute
	private String msg;
	
	@Attribute
	private int count;
	
	public void disp() {
		
		System.out.println("Method 'disp' called.");
		System.out.println("msg  : " + msg);
		System.out.println("cnt  : " + count);

		count ++;
	}
}

@Attributeを付加することで、フレームワークが自動的に設定を行ってくれる。disp()はTapestryでいうアクションリスナーみたいな感じ(というほど大げさなものでもないけど)。

Attributeアノテーション

@Retention(RetentionPolicy.RUNTIME)をつけないと実行時に情報を読めないので必須。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Attribute {

	String name() default ""; // デフォルト時はフィールド名がキーになる
}
テンプレート

やりたいようにコーディング

<html lang="ja">
	<head>
		<meta http-equiv="content-type" content="text/html">
		<title>Welcome!</title>
	</head>
	<body>
        <p>
            ${count}
        </p>
        <p>
            ${hoge.name}
        </p>
        <Form Action="?action=disp" Method="POST">
            
            <Input Type="text" name="msg" value="${msg}" />
            <input type="hidden" name="count" value="${count}" />
            <Input Type="submit" name="button" value="write" />
            
        </Form>
        <p>
            ${msg}
        </p>
	</body>
</html>

フレームワーク側の仕組み

フィールド・アノテーションを読み取るロジックはこんな感じ。

リクエスト変数 > アクションクラスのフィールド
for (Field field : obj.getClass().getDeclaredFields()) {
	
	field.setAccessible(true);
	Attribute a = field.getAnnotation(Attribute.class);
	if (a == null) {
		
		continue;
	}
	String valueKey = a.name();
	if (valueKey.equals("")) {
		
		valueKey = field.getName();
	}
	try {
		
		Object value = req.getParameter(valueKey);
		if (field.getType() == int.class) {
			
			value = value == null ? 0 : Integer.parseInt(value.toString());
		}
		field.set( obj, value );
		
	} catch (IllegalArgumentException e) {
		e.printStackTrace();
	} catch (IllegalAccessException e) {
		e.printStackTrace();
	}
}
アクションクラスのフィールド > リクエスト変数
for (Field field : obj.getClass().getDeclaredFields()) {

	field.setAccessible(true);
	Attribute a = field.getAnnotation(Attribute.class);
	if (a == null) {
		
		continue;
	}
	String valueKey = a.name();
	if (valueKey.equals("")) {
		
		valueKey = field.getName();
	}
	try {
		
		Object value = field.get( obj );
		req.setAttribute(valueKey, value == null ? "" : value);
			
	} catch (IllegalArgumentException e) {
		e.printStackTrace();
	} catch (IllegalAccessException e) {
		e.printStackTrace();
	}
}
アクションリスナーを呼び出す

ちょっといいかげんに、パラメータで「action=メソッド名」できたものをメソッドを呼び出すといった感じ。

private void doAction(Object obj, String action) {

	if (action == null) {
		
		return;
	}

	try {
		Method method = obj.getClass().getDeclaredMethod(action, new Class[0]);
		try {
			Object[] param = null;
			method.invoke(obj, param);
			
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
		
	} catch (SecurityException e) {
		e.printStackTrace();
	} catch (NoSuchMethodException e) {
		e.printStackTrace();
	}
}


あとはhttp://localhost:8080/sylvester/Input.doで起動させる。

ここにファイル一式おいてるので興味があれば試してみてください。http://coolstyle.dyndns.org/opensource/files/Sylvester.zip