問題:#
- filterChain はいつ実行されるのですか?
- filterChain の filter はどこから来るのですか?
- standardContext はいつフィルタのコレクションを収集し始めるのですか?
1. filterChain はいつ実行されるのですか?#
org.apache.catalina.core.StandardWrapperValve
クラスには以下のようなメソッドがあります。
public final void invoke(Request request, Response response)
throws IOException, ServletException {
//大部分の無関係なコードを省略
Servlet servlet = null;
Context context = (Context) wrapper.getParent();
//大部分の無関係なコードを省略
try {
if (!unavailable) {
//1. サーブレットを作成
servlet = wrapper.allocate();
}
} catch(){
...
}
// 2. このリクエストのためのフィルターチェーンを作成
ApplicationFilterChain filterChain =
ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
Container container = this.container;
try {
if ((servlet != null) && (filterChain != null)) {
// 必要に応じて出力を飲み込む
if (context.getSwallowOutput()) {
try {
SystemLogHandler.startCapture();
if (request.isAsyncDispatching()) {
request.getAsyncContextInternal().doInternalDispatch();
} else {
//FilterChainオブジェクトのdoFilterメソッドを実行します。
filterChain.doFilter(request.getRequest(),
response.getResponse());
}
} finally {
}
} else {
if (request.isAsyncDispatching()) {
request.getAsyncContextInternal().doInternalDispatch();
} else {
filterChain.doFilter
(request.getRequest(), response.getResponse());
}
}
}
} catch(){
} finally {
// このリクエストのためのフィルターチェーンを解放します(あれば)
if (filterChain != null) {
filterChain.release();
}
// 割り当てられたサーブレットインスタンスを解放します
try {
if (servlet != null) {
wrapper.deallocate(servlet);
}
}
}
}
後でフィルターチェーンオブジェクトを作成するコードを見ますが、まずはフィルターチェーンを実行する方法を見てみましょう。フィルターチェーンオブジェクトの実行フローの実装:
public final class ApplicationFilterChain implements FilterChain {
//現在実行中のフィルタのインデックス
private int pos = 0;
//一致したフィルタの数
private int n = 0;
//サーブレット
private Servlet servlet = null;
//一致したフィルタの配列
private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];
@Override
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
//セキュリティが有効になっている場合
if( Globals.IS_SECURITY_ENABLED ) {
//... セキュリティビーンフィルタ
} else {
//フィルタロジックを実行
internalDoFilter(request,response);
}
}
private void internalDoFilter(ServletRequest request,
ServletResponse response)
throws IOException, ServletException {
//ループして次のフィルタを呼び出します
if (pos < n) {
//posインデックスを++
ApplicationFilterConfig filterConfig = filters[pos++];
try {
Filter filter = filterConfig.getFilter();
if( Globals.IS_SECURITY_ENABLED ) {
} else {
//フィルターチェーン内の現在のフィルタのdoFilter()を実行
filter.doFilter(request, response, this);
}
} catch(){
...
}
return;
}
// チェーンの終わりに落ちました -- サーブレットインスタンスを呼び出します
try {
if ((request instanceof HttpServletRequest) &&
(response instanceof HttpServletResponse) &&
Globals.IS_SECURITY_ENABLED ) {
} else {
//実行するフィルタがない場合、サーブレットのserviceメソッドが実行され、私たちが書いたビジネスロジックが含まれます。
servlet.service(request, response);
}
}catch(){
...
}finally{
...
}
}
}
filterChain クラスのソースコードから、底層には一致したフィルタの配列が含まれており、追加されたフィルタオブジェクトは順序付けられています。追加する際に決定されます。
では、いつ追加されるのでしょうか?
2. filterChain の filter はどこから来るのですか?#
実際には、org.apache.catalina.core.StandardWrapperValve
クラスの invoke メソッド内で、
ApplicationFilterChain filterChain =
ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
public static ApplicationFilterChain createFilterChain(ServletRequest request,
Wrapper wrapper, Servlet servlet) {
// 実行するサーブレットがない場合、nullを返します
if (servlet == null)
return null;
// フィルターチェーンオブジェクトを作成して初期化します
ApplicationFilterChain filterChain = null;
if (request instanceof Request) {
Request req = (Request) request;
if (Globals.IS_SECURITY_ENABLED) {
// セキュリティ: リサイクルしない
filterChain = new ApplicationFilterChain();
} else {
filterChain = (ApplicationFilterChain) req.getFilterChain();
if (filterChain == null) {
filterChain = new ApplicationFilterChain();
req.setFilterChain(filterChain);
}
}
} else {
// リクエストディスパッチャーが使用されています
filterChain = new ApplicationFilterChain();
}
filterChain.setServlet(servlet);
filterChain.setServletSupportsAsync(wrapper.isAsyncSupported());
// このコンテキストのフィルターマッピングを取得します
StandardContext context = (StandardContext) wrapper.getParent();
//ServletContextオブジェクトに登録されたすべてのフィルタの配列を取得します
FilterMap filterMaps[] = context.findFilterMaps();
// フィルターマッピングがない場合、終了します
if ((filterMaps == null) || (filterMaps.length == 0))
return filterChain;
// フィルターマッピングを一致させるために必要な情報を取得します
DispatcherType dispatcher =
(DispatcherType) request.getAttribute(Globals.DISPATCHER_TYPE_ATTR);
String requestPath = null;
Object attribute = request.getAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR);
if (attribute != null){
requestPath = attribute.toString();
}
String servletName = wrapper.getName();
// このフィルターチェーンに関連するパスマッピングされたフィルタを追加します
for (int i = 0; i < filterMaps.length; i++) {
if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
continue;
}
if (!matchFiltersURL(filterMaps[i], requestPath))
continue;
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
context.findFilterConfig(filterMaps[i].getFilterName());
if (filterConfig == null) {
// FIXME - 設定の問題をログに記録
continue;
}
filterChain.addFilter(filterConfig);
}
// サーブレット名に一致するフィルタを2番目に追加します
for (int i = 0; i < filterMaps.length; i++) {
if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
continue;
}
if (!matchFiltersServlet(filterMaps[i], servletName))
continue;
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
context.findFilterConfig(filterMaps[i].getFilterName());
if (filterConfig == null) {
// FIXME - 設定の問題をログに記録
continue;
}
filterChain.addFilter(filterConfig);
}
// 完成したフィルターチェーンを返します
return filterChain;
}
フィルターチェーンオブジェクトを作成する際に、ServletContext から登録されたすべてのフィルタの配列を取得し、必要なものをこのリクエストで作成されたフィルターチェーンオブジェクトに追加することがわかります。また、servletContext オブジェクトに登録されたすべてのフィルタは元々配列であり(順序付けられています)、したがってマッチングを遍歴する際も順序付けられています。
3. ServletContext はいつから配列を収集し始めるのですか?#
filterMaps は StandardContext クラス内にあります。
public class StandardContext extends ContainerBase
implements Context, NotificationEmitter {
private final ContextFilterMaps filterMaps = new ContextFilterMaps();
//
@Override
public void addFilterMap(FilterMap filterMap) {
validateFilterMap(filterMap);
// このフィルターマッピングを登録されたセットに追加します
filterMaps.add(filterMap);
fireContainerEvent("addFilterMap", filterMap);
}
}
Tomcat を起動すると、スタックを確認して呼び出し順序を確認できます。
StandardContext.startInternal()
-->fireLifecycleEvent()
-->ContextConfig.lifecycleEvent()
-->ContextConfig.lifecycleEventcon
-->ContextConfig.con.figureStart()
-->ContextConfig.webConfig()
メソッドを確認します。
protected void webConfig() {
if (ok) {
configureContext(webXml);
}
}
private void configureContext(WebXml webxml) {
for (FilterMap filterMap : webxml.getFilterMappings()) {
context.addFilterMap(filterMap);
}
}
ここで web.xml からすべてのフィルタのセットを収集し、収集したフィルタのセットを配列に変換して StandardContext オブジェクトに設定します。