NC前端衆所周知 一直採起的 Swing方式。前端
在前端經過Java的EJB協議和LOOKUP命名註冊概念管理遠程 RMI調用接口的交互。java
那麼當咱們在 swing界面 點擊某個按鈕或操做 他到底如何和後端的服務器進行交互呢?git
這裏咱們 須要先調試 定位前端的 請求類:後端
1. 前端交互前 獲取 RMI接口實現類的標準步驟:api
private IPFWorkflowQry ipflowqry = (IPFWorkflowQry)NCLocator.getInstance().lookup(IPFWorkflowQry.class.getName());
讓咱們在Swing調試下,看看 lookup到底作了什麼操做:緩存
咱們發現swing找到是這個 nc.bs.framework.comn.cli.ClientNCLocator 的實例 而後這個實例調用 服務器
nc.bs.framework.comn.cli.RemoteResolverImpl#lookup 方法進行查找app
而後第一次查找會委託爲 nc.bs.framework.comn.cli.ServiceProxyFactoryImpl#create(java.lang.Class[], java.lang.String, nc.bs.framework.comn.cli.ClientCommunicator, java.lang.String, java.lang.String) 方法建立代理對象ide
public Object create(Class[] api, String url, ClientCommunicator cc, String module, String name) { ClientProxy handler = new ClientProxyImpl(url, cc, module, name); return Proxy.newProxyInstance(api[0].getClassLoader(), api, handler); }
這裏咱們已經看到了 套路了, 就是JDK Proxy 對接口進行JVM內存匿名類代理你的方法請求。那麼他的代理幹了啥呢?學習
讓咱們看看 nc.bs.framework.comn.cli.ClientProxyImpl#invoke 方法:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { long nowTime = System.currentTimeMillis(); try { Logger.debug(enterMethodFormat.format(new Object[]{methodToString(method)})); String methodName = method.getName(); Class[] params = method.getParameterTypes(); Integer var10; if (methodName.equals("equals") && params.length == 1 && params[0].equals(Object.class)) { Object value = args[0]; if (value == null || !Proxy.isProxyClass(value.getClass())) { Boolean var41 = Boolean.FALSE; return var41; } Class[] proxyItfs = proxy.getClass().getInterfaces(); Class[] paraItfs = value.getClass().getInterfaces(); InvocationHandler paraHandler = Proxy.getInvocationHandler(value); if (paraHandler instanceof ClientProxyImpl && proxyItfs.length == paraItfs.length) { ClientProxyImpl argHandler = (ClientProxyImpl)paraHandler; Boolean var43; if (!this.name.equals(argHandler.name)) { var43 = Boolean.FALSE; return var43; } if (this.module == null && argHandler.module != null) { var43 = Boolean.FALSE; return var43; } if (!this.module.equals(argHandler.module)) { var43 = Boolean.FALSE; return var43; } for(int i = 0; i < proxyItfs.length; ++i) { if (proxyItfs[i] != paraItfs[i]) { Boolean var15 = Boolean.FALSE; return var15; } } var43 = new Boolean(this.dispatchURL.equals(argHandler.dispatchURL)); return var43; } } else { if (methodName.equals("hashCode") && params.length == 0) { Class[] proxyItfs = proxy.getClass().getInterfaces(); var10 = new Integer(this.dispatchURL.hashCode() + 27 * proxyItfs.hashCode()); return var10; } if (methodName.equals("toString") && params.length == 0) { String var9 = "<" + this.dispatchURL + ">" + "<" + this.module + ">" + "<" + this.name + ">"; return var9; } } RemoteCallStatistic.inc(); InvocationInfo ii = this.getInvocationInfo(proxy, method, args); var10 = null; try { if (RuntimeEnv.getInstance().isRunningInServer()) { ThreadTracer.getInstance().updateEvent("begin call master,methodname=" + methodName); } } catch (Exception var33) { Logger.error(var33.getMessage()); } Result result; try { result = this.urlCall(ii); } catch (Throwable var34) { if (var34 instanceof Error) { throw (Error)var34; } if (var34 instanceof RuntimeException) { throw (RuntimeException)var34; } throw new ConnectorException("Remote Call Exception", var34); } if (result == null) { throw new ConnectorException("cann't get result!"); } else if (result.appexception != null) { throw result.appexception; } else { Object var42 = result.result; return var42; } } finally { RemoteCallStatistic.dec(); Logger.debug(leaveMethodFormat.format(new Object[]{methodToString(method), new Long(System.currentTimeMillis() - nowTime)})); if (RuntimeEnv.getInstance().isRunningInServer()) { try { ThreadTracer.getInstance().updateEvent("end call master"); } catch (Exception var32) { Logger.error(var32.getMessage()); } } } }
能夠看到 就是個 簡單的java http請求調用,封裝了一個 vo 推送到了後端固定的 url上。
前端結束了。 讓咱們看看 後端收到後 幹了啥。
private void doHandle(RMIContext rmiCtx) throws IOException { Result result = new Result(); byte[] data = this.readFromNetWrok(rmiCtx); try { this.readInvocationInfo(rmiCtx, data); boolean legal = this.isLegalConcurrent(rmiCtx); if (!legal) { throw new FrameworkSecurityException(" had over limit concurrent"); } this.preRemoteProcess(); result.result = this.invokeBeanMethod(rmiCtx); } catch (Throwable var12) { Throwable appException = extractException(var12); if (verifyThrowable(appException)) { result.appexception = appException; } else { Logger.error("unexpected exception", appException); String errorMsg = ""; Throwable lastEJBException = this.getLastEJBException(appException); if (lastEJBException != null) { result.appexception = (Throwable)(lastEJBException.getCause() != null ? lastEJBException.getCause() : new BusinessRuntimeException(lastEJBException.getMessage())); } else { errorMsg = appException.getMessage(); result.appexception = new BusinessRuntimeException(errorMsg); } } } byte[] data = null; rmiCtx.setResult(result); try { this.beforeWriteClient(rmiCtx); this.writeResult(rmiCtx); } finally { this.afterWriteClient(rmiCtx); } }
而後 交給了 下面方法處理:
private Object invokeBeanMethod(RMIContext rmiCtx) throws Throwable { InvocationInfo invInfo = rmiCtx.getInvocationInfo(); String module = invInfo.getModule(); String service = invInfo.getServiceName(); String mn = invInfo.getMethodName(); Class<?>[] pts = invInfo.getParametertypes(); Object[] ps = invInfo.getParameters(); Object o = null; this.beforeInvokeBeanMethod(rmiCtx); Object var11; try { if (module == null) { o = this.remoteCtx.lookup(service); } else { Context moduleCtx = (Context)this.ctxMap.get(module); if (moduleCtx == null) { Properties props = new Properties(); props.put("nc.targetModule", module); props.put("nc.locator.provider", "nc.bs.framework.server.ModuleNCLocator"); moduleCtx = NCLocator.getInstance(props); this.ctxMap.put(module, moduleCtx); } o = ((Context)moduleCtx).lookup(service); } Method bm = o.getClass().getMethod(mn, pts); bm.setAccessible(true); Object result = bm.invoke(o, ps); var11 = result; } catch (ComponentException var15) { Logger.error("component lookup error", var15); throw var15; } finally { this.afterInvokeBeanMethod(rmiCtx); } return var11; }
what 又是 EJB lookup???
emmmm 機智一想 這裏應該是 爲了代理一波實現事務和調用信息記錄等各類騷操做。
套路換了 換了個 lookup實現類 nc.bs.framework.server.ModuleNCLocator#lookup 進去觀摩一下 學習學習。
emmmm 匿名類 不虛 繼續剛 發現匿名類是: nc.bs.framework.server.AbstractContainer#getContext :
最後lookup 轉發到了 nc.bs.framework.server.AbstractContext#lookup 這裏:
public Object lookup(String name) throws ComponentException { if (name == null) { throw new ComponentNotFoundException("component name is null when lookup"); } else if (name.startsWith("->")) { name = name.substring(2); ComponentMeta meta = this.findMeta(name); return this.findComponent(meta); } else { Object retObject = null; retObject = this.getServiceCache().get(name); if (retObject == null) { JndiContext jndiCtx = this.getJNDIContext(); if (name.startsWith("java:comp/env/")) { retObject = jndiCtx.lookup(name); if (EJBUtil.isHome(retObject)) { retObject = this.serviceFromEJBHome(name, retObject, (ComponentMeta)null); this.getServiceCache().put(name, retObject); } } else if (name.equals("javax.transaction.UserTransaction")) { retObject = this.getUserTransaction(); } if (retObject == null) { if (!this.supportBO() && name.endsWith("BO")) { throw new ComponentNotFoundException(String.format("The BO(3.x) is not supported, please rewrite the component %s", name)); } ComponentMeta meta = null; try { meta = this.findMeta(name); } catch (ComponentNotFoundException var12) { this.getLogger().warn(String.format("The component(meta): %s is not found in %s %s", name, "ESA", " try to search it from jndi.")); } String originalName = name; if (meta != null && !name.equals(meta.getName())) { name = meta.getName(); retObject = this.getServiceCache().get(name); } if (retObject == null) { boolean needTx = false; String jndiName = name; if (meta != null) { needTx = meta.getTxAttribute() != null && TxAttribute.NONE != meta.getTxAttribute(); jndiName = meta.getEjbName(); } boolean bugfix = RuntimeEnv.isRunningInWeblogic(); if ((meta == null || needTx && !this.isExcept(meta) && !this.isAgileMode()) && !this.isBlackService(name)) { try { retObject = this.jndi(jndiName); } catch (Throwable var11) { } if (EJBUtil.isHome(retObject)) { retObject = this.serviceFromEJBHome(name, retObject, meta); } else if (needTx && retObject != null) { retObject = Proxy.newProxyInstance(retObject.getClass().getClassLoader(), ClassUtil.getInterfaces(retObject.getClass(), ServerConstants.excludes), new EJB3ServiceHandler(name, retObject)); } if (retObject != null) { this.getServiceCache().put(name, retObject); if (originalName != name) { this.getServiceCache().put(originalName, retObject); } } else if (meta == null || !Boolean.TRUE.equals(bugfix) && this.isProductMode() && !this.isExcept(meta)) { this.markServiceBlack(name); if (originalName != name) { this.markServiceBlack(originalName); } } } if (retObject == null) { if ((meta == null || this.isProductMode() && needTx && !this.isExcept(meta)) && !bugfix) { throw new ComponentNotFoundException(name, String.format("The tx component: %s is not found in %s %s}", name, "jndi", " please deploy it!") + " jndiName: " + jndiName + " meta: " + meta); } if (meta != null) { if (needTx && !this.outJAVAEE() && !this.isExcept(meta) && !this.isAgileMode() && bugfix) { try { INamingHack hack = this.getNamingHack(); if (hack != null) { retObject = hack.hackLookup(meta.getEjbName()); } if (retObject != null) { this.getServiceCache().put(originalName, retObject); } } catch (Throwable var10) { } } if (retObject == null) { retObject = this.findComponent(meta); if (needTx && !this.outJAVAEE()) { retObject = this.dynamicBService(meta, retObject); if (bugfix) { this.getServiceCache().put(originalName, retObject); } } } return retObject; } throw new ComponentNotFoundException(name, "Can not find component(both in jndi and ESA)"); } } } } return retObject; } }
簡單說 就是 你NC啓動的時候 掃描 modelues文件夾, 每個文件夾會新建一個
緩存對象, 裏面緩存了 全部的接口xml配置的列表各類信息等。
獲取到接口配置信息 根據你配置的 實例化 服務器最終實現類 同時提供 NC 事務代理或無事務代理的相關 JDK proxy handler實現類對象 幫你去實際調用 具體實現類方法:
nc.bs.framework.server.AbstractContext#dynamicBService 後端代理類。
private Object dynamicBService(ComponentMeta meta, Object obj) { if (obj == null) { Logger.error("error dyanmic service for null: " + meta.getName()); } Object delegate = null; boolean bmt = false; if (meta.getTxAttribute() == TxAttribute.BMT) { delegate = this.lookup("java:comp/env/ejb/nc.itf.framework.ejb.BMTProxy"); bmt = true; } else { delegate = this.lookup("java:comp/env/ejb/nc.itf.framework.ejb.CMTProxy"); } return bmt ? Proxy.newProxyInstance(delegate.getClass().getClassLoader(), meta.getInterfaces(), new BMTEJBServiceHandler((BMTProxy)delegate, obj)) : Proxy.newProxyInstance(delegate.getClass().getClassLoader(), meta.getInterfaces(), new CMTEJBServiceHandler((CMTProxy)delegate, obj)); }
此處進行判斷 配置的事務方式 通常是CMT 實現類 : nc.bs.framework.ejb.CMTEJBServiceHandler
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { return method.getName().endsWith("_RequiresNew") ? this.cmtProxy.delegate_RequiresNew(this.wrapped, method, args) : this.cmtProxy.delegate(this.wrapped, method, args); } catch (Throwable var6) { Throwable lastEJBException = this.getLastEJBException(var6); if (lastEJBException == null) { throw var6; } else if (lastEJBException.getCause() != null) { throw lastEJBException.getCause(); } else { throw var6; } } }
最終事務的代理實現類是:nc.itf.framework.ejb.CMTProxy_Local
protected void beforeCallMethod(int methodId) { Logger.info("Begin Transaction(" + methodId + ")"); MwTookit.setThreadState("nc.bs.mw.naming.BeanBase.beforeCallMethod"); this.setLastCallTime(System.currentTimeMillis()); boolean isCmt = ((HomeBase)this.getEJBLocalHome()).getEJBBeanDescriptor().isCmt(); if (isCmt) { try { this.currentMethodTransectionType = this.getMethodTransectionType(methodId); int isolateLevel = this.getMethodIsolateLevelType(methodId); this.setIerpTransactionManagerProxy(TransactionFactory.getTMProxy()); this.getIerpTransactionManagerProxy().begin(this.currentMethodTransectionType, isolateLevel); } catch (Exception var4) { Logger.error("BeforeCallMethod", var4); } } else { if (this.getIerpUserTransaction() == null) { this.setIerpTransactionManagerProxy((IContainerTransProxy)null); this.setIerpUserTransaction(TransactionFactory.getUTransaction()); } this.getIerpUserTransaction().bindToCurrentThread(); } MwTookit.setThreadState("nc.bs.mw.naming.BeanBase.beforeCallMethod over"); }
最後就是一頓操做猛如虎,nc總體調用完成。
本文章 只是簡單講述了一下NC的先後端RMI解決方案,注意 事務實現類 是單機NC狀況下的實現, 當WAS環境下 接口實現類是不一樣的 請注意。
本文中使用的 idea NC開發插件由本人開發 gitee: https://gitee.com/yhlx/idea_plugin_nc5devplugin .