Struts2のActionとインターセプターのAOP部分を見る
前回のAOPの定番 Spring Frameworkの動的プロキシ部分を見るをふまえて、Struts2のAOP部分をStruts 2.0.11のサンプル「blank」を例に見てみます。
サンプルの「blank」のweb.xmlを見ると、以下のように設定してありますので、リクエスト毎にorg.apache.struts2.dispatcher.FilterDispatcherのdoFilterメソッドが呼び出されるのがわかります。
<filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class> </filter>
1).そのorg.apache.struts2.dispatcher.FilterDispatcher(419行目)#doFilter()では、以下の呼び出しがあります。
419 dispatcher.serviceAction(request, response, servletContext, mapping);
※FilterDispatcherはjavax.servlet.Filterを実装したクラスです。
2).このdispatcherは、org.apache.struts2.dispatcher.Dispatcherであり、そのorg.apache.struts2.dispatcher.Dispatcher(504行目)#serviceAction()では、以下の呼び出しがあります。
504 proxy.execute();
※serviceActionメソッドは、ActionProxyを生成し、ActionProxyのexecuteメソッドを実行します。
3).このproxyは、org.apache.struts2.impl.StrutsActionProxyであり、そのorg.apache.struts2.impl.StrutsActionProxy(52行目)#execute()では、以下の呼び出しがあります。
52 return invocation.invoke();
4).このinvocation.invoke()は、com.opensymphony.xwork2.DefaultActionInvocation(210行目)#invoke()であり、そのcom.opensymphony.xwork2.DefaultActionInvocation(224行目)#invoke()は、以下のように書かれています。
210 public String invoke() throws Exception { 211 String profileKey = "invoke: "; 212 try { 213 UtilTimerStack.push(profileKey); 214 215 if (executed) { 216 throw new IllegalStateException("Action has already executed"); 217 } 218 219 if (interceptors.hasNext()) { 220 final InterceptorMapping interceptor = (InterceptorMapping) interceptors.next(); 221 UtilTimerStack.profile("interceptor: "+interceptor.getName(), 222 new UtilTimerStack.ProfilingBlock<String>() { 223 public String doProfiling() throws Exception { 224 resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this); 225 return null; 226 } 227 }); 228 } else { 229 resultCode = invokeActionOnly(); 230 } ・ ・ ・
この辺で、前回AOPの定番 Spring Frameworkの動的プロキシ部分を見ると同じようなロジックがでてきました。
上記の、224行目で自インスタンスを引数にインターセプターのinterceptメソッドを呼び出しています。
ここで呼び出されるインターセプターのサンプルは、org.apache.struts2.interceptor.ServletConfigInterceptorを見てみましょう。
org.apache.struts2.interceptor.ServletConfigInterceptor(125行目)#intercept()は、以下のように書かれています。
125 public String intercept(ActionInvocation invocation) throws Exception { 126 final Object action = invocation.getAction(); 127 final ActionContext context = invocation.getInvocationContext(); 128 129 if (action instanceof ServletRequestAware) { 130 HttpServletRequest request = (HttpServletRequest) context.get(HTTP_REQUEST); 131 ((ServletRequestAware) action).setServletRequest(request); 132 } 133 134 if (action instanceof ServletResponseAware) { 135 HttpServletResponse response = (HttpServletResponse) context.get(HTTP_RESPONSE); 136 ((ServletResponseAware) action).setServletResponse(response); 137 } 138 139 if (action instanceof ParameterAware) { 140 ((ParameterAware) action).setParameters(context.getParameters()); 141 } 142 143 if (action instanceof RequestAware) { 144 ((RequestAware) action).setRequest((Map) context.get("request")); 145 } ・ ・ ・ 170 return invocation.invoke(); 171 }
org.apache.struts2.interceptor.ServletConfigInterceptorの設定は、デフォルトでstruts-default.xmlに記述してあるので、特に設定なしでActionが動作する毎に動作します。
あとは前回AOPの定番 Spring Frameworkの動的プロキシ部分を見る同様、4).com.opensymphony.xwork2.DefaultActionInvocation#invokeメソッドは自インスタンスを引数にインターセプターのintercept()を呼び出す。
それぞれのインターセプターはintercept()の中で実行対象メソッドの前後処理を行い、引数で受け取ったDefaultActionInvocationのinvoke()を呼び出す。
つまり、invokeメソッドとinterceptメソッドが連鎖することになります。
このServletConfigInterceptor#interceptは、対象のActionクラスがどのインターフェイスを実装しているかによって、処理を行います。(ActionがRequestAwareを実装してれば、ActionのsetRequestメソッドでリクエストマップを設定するなど。)