ホームページ >Java >&#&チュートリアル >独立したデータベースにアクセスするための中間サービスを作成する
会社のビジネスの継続的な変化に伴い、数年前のプロジェクト A と基盤となる DB_A データベースは、コア ビジネス サービスとコア データベースに変換されました。
DB_A データベースからデータを取得したい Web サービスがますます増えており、プロジェクト間の関係は徐々に次のように進化しています:
開発傾向を確認するのは簡単です上の写真のように、多くの問題があります(プロジェクトの関係は個人によって抽象化された簡略化されたバージョンであり、実際の状況はこれよりはるかに複雑です)。
a. webappAの実行中に例外が発生してアクセスできない場合、webappB/webappC .... DB_Aのデータは正常に取得できますか?
b. DB_A データを取得するために webappB/webappC に提供されるさまざまなサービスは webappA に無限に拡張されます。
c. 実行プロセス中、webappA プロジェクトは通常どおりユーザーに独自のサービスを提供するだけでなく、他のプロジェクトのデータ取得要求も考慮する必要があり、これにより必然的にパフォーマンスのボトルネックが発生します。
これらの問題のいくつかは、プロジェクトのオンライン推進中にすでに発生しています。メンテナンスによるダウンタイムが時折発生し、プロジェクト チームの顔に大声で平手打ちを食らうことになるのは、本当に不快です。
余談: 現在のインターネットの発展速度とさまざまな企業の事業拡大によれば、2 年以内にプロジェクトの発展方向を正確に予測し、事前に拡張を準備できるアーキテクトはすでに非常に優れています。
プロジェクト チームの誰かが、プロジェクト webappA をバイパスし、残りの webappB/webappC... を直接 DB_A に接続して対話することを提案しましたが、すぐに拒否されました (プロジェクトごとにデータベース アクセス層を再定義して記述する必要があります) )。
データベースアクセス層を分離して、認可されたアイテムにアクセスできるようにするサービスとして使用できますか?以下の通り:
中心となるアイデアは、無制限の数の N wabapp に対して特定のデータベースへのアクセスを提供することです。これにより、プロジェクト間の結合が回避されるだけでなく、データ アクセス層の再利用率も向上します。
あなたはすでにアイデアを持っています、始めましょう、BB は問題を解決できません。 組み立てに約 2 日かかり、無数の穴を埋め、最終的には期待どおりのものが完成しました。
元のプロジェクトは商用利用のためオープンソース化できません。デモを再構成した後、オープンソース化されました。
この分野で練習する必要がある学生の場合、クローンを作成してローカルで実行すると、すべてが明確になります。
dao-service-impl サービスにアクセスするには、DB_A データ プロジェクトが dap-service-api に依存する必要があります。
dao-service-api は外層に提供されるインターフェースであり、最終的なプレゼンテーションメソッドは jar であり、Maven プロジェクトはこれに直接依存できます。
古い非 Maven プロジェクトがある場合は、maven-jar-plugin/maven-assembly-plugin を使用してすべての依存する jar をアセンブルし、プロジェクト ライブラリに追加します。
dao-service-impl は、cxf + spring + druid + jpa (hibernate impl) オープンソース クラス ライブラリから構築された純粋なバックエンド コンポーネント サービスです。
サービスインターフェイスの実装層としての最終的なプレゼンテーション方法は war であり、クラスターにデプロイすることも、他のプロジェクトにサービスを提供するために分散することもできます。
ディレクトリ構造は一目瞭然で、簡単なコード生成(GenCodeServlet)が実装されており、dao層+webService層のインターフェースと実装を自動生成できます。
webService はレイヤー インジェクション dao レイヤー インターフェイスを実装し、単一のテーブルを追加、削除、変更、確認する 5 つのメソッドをカプセル化します。一般的に、冗長なメソッドを記述する必要はなく、SQL の 90% を記述する必要はありません。
@WebService @SOAPBinding(style = SOAPBinding.Style.RPC)public interface UserWs {/** * 通过 ID 过去单个 User 实体对象 * cxf 传输返回对象不可为null,Dao 层获取为null时 * 实例化返回空对象,判空时使用对象主键进行判断即可 * * @param id 主键ID */UserPO getUser(String id);/** * 通过类似的 PO 获取多个 User 实体对象 * * @param userPO 对照的实体对象 */List<UserPO> listUser(UserPO userPO);/** * 通过类似的 PO 获取多个 User 实体对象 * * @param userPO 对照的实体对象 * @param orderby 排序字段 * @param asc 是否升序 */List<UserPO> listUserOrdrBy(UserPO userPO, String orderby, Boolean asc);/** * 新增 User 实体对象 * * @param userPO 要新增的对象 */UserPO addUser(UserPO userPO);/** * 更新 User 实体对象 * * @param userPO 要更新的对象 */UserPO updateUser(UserPO userPO); }
開発方法は単純かつ大雑把ですが、ツールを使ってHibernateデータベースpoを逆生成し、GenCodeServletにアクセスしてdao/ws層のインターフェースと実装を生成します。
構成ファイルのオプションを追加し、cxf webService サービスを公開します。これには 5 分もかかりません。
公開された単一テーブル サービスは、呼び出し元のデータベース アクセス層として理解され、ビジネス ロジックを結合するためにプロジェクトによって指定された場所に挿入されます。
このモジュールの目的は、cxf によって公開されたサービスを統合する方法を示すことです。
a. Spring は呼び出し側プロジェクトに統合されています (dao-service-api に依存します)
<jaxws:client id="UserWs" serviceClass="com.rambo.dsd.sys.ws.inter.UserWs" address="${cxf.server.url}/UserWs"><jaxws:outInterceptors><ref bean="wss4JOutInterceptor"/></jaxws:outInterceptors></jaxws:client>
具体的な使用方法 (Spring の注入を前提としています)
Map<String, Object> map = new HashMap<>(); UserWs userWs = (UserWs) SpringContextUtil.getBean("UserWs"); UserPO user = userWs.getUser("031e7a36972e11e6acede16e8241c0fe"); map.put("1.获取单个用户:", user); user.setPhone("18975468245"); UserPO userPO1 = userWs.updateUser(user); map.put("2.更新单个用户:", userPO1); UserPO userPO2 = new UserPO(); userPO2.setName("rambo"); userPO2.setPasswd(SecurityUtil.encryptMD5("123456")); userPO2.setSex("男"); userPO2.setYxbz("Y"); UserPO userPO3 = userWs.addUser(userPO2); map.put("3.新增单个用户:", userPO3); UserPO userPO4 = new UserPO(); userPO4.setSex("男"); List<UserPO> userPOList = userWs.listUser(userPO4); map.put("4.获取所有的男用户:", userPOList); UserPO userPO5 = new UserPO(); userPO5.setSex("男"); List<UserPO> userPOList1 = userWs.listUserOrdrBy(userPO5, "sorts", true); map.put("5.获取所有的男用户并按照 sorts 字段排序:", userPOList1);return map;
b. Spring は呼び出し元プロジェクトに統合されていません (dao-service-api に依存します)
ツールまたはコマンドを使用して cxf サービス クライアントを生成し、使用されているサービス インスタンスを取得するためのファクトリー モードを導入します。それはそれでです。
UserWsImplService userWsImplService = new UserWsImplService(new URL(cxfServerUrl + "/UserWs?wsdl")); UserWs userWs = userWsImplService.getUserWsImplPort(); addWSS4JOutInterceptor(userWs); UserPO user = userWs.getUser("031e7a36972e11e6acede16e8241c0fe"); map.put("1.获取单个用户:", user); user.setPhone("18975468245"); UserPO userPO1 = userWs.updateUser(user); map.put("2.更新单个用户:", userPO1); UserPO userPO2 = new UserPO(); userPO2.setUuid(StringUtil.getUUID()); userPO2.setName("rambo"); userPO2.setPasswd(SecurityUtil.encryptMD5("123456")); userPO2.setSex("男"); userPO2.setYxbz("Y"); UserPO userPO3 = userWs.addUser(userPO2); map.put("3.新增单个用户:", userPO3); UserPO userPO4 = new UserPO(); userPO4.setSex("男"); UserPOArray userPOArray1 = userWs.listUser(userPO4); map.put("4.获取所有的男用户:", userPOArray1); UserPO userPO5 = new UserPO(); userPO5.setSex("男"); UserPOArray userPOArray2 = userWs.listUserOrdrBy(userPO5, "sorts", true); map.put("5.获取所有的男用户并按照 sorts 字段排序:", userPOArray2.getItem());
結局のところ、cxfは外部に公開されたサービスであり、セキュリティは依然として非常に重要です。
セキュリティ認証が cxf ws-security wss4j インターセプター実装に導入され、SOAP ヘッダーに認証情報が追加されます。
a. サーバー構成
<!--服务端安全认证回调函数--><bean id="serverAuthCallback" class="com.rambo.dsd.base.handler.CXFServerAuthHandler"/><!--安全日志认证拦截器--><bean id="wss4JInInterceptor" class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor"><constructor-arg><map><entry key="action" value="UsernameToken"/><entry key="passwordType" value="PasswordDigest"/><entry key="passwordCallbackRef" value-ref="serverAuthCallback"/></map></constructor-arg></bean>
サーバーは javax.security.auth.callback.CallbackHandler のセキュリティ コールバック関数を実装します:
public class CXFServerAuthHandler implements CallbackHandler { protected final static Logger log = LoggerFactory.getLogger(CXFServerAuthHandler.class); private static final Map<String, String> userMap = new HashMap<String, String>(); static { userMap.put("webappA", "webappA2017"); userMap.put("webappB", "webappB2017"); } public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { for (Callback callback : callbacks) { WSPasswordCallback pc = (WSPasswordCallback) callback; String clientUsername = pc.getIdentifier(); String serverPassword = userMap.get(clientUsername); log.info(" client:{} is starting webservice...", clientUsername); int usage = pc.getUsage(); if (usage == WSPasswordCallback.USERNAME_TOKEN) { pc.setPassword(serverPassword); } else if (usage == WSPasswordCallback.SIGNATURE) { pc.setPassword(serverPassword); } } } }
b. Spring クライアントの構成を食べました
<!--客户端安全认证回调函数--><bean id="wsClientAuthHandler" class="com.rambo.dsc.handler.WsClientAuthHandler"/><!--安全认证对外拦截器--><bean id="wss4JOutInterceptor" class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor"><constructor-arg><map><entry key="action" value="UsernameToken"/><entry key="user" value="webappA"/><entry key="passwordType" value="PasswordDigest"/><entry key="passwordCallbackRef" value-ref="wsClientAuthHandler"/></map></constructor-arg></bean>
挿入された webService サービス構成インターセプター:
<jaxws:outInterceptors><ref bean="wss4JOutInterceptor"/></jaxws:outInterceptors>
クライアントは、javax.security.auth.callback.CallbackHandler のセキュリティ コールバック関数を実装します:
public class WsClientAuthHandler implements CallbackHandler { public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { for (Callback callback : callbacks) { WSPasswordCallback pc = (WSPasswordCallback) callback; pc.setPassword("webappA2017"); } } }
c .エンコーディングSpring 統合のないクライアント
private void addWSS4JOutInterceptor(Object wsClass) { Endpoint cxfEndpoint = ClientProxy.getClient(wsClass).getEndpoint(); Map outProps = new HashMap(); outProps.put(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN); outProps.put(WSHandlerConstants.USER,"webappA"); outProps.put(WSHandlerConstants.MUST_UNDERSTAND, "0"); outProps.put(WSHandlerConstants.PASSWORD_TYPE, "PasswordDigest"); outProps.put(WSHandlerConstants.PW_CALLBACK_CLASS, WsClientAuthHandler.class.getName()); cxfEndpoint.getOutInterceptors().add(new WSS4JOutInterceptor(outProps)); }
プロジェクトのサーバー側のセキュリティ認証は UsernameToken を使用します。もちろん、セキュリティ認証方法をカスタマイズすることもできます。
4. 結論
インターネット企業のサービスアーキテクチャは血統であり、習慣です。各企業には独自のルーチンと構造があり、細部は異なりますが、中心となる概念は同じです。
この実践には少しマイクロサービスの雰囲気がありますが、サービスの登録/ルーティング/フォールトトレランス/キャッシュなど、十分とは言えません...プロジェクトは他にもたくさんあります。オープンソース化されていますので、興味があれば一緒に改善してください。
以上が独立したデータベースにアクセスするための中間サービスを作成するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。