はじめに
これら2つのフレームワークの異常な統合に関する私の経験を共有したいと思います。 「なぜJSFが必要なのか」という非常に重要な問題に触れたいとは思いません。
かなり長い間、動物園アプリケーションは、Spring + Hibernate +多数のPL / SQLファイルとOracleパッケージで開発されました。 ユーザーインターフェイスは、自作のJavaScriptとHTMLを使用する場所で、第4および第2バージョンのExtJSで作成されました。 一般的に、通常の企業フランケンシュタイン。 不可抗力の状況により、JSFを使用して一部のインターフェイスを作成する必要があったため、JSFはSpring MVCに基づく既存のリクエスト処理システムに統合する必要があります。 Primefacesを使用しましたが、残りの実装にも同じ方法が適用されると思います。
明らかなこと
まず、web.xmlファイルに別個のサーブレットを作成します。
<servlet> <servlet-name>JSF</servlet-name> <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>JSF</servlet-name> <url-pattern>*.xhtml</url-pattern> </servlet-mapping>
また、faces-config.xmlファイルにSpringサポートを追加して、xhtmlオブジェクトのスプリングアノテーションによって作成されたBeanを使用できるようにします。
<application> <el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver> </application>
ラップリクエスト
これまでは、すべてが非常にシンプルで、ほぼどこでも説明されています。 次の問題を解決する必要があります。
- JSFに春を通じてgetおよびpostリクエストを提供します。
- トランザクションサポートを追加します(Hibernateの内部には遅延コレクションがあります)
- ビューの有効期間でBeanを作成する
- xhtmlファイルへのユーザーアクセスを拒否する
最初の2つのポイントは同時に解決できます。 このアプリケーションでは、JSFインターフェースが作成されるすべてのURLは「/ app / * / jsf / *」という形式です。
@Controller("") @RequestMapping("/app/*/jsf") public class JSFTestController { @RequestMapping(value = "/*", method = {RequestMethod.GET}) @Transactional(rollbackFor = Exception.class) public void redirectToJSF(HttpServletRequest request, HttpServletResponse response) throws Exception { String uri = request.getRequestURI(); // RequestContextHolder.getRequestAttributes().setAttribute(JSF_REQUEST_URL, uri, RequestAttributes.SCOPE_REQUEST); String xhtmlPath= getXHTMLPath(uri); // xhml , // , request.getRequestDispatcher(xhtmlPath).forward(request, response); } // public static String getURLFromRequest(HttpServletRequest request) { return (String) RequestContextHolder.getRequestAttributes().getAttribute(JSF_REQUEST_URL, RequestAttributes.SCOPE_REQUEST); }
getXHTMLPathメソッドは、xhtmlファイルへのパスを拡張します(例: /resources/jsf/accounts/find_create.xhtml)。[warfile] \ resources \ jsf \ accounts \ find_create.xhtmlにあるファイルにアクセスします。
レンダリングされたxhtmlページを表示するにはこれで十分ですが、PrimeFacesはインターフェイスを構築するためのPOSTリクエストが豊富であり、明らかに間違ったアドレスに移動します。 フォームレンダラーは、リクエストのPOSTパス(つまり、フォームのアクション属性の値)の形成を担当します。 静的関数getActionStrを 1つだけ変更する必要がありますが、残念ながら静的にされたため、それを呼び出す関数をブロックする必要があります。
public class ActCorrectFormRenderer extends FormRenderer { private static final com.sun.faces.renderkit.Attribute[] ATTRIBUTES = AttributeManager.getAttributes(AttributeManager.Key.FORMFORM); private boolean writeStateAtEnd; public ActCorrectFormRenderer() { WebConfiguration webConfig = WebConfiguration.getInstance(); writeStateAtEnd = webConfig.isOptionEnabled(WebConfiguration.BooleanWebContextInitParameter.WriteStateAtFormEnd); } @Override public void encodeBegin(FacesContext context, UIComponent component) throws IOException { rendererParamsNotNull(context, component); if (!shouldEncode(component)) { return; } ResponseWriter writer = context.getResponseWriter(); assert (writer != null); String clientId = component.getClientId(context); // since method and action are rendered here they are not added // to the pass through attributes in Util class. writer.write('\n'); writer.startElement("form", component); writer.writeAttribute("id", clientId, "clientId"); writer.writeAttribute("name", clientId, "name"); writer.writeAttribute("method", "post", null); writer.writeAttribute("action", getActionStr(context), null); String styleClass = (String) component.getAttributes().get("styleClass"); if (styleClass != null) { writer.writeAttribute("class", styleClass, "styleClass"); } String acceptcharset = (String) component.getAttributes().get("acceptcharset"); if (acceptcharset != null) { writer.writeAttribute("accept-charset", acceptcharset, "acceptcharset"); } RenderKitUtils.renderPassThruAttributes(context, writer, component, ATTRIBUTES); writer.writeText("\n", component, null); // this hidden field will be checked in the decode method to // determine if this form has been submitted. writer.startElement("input", component); writer.writeAttribute("type", "hidden", "type"); writer.writeAttribute("name", clientId, "clientId"); writer.writeAttribute("value", clientId, "value"); writer.endElement("input"); writer.write('\n'); // Write out special hhidden field for partial submits String viewId = context.getViewRoot().getViewId(); String actionURL = context.getApplication().getViewHandler().getActionURL(context, viewId); ExternalContext externalContext = context.getExternalContext(); String encodedActionURL = externalContext.encodeActionURL(actionURL); String encodedPartialActionURL = externalContext.encodePartialActionURL(actionURL); if (encodedPartialActionURL != null) { if (!encodedPartialActionURL.equals(encodedActionURL)) { writer.startElement("input", component); writer.writeAttribute("type", "hidden", "type"); writer.writeAttribute("name", "javax.faces.encodedURL", null); writer.writeAttribute("value", encodedPartialActionURL, "value"); writer.endElement("input"); writer.write('\n'); } } if (!writeStateAtEnd) { context.getApplication().getViewHandler().writeState(context); writer.write('\n'); } } private static String getActionStr(FacesContext context) { return JSFTestController.getURLFromRequest(HttpUtils.getCurrentRequest()); } }
これは不便ですが、静的関数を変更する他のオプションはありません。 しかし、この方法では、フォームのアクションを変更することが可能であり、投稿リクエストはページのビューを構築した取得リクエストと同じアドレスに送られます。 faces-configファイルにレンダラーを登録することは残ります。
<render-kit> ... <renderer> <component-family>org.primefaces.component</component-family> <renderer-type>org.primefaces.component.MenuRenderer</renderer-type> <renderer-class>com.XXXXX.ActCorrectMenuRenderer</renderer-class> </renderer> ... </render-kit>
アプリケーションはGETリクエストとPOSTリクエストを処理する際に少し異なるチェックを使用するため、メソッドは分離されますが、簡単な例では、redirectToJSFメソッドに@RequestMapping(value = "/ *"、method = {RequestMethod.GET、RequestMethod.POST})の注釈を付けることができます
「表示」スコープ
JSFでは、ビュー内に存在するBean、つまり すべてのGETリクエストで生成され、このGETリクエストで生成されたページからのPOSTリクエストで有効になります。 車輪を再発明することはせず、ここでクラスのコードを借ります: forum.springsource.org/showthread.php?80595-View-scope-with-Spring
安全性
アプリケーションは1つのブラウザーから複数のユーザーの下で同時に動作でき、セッションGUIDがURLに追加されたため、xhtmlファイルへの直接アクセスは許可されないためブロックされ(ログインフォームはJSFなしで作成された)、それを引き出します(またはJSFにユーザーのリクエストを直接処理させる)次の形式のセッションGUIDを持つ要求:
localhost:8443 / <アプリケーション名> /app/ea10efcb-4a2e-4eeb-aa71-24310882f7ad/jsf/accounts/find_create.xhtml
ファイルがないため失敗します[xxx.war] /app/ea10efcb-4a2e-4eeb-aa71-24310882f7ad/jsf/accounts/find_create.xhtml
さらに、次のフィルターがインストールされました。
- "/javax.faces.resource/**"-空のフィルター(申し訳ありませんが、必要なものを使用してください)
- /テーマ/ **-空のフィルター
- / ** /リソース/ jsf / **-認証チェック付き
おわりに
さて、ここでは、JSFを複雑なアプリケーションに追加するという比較的痛みのない(プログラムにとって、開発者にとって、多くのポイントが非常に苦痛だった)主要なポイントを調べました。 舞台裏では、POST要求時の例外処理、パフォーマンスを最適化するためのビンへのランダムコンポーネント呼び出しとの戦いについての質問がありましたが、これは別の記事のトピックです。