NC系列,先後端交互底層原理實現 和 事務代理實現的研究

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上。

前端結束了。 讓咱們看看 後端收到後 幹了啥。

經過調試咱們發現 這個url對應的是: nc.bs.framework.comn.serv.CommonServletDispatcher 類 而後請求發給了 nc.bs.framework.rmi.server.RMIHandlerImpl#doHandle 方法處理。
 
 
 
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   .

相關文章
相關標籤/搜索