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 .