やって・・・
今日は恐ろしく眠い。すぐに寝よう。
でも「AOPをちょっとやってみた」のと「ToStringBuilder」を使うためCommonsLangを導入した。
自作Interceptor
先に書いたが、JDBCRealmを使っていて、/schedule/* にアクセスされたものは、いったんFORM認証の画面が表示されて認証成功したら、当初のURLにいっちゃうわけである。つまり、
今までは、index() の頭にユーザネームを取得して、UserService 経由でユーザ情報(UserDto )を取得する処理を書いていた。こんな感じ。
public class ScheduleAction { public UserDto userDto; public UserService userService; public HttpServletRequest request; private static Logger logger = Logger.getLogger(ScheduleAction.class); @Execute(validator = false) public String index() { if (userDto.id == null) { Principal userPrincipal = request.getUserPrincipal(); logger.debug("Logged User is " + userPrincipal.getName()); User user = userService.getUserDto(userPrincipal.getName()); Beans.copy(user, userDto).execute(); } :
と、まあこんな感じ。このコードをメソッドごとに複数書くのもアレだし、管理用の画面である/manage/*にも同じコードを書くのはもっとアレなので、
- 認証後自動的にPrincipalからユーザ情報(UserDto)を取得する
インターセプターを作ってみた。*1
public class UserDtoAutoRetrieveInterceptor extends AbstractInterceptor { private static final long serialVersionUID = 1L; public UserService userService; public UserDto userDto; protected S2Container container; public Object invoke(MethodInvocation invocation) throws Throwable { HttpServletRequest request = (HttpServletRequest) container.getExternalContext().getRequest(); Principal userPrincipal = request.getUserPrincipal(); User user = userService.getUserDto(userPrincipal.getName()); Beans.copy(user, userDto).execute(); System.out.println("###" + userDto); return invocation.proceed(); } public S2Container getContainer() { return this.container; } public void setContainer(S2Container container) { this.container = container.getRoot(); } }
あと、app.diconにコンポーネントの定義を登録して、customizer.diconのActionのほうに
<initMethod name="addAspectCustomizer"> <arg>"UserDtoAutoRetrieveInterceptor"</arg> </initMethod>
を設定した。
実はまだAOPは勉強しはじめなので、よそ様のコードの見よう見真似でわけもわからず作っているところがある。Containerのgetter/setterは本当にいるのかどうかもよくわかっていない。それはともかくだ。インターセプターでも
public UserService userService; public UserDto userDto;
をインジェクションしてくれるのには感動モノだったが、さきほどみせたActionクラスのフィールドに定義している userDtoの参照は、この自作Interceptorで取得したuserDtoの参照とは異なるようで、目論見はうまくいかなかった。
両方、DIコンテナで管理してくれてるものなんだろうけどなぁ。
ToStringBuilder
@Override public String toString() { return ToStringBuilder.reflectionToString( this, ToStringStyle.MULTI_LINE_STYLE).toString(); }
と書くと、
kizashi.scheduler.dto.UserDto@886462[
id=1
loginId=foo
passwd=bar
lastName=yamada
firstName=taro
mailAddress=hoge@huga.huga
birthday=
createdAt=2008-06-20 22:59:19.0
updatedAt=2008-06-20 22:59:12.0
createdBy=creater
updatedBy=updater
]
と、いい感じにしてくれる。
昔、toStringをがんばって実装している人たちがいたなぁ。とくにフィールドがあほほど多いクラスのtoStringメソッドは切なかった。
今日は眠いのでここまで。
SeasarをわからずにSAStrutsを使い始めたが、Seasarの(というかDIxAOPの)勉強になってよいわ。楽しい。
*1:もしこれがアプリで認証する場合だと、Loginしているかどうかを毎回チェックするインターセプターが必要になりそう。