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   .

相关文章
相关标签/搜索