构件运行环境提供了构件包资源加载以及构件包资源热部署框架,同时提供对应用上下文以及构件包上下文的管理,对资源采用统一的接口进行访问。
构件运行环境包括四方面的内容:应用上下文、扩展点、资源加载和资源的热部署。
构件运行环境提供一单实例类ApplicationContext取得应用的上下文信息,其使用方式如下表所示:
构件运行环境提供了资源的加载机制,针对特定的类型文件进行加载,如.xsd、.biz文件等,为了提高资源加载速度,资源加载支持懒加载模式,只有用到的资源才进行加载。
在EOSServer启动时,构件运行环境会扫描应用对应的系统构件存储库和用户构件存储库目录,并记录目录下的所有资源信息。为提高扫描的效率,用户可以配置不需要扫描的类型和路径,配置文件在sys-config.xml文件中,配置项为:
注意
目录的设置只支持构件包下的一层目录,如com、classes等,不支持多层目录(如com/eos)。
构件运行环境提供了统一的资源加载模型,需要加载的每种文件类型都有一个加载器(Loader)与之对应,加载器负责把物理模型解析为逻辑模型,供运行期调用。EOS中用到的类型加载器有:数据实体、逻辑流、页面流等。
为了提高系统的启动速度,在资源加载时支持懒加载,只有在使用到资源时,才装载资源,提高运行的效率。懒加载配置在sys-config.xml文件中,配置项为:
资源热更新采用守护线程的方式,在系统启动时会启动守护线程监听资源的变化,如果资源变化包括新增、修改和删除的文件,守护线程会重新调用加载器(Loader)对变更资源进行加载。
在应用启动、停止,构件包加载、卸载时,构件运行环境提供了一种回调机制,当这些事件发生时,可以回调用户定义的操作,完成其特定的功能。
当构件运行环境启动或者停止时,触发事件(RuntimeEvent),调用用户的回调接口。事件RuntimeEvent中包含的对象有:
扩展方式如下:
publicinterfaceIRuntimeListener{/***启动应用*@paramenvent事件源*/publicvoidstart(RuntimeEventenvent);/***结束应用*@paramenvent事件源*/publicvoidstop(RuntimeEventenvent);}
说明
扩展点执行的顺序为配置文件中定义的顺序。
在构件包发生加载、卸载事件时回调用户的接口,监听构件包的变化,此监听事件是针对所对应的构件包,可以为不同构件包配置不同的监听接口。
监听器执行的顺序为文件中配置的顺序。
用户可以查看的日志有三个层次:
应用层日志有三种:
构件包层日志,用户通过使用系统提供日志功能自己记录。系统通过封装slf4j来提供记录日志的功能,用户可以配置、扩展、重新封装日志。
例如:
LoggergetContributionTraceLogger(StringcontributionName,StringloggerName)参数说明:
返回值说明:
pom依赖
添加eos-sdk依赖包
以下代码仅作参考:
publicclassAFCRuntimeExceptionextendsEOSRuntimeException{privatestaticfinallongserialVersionUID=828959048127312471L;/****@paramcode异常编码*/publicAFCRuntimeException(Stringcode){super(code);}/****@paramcode异常编码*@paramparams异常描述时用到的格式化参数*/publicAFCRuntimeException(Stringcode,Object...params){super(code,params);}/****@paramcode异常编码*@paramcause上层异常*@paramparams异常描述时用到的格式化参数*/publicAFCRuntimeException(Stringcode,Throwablecause,Object...params){super(code,params,cause);}/****@paramcode异常编码*@paramcause上层异常*/publicAFCRuntimeException(Stringcode,Throwablecause){super(code,cause);}}自定义异常编码和异常编码配置文件
异常编码定义:
ExceptionResource.properties:
999999=Serverinternalerror,pleasecontacttheadministrator100001=Systemerror::Parameterverificationerror100002=Systemerror::Serializationobjecterror,object''{0}''100003=Systemloginerror::CurrentstatusNotloggedin100004=Errorimportingfilecontent''{0}''100005=Errorimportinglocationconnect''{0}''100006=Errorexporting''{0}''...........ExceptionResource_zh_CN.properties:
999999=\u670D\u52A1\u5668\u5185\u90E8\u9519\u8BEF,\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458!100001=\u7CFB\u7EDF\u9519\u8BEF::\u53C2\u6570\u6821\u9A8C\u9519\u8BEF100002=\u7CFB\u7EDF\u9519\u8BEF::\u5E8F\u5217\u5316\u5BF9\u8C61\u9519\u8BEF,object''{0}''100003=\u7CFB\u7EDF\u767B\u5F55\u5F02\u5E38::\u5F53\u524D\u72B6\u6001\u672A\u767B\u5F55100004=\u5BFC\u5165\u6587\u4EF6\u5185\u5BB9\u9519\u8BEF''{0}''100005=\u5BFC\u5165\u5730\u5740\u4FE1\u606F\u9519\u8BEF''{0}''100006=\u5BFC\u51FA\u9519\u8BEF''{0}''............注意:如果自定义的runtimeException是继承自EOSRuntimeException,那么异常编码文件的目录一定是固定在META-INF/resources下面,名称叫ExceptionResource.properties中文:ExceptionResource_zh_CN.properties
开发中使用
if(!checkPassword(user.getPassword(),loginUser.getPassword())){session.setAttribute("currentSessionError","error");throwAFCExceptionCode.USER_INFO_ERROR.runtimeException();}用户自定义异常处理(不使用eos统一异常处理规范)
自定义异常Response响应结构,代码示例如下:
publicclassResponseErrorModel{privateintstatus;privateStringcode;privateStringerror;privateStringmessage;publicResponseErrorModel(){this.status=status;this.code=code;this.error=error;this.message=message;}/***HttpResponse的状态码**@returnHttpResponse的状态码*/publicintgetStatus(){returnstatus;}/***错误码**@return错误码*/publicStringgetCode(){returncode;}/***错误简要信息**@return错误简要信息*/publicStringgetError(){returnerror;}/***错误详细信息**@return错误详细信息*/publicStringgetMessage(){returnmessage;}}自定义异常拦截器,代码示例如下:
@ControllerAdvice@Order(Ordered.LOWEST_PRECEDENCE-10)publicclassControllerExceptionHandler{privatestaticLoggerlogger=TraceLoggerFactory.getLogger(ControllerExceptionHandler.class);@ExceptionHandler(value=Throwable.class)protectedResponseEntity
EOS引擎会在调用运算逻辑、逻辑流、服务时自动创建一个MUO,并根据user-config.xml的配置将HttpSession中的部分数据放入MUO,并把MUO传入给运算逻辑、逻辑流、服务使用,在运算逻辑、逻辑流、服务执行完毕后,EOS引擎会把在运算逻辑、逻辑流、服务执行中对MUO的修改值回写到HttpSession中。
使用MUO的优点:
在Studio调试数据中设置customer:
MUO和HttpSession
用户在配置configValue时要注意,不要把key写成"userObject",因为会被系统默认的属性覆盖。如果configValue的节点值是以"java:"开头,那么所指定的javaclass必须是一个符合javabean规范的class。
虽然MUO也是一种数据上下文,并且数据上下文允许创建(create)、删除(delete)、增加(add)数据对象的操作,但MUO并不允许create、delete、add的操作,否则会抛出异常。因为MUO是个受管对象,只能访问在user-config.xml中配置过的属性,所以不允许动态的创建和删除属性。MUO只允许在某个属性上设置数据对象(set)和根据属性名称取用户对象(get)操作,而且set/get操作必须以configValue中配置过的key开头,否则MUO不会有任何改变。
例如在Session-Manage/Managed-User-Object中配置了java:com.test.Customer,可使用以下代码操作MUO:
//获取当前线程中的IMUODataContextIMUODataContextmuo=DataContextManager.current().getMUODataContext();//获取客户的id,注意用customer开头,因为configValue的key是customermuo.get("customer/custId");//设置客户的消费日期muo.set("customer/custDate",newjava.util.Date());//获取不存在的属性,返回nullmuo.get("customer/noexistdkey");//设置不存在的属性,抛异常muo.set("customer/noexistdkey","value");//获取不存在的key,返回null。muo.get("noexistdkey");//设置不存在的key,没有任何效果muo.set("noexistdkey","value");//获取用户信息(userObject是系统默认的隐藏属性)muo.get("userObject/userId");//create,delete,add抛异常muo.createObject("customer",com.test.Customer.class);muo.add("customer","value");muo.deleteObject("customer");说明
MUO的配置方式
MUO可以通过2种方式配置:
用户对象是存放用户信息的对象。它主要用于在日志记录中确定一个用户做了哪些操作,以及在做资源访问权限校验时根据这个对象来判断是否有权限访问资源(如:页面流、逻辑流等)。
IUserObject是一个用户对象的接口,继承了一个IOpenable的接口,可以动态地增加或删除属性,类似于一个Map。
提示
关于OnlineUserManager和OnlineUserManagerUtil:
uniqueId是在创建UserObject时自动生成的唯一的标识。
调用logoutByUniqueId就是注销一个UserObject,如果调用logoutByUserId则会把userId相同的多个UserObject注销。
用户密码:
user在页面流中定义成一个"com.eos.data.datacontext.UserObject"类型的变量。因为UserObject并没有定义pwd这个属性,所以要通过动态的属性"attributes"下设置一个pwd的String属性。
一个简单实现回调接口的例子
场景描述:只有当UserCredential中的属性"loginUser"的值等于"admin"时,才认为是有效用户。
虚拟用户的配置方式
虚拟用户可以通过2种方式配置:
ServiceDataObjects(SDO)是一种数据编程架构和API。它统一了不同数据源类型的数据编程,提供了对通用应用程序模式的健壮支持,并使应用程序、工具和框架更容易查询、读取、更新和检查数据。
SDO中关键的概念包括DataObject(数据对象),datagraph(数据图)。
数据对象用于描述业务数据,数据对象利用属性保存数据。数据对象接口的设计原则是使编程更为简单。因为它提供了对所有普通类型的业务数据的访问以及各种访问模式的支持,如基于名字、索引和路径进行访问。
数据对象可以分为以下几类:
多个属性之间的顺序不可维护是指:如果有一个数据对象A,其中有Many-Valued的属性bs、cs。如果用户的操作顺序是A.set("bs[1]","b_value1")、A.set("cs[1]","c_value1")、A.set("bs[2]","b_value2"),这样的操作顺序就是跨越多个属性之间的顺序。现在EOS6的实现中无法维护这种顺序。即bs数据里的元素的顺序是可以保证的,但对bs和cs的之间的顺序是未实现的。SDO2.1规范里有sequenced类型的DataObject可以维护这种关系,但是EOS6暂时没有实现。
数据对象拥有给其属性所赋的数据值。例如,一个采购订单数据对象order的orderDate属性有一个值为:2005-6-30。通过使用order.get("orderDate")和order.set("orderDate")方法,可以获取或修改其orderDate属性的值。
直接调用set(path,value)接口时:
属性可能有一个或多个值。如果一个属性是多值的,那么property.isMany()返回true,get(property)方法将返回一个列表(List)。
在数据接口上,当属性没有值时,返回值为List的数据对象方法将返回一个空List而不是返回一个null。返回的List可以描述数据对象的值的任何变化。
对于访问多值属性来说,使用getList(property)方法是极其方便的。如果property.isMany()是true,那么set(property,value)和setList(property,value)方法需要"value"分别为java.util.Collection和List对象。这些方法和在使用完getList(property).clear()之后紧接着使用getList(property).addAll(value)方法效果一样。
对于多值属性,get()和getList()方法将返回一个包含当前值的列表List。通过List接口立即可以对数据对象的当前值进行更新操作。每次使用get()或getList()方法访问时,都将返回同一个List对象。
对于一个多值属性,isSet(property)方法将返回:
对于一个单值属性,isSet(property)方法将返回:
当调用了set()而没有调用unset(),则isSet()返回true,而不管属性值有没有发生修改。例如,当调用了set(property,property.getDefault()),则isSet(property)返回true。
unset(property)方法可以用来清空一个简单属性,因此在unset(property)之后,isSet(property)返回false,get(property)返回缺省值。delete()方法将取消数据对象的所有非只读属性的值。在unset()之后,get(property)将返回缺省值,对于多值属性,则返回一个空列表。
数据图内的数据对象被组织成一个树型结构。一个数据对象作为树的根节点,而其它数据对象则组成这个树的其它叶子节点。
由根数据对象开始的包含引用(containmentreferences)可以组成一个树型结构。根数据对象引用其它数据对象,而这些数据对象则可以进一步引用更深一层的数据对象。除了根数据对象,数据图内的每一个数据对象必定有一个从树内其它节点而来的包含引用。图内的每一个数据对象可以跟踪它的包含内容引用的位置。
对于一个数据图来说,拥有一个非包含引用是可能的。这些引用所指向的数据对象是同一个数据图的一部分(引用的数据对象必定是同一个树的一部分),但是这些引用不会影响数据图的树型结构。
包含引用和非包含引用都是数据对象的属性。该属性的类型可以是任何一种数据对象类型。
一个特殊的数据对象引用属性是否是一个包含引用或非包含引用由该数据图的数据模型定义,例如定义一个XML文档数据类型的XSD。一旦数据模型被定义好后,将不能被修改。可以通过访问property.isContainment()来查询一个特殊的引用是否是一个包含引用。
容器型数据对象(containerDataObject)含其它数据对象。一个数据对象最多只能被一个容器性数据对象包含。如果一个数据对象没有容器,那么它就是一个根数据对象。
getContainer()和getContainmentProperty()方法提供了自上而下的简单浏览数据对象的途径。getContainer()方法返回一个父数据对象,getContainmentProperty()方法返回包含该对象的父对象中的对应的属性。使用detach()方法,在不做任何改变的情况下,可以将一个数据对象能够从它的父对象中移出。即把父对象中的对应的propertyunset。
创建方法可以创建一个该属性所属类型的数据对象,或者创建一个在参数中说明的类型的数据对象,并且能够将一个创建好的数据对象添加到指定的属性。如果属性是单值的,属性的值将被设置为该新创建的对象。如果属性是多值的,新创建的对象将被作为最后一个对象而添加到该属性中。只有包含属性能够被用来创建对象。一个新创建的对象的所有属性都是未被设过值的(isSet==false)。
delete()方法取消(unset)了数据对象的所有非只读属性。如果包含属性不是只读的,delete()方法也将把该数据对象从包含其的数据对象中移出。包含属性所包含的递归的子数据对象也将被删除。如果包含属性是只读的,将会抛出异常。
如果其它数据对象有一个非包含属性指向已删除的数据对象,那么这些引用将不会被修改。然而为了满足数据图的封闭特性,这些引用指向的对象的属性值会被修改。一个被删除的数据对象能够被再次使用,并且能够被再次添加到数据图中。
数据对象有两种类型的属性:
指定类型(Type)的属性可以通过getType().getProperties()方法获得,该方法将返回一个列表。除了拥有有类型描述的属性之外,数据对象还能够拥有开放属性。
当Type.isOpen()为true时,允许拥有开放内容属性。一些类型将开放(open)设置为false,因为它们不能接受附加的属性。
当你使用getInstanceProperties()方法时,将返回一个只读的列表,里面包含一个数据对象当前所使用的所有属性。这包括开放内容的属性。属性的顺序由所有getType().getProperties()方法返回值开始;其它属性排在后面。每一次对getInstanceProperties()方法的调用将返回同一个列表对象,除非该数据对象已经更新,从而导致列表内容改变。
根据一个属性名称,通过调用DataObject的getInstanceProperty(propertyName)可以获取相应的property。
当一个数据对象拥有多个属性时,每一个属性都能够被一个数字索引所引用,其中第一个属性的数字索引从0开始。
get(intproperty)方法中使用的属性索引是该属性在getInstanceProperties()方法返回的列表中的位置。
如果数据正在修改之中,不推荐对开放内容使用按属性索引这种方式访问;除非该索引被用在getInstanceProperties()方法获得的属性中,因为在getInstanceProperties()方法中,开放内容的属性索引能够改变,如果有几个开放内容属性的值被重复的设置和取消设置。
下面的例子是正确的,因为索引和getInstanceProperties()方法一起使用。
由于数据对象不是同步的,因此用户不应该在读的同时对其进行更新操作。
该实例显示了一个普通的模式,在所有实例属性中循环并且打印属性名和值:
一个Type总是有:
一个Type还可以有:
一个数据对象是由属性值建立起来的。
一个属性包括了:
由于EOS暂时没有完全实现SDO规范,所以下面所列的API就是EOS所实现的全部功能。
//创建Company的sdo对象。//create()的第一个参数是URI(就是xsd的targetNamespace),在EOS中就是数据集的名称。//第二个参数是实体名称。DataObjectcompany=DataFactory.INSTANCE.create("com.primeton.eos.example.newdataset1","Company");company.get("name");相当于company.get(company.getType().getProperty("name"))company.set("name","acme");company.get("department.0/name")相当于((DataObject)((List)company.get("department")).get(0)).get("name").n下标从0开始company.get("department[1]/name")相当于[]下标从1开始company.get("department[number=123]")返回第一个number=123的department
datatype之间是否允许类型转换如下表所示(x表示允许):
每个圆圈代表一个DataObject。对象树如下图所示。
对象图如下图所示:
对象树是一种自顶向下单方向,并且除了根对象外每个对象有且只有一个引用的特殊的对象图。
对比上面2张图可以发现,在对象图中A_2_1的父对象变成了A_1,并且A_2和A_2_1之间的引用变成了红色。
在对象树中的所有的引用都是SDO所指的包含引用,而在对象图中除了A_2和A_2_1之间的引用是SDO所指的非包含引用外,其余的引用也都是包含引用。
包含引用和非包含引用的差别显示在delete的时候。
如在对象树中删除A_2,那么A_2的所有非只读属性会被unset,并且A_2的所有包含引用指向的对象A_2_1和A_2_2的所有非只读属性也会被unset。
如在对象图中删除A_2,那么A_2的所有非只读属性会被unset,并且A_2的包含引用指向的对象A_2_2的所有非只读属性也会被unset。和在对象树中不同的是,因为A_2对A_2_1是非包含引用,所以在删除A_2的时候不会对A_2_1有操作。
Xpath提供了访问对象的内容的一组语法,通过该语法,可以遍历访问对象;这些对象可以以JavaBean、SDO、Map等各种形式存在,也可以是它们的混合形式。利用Xpath语法,屏蔽和统一了这些对象的访问方式。
EOS支持的Xpath语法如下:
path::='/'(step'/')*stepstep::='@'property|property'['index_from_1']'|#;;如果是DOM节点,取节点的值|property'.'index_from_0|reference'['@attribute'='value']'|".."property::=NCName;;maybesimpleorcomplextypeattribute::=NCName;;mustbesimpletypereference::NCName;;index_from_0::=Digitsindex_from_1::=NotZero(Digits)value::=LiteralLiteral::='"'[01Xpath语法^"]*'"'|"'"[01Xpath语法^']*"'"NotZero::=[1-9]Digits::=[0-9]+EOSXpath与SDOXpath的语法有以下差异:
举一个对DOM节点值的访问的例子,假设在一个混合对象JavaBean中,有一个Element类型的名称为elem的DOM对象。该DOM对象如下:
根据上面列出的Xpath语法,我们可以看到,除了标准Xpath语法外,EOS的Xpath语法额外添加了一种下标取值的语法,即".(以0开始的下标)"。"department.0/name"相当于"department[1]/name"。
Xpath支持的访问的对象有以下几种:
在根据Xpath创建对象,对对象属性进行赋值时,如果不能从对象的数据元信息(比如从DataObject的Type中获取的属性,从JavaBean、Map中获取属性的类型)中得到当前要创建的节点信息,那么就需要根据类型映射获取该节点的类型。
在根据Xpath对对象进行赋值操作时,如果要动态创建对象或属性,则类型映射的规则如下:
比如类似下面的类型映射是不支持的。
XPathLocatorlocator=XPathLocator.getInstance();locator.setValue(newHashMap(),"property[1]","test");等同于
Maproot=newHashMap();Object[]data=newObject[1];data[0]="test";root.put("property",data);XPathLocatorlocator2=XPathLocator.getInstance();locator2.setValue(newHashMap(),"property/subProperty","test");等同于
定义了这样一个类CompoundClass:
classCompoundClass{privateListdatas;publicListgetDatas(){returnthis.datas;}publicvoidsetDatas(Listdatas){this.datas=datas;}}CompoundClasscompound=newCompoundClass();XPathLocatorlocator=XPathLocator.getInstance();locator.setValue(compound,"datas[1]","test");等同于
比如:JavaBean中有一个名称为dom的Document对象,名称为elem的Element对象。
classJavaBean{privateDocumentdom;privateElementelem;publcDocumentgetDom(){returndom;}publicElementgetElem(){returnelem;}}其中dom和elem属性都是下面的结构
数据库连接管理提供了对数据源的管理功能,允许用户配置多个数据源,并提供接口,用于获取应用级别和构件包级别的数据源连接。它还提供了一组扩展接口,在JDBC操作过程中,对数据库连接connection、执行语句statement、结果集Resultset进行监控。
EOS6中允许用户配置多个数据源,每个数据源都有引用名称,系统管理着数据源名称到数据源的映射,用户可以根据数据源名称得到数据源。
系统默认提供了一个名称为default的数据源,用户不指定数据源引用名称时,就是取默认的数据源。
对于使用构件包的数据源引用名称default去查找数据源时,如果对应的应用数据源名称不存在,会使用"default"的应用数据源代替。而如果构件包的数据源引用名称不是default且对应的应用数据源名称不存在时,会抛异常。如上面示例的配置,如果在user-config.xml中没有配置名称为other的应用数据源,当在构件包中获取构件包级别的default数据源时,实际使用的是default的应用数据源(因为没有找到other的应用数据源)。
用户在JDBC操作过程中,系统提供扩展点,供用户扩展;同时提供给用户注入扩展点实现的接口。
比如用户在数据库的JDBC操作过程中,调用数据库连接,connection.close()方法时,用户可能在这个过程中加入自己的行为或功能。
系统提供给用户的扩展点如下:
用户可以通过JDBC操作扩展的注册接口注册自己的扩展实现。
监控的方面包含:
用户可以通过配置对这些监控进行打开和关闭操作。具体的配置信息在用户配置文件user-config.xml中。
EOS6提供了对单个数据源的事务管理的实现(本地事务),即数据源事务管理器,也提供了利用全局事务(JTA)进行管理的方式。
利用本地事务进行管理时,数据库连接的autoCommit属性会自动的设为false,这样就可以利用事务管理器对数据进行提交或者回滚。
在事务中通过对连接进行关闭,实际上并不能把数据库连接关掉,只有当事务进行提交或者回滚时,数据库连接才会真正的释放。
见下面的编程模型:
这个选择有深刻的含义。例如,全局事务可以用于多个事务性的资源(典型例子是关系数据库和消息队列)。使用本地事务,应用服务器不需要参与事务管理,并且不能帮助确保跨越多个资源(需要指出的是多数应用使用单一事务性的资源)的事务的正确性。
EOS6提供了一致的事务管理抽象模型;它能使开发者在任何环境下使用统一的编程模型,而不需要关心使用的是全局事务,还是本地事务。
通常在一个事务中执行的所有代码都会在这个事务中运行。但是,如果一个事务上下文已经存在,有几个选项可以指定一个事务性方法的执行行为。例如,简单地在现有的事务中继续运行(大多数情况);或者挂起现有事务,创建一个新的事务。
EOS6支持以下几种事务传播方式:
以上这些传播方式常量都定义在对外接口com.eos.common.transaction.ITransactionDefinition中。
事务的传播也支持跨应用服务器的ejb调用。有一点需要说明的是在weblogic环境下,对于跨domain之间的ejb调用的事务的传播,需要两个domain的名称不能相同,两个server的名称也不能相同。
在标准SQL规范中,定义了4个事务隔离级别,不同的隔离级别对事务的处理不同:
EOS6支持以下几种隔离级别:
如果在事务开始接口中不指定事务的隔离级别的话,那么就用用户在事务配置中指定的隔离级别。默认情况下,事务配置中的事务隔离级别为数据库默认的隔离级别。
事务管理器是操作事务的接口。可以通过它执行事务的开始、提交、回滚,以及查看事务状态等操作。
事务的编程模型如下:
ITransactionManagertxManager=TransactionManagerFactory.getTransactionManager();txManager.begin();//默认的事务传播方式是requiredtry{txManager.begin();//默认的事务传播方式是requiredtry{//dosomething......txManager.commit();}catch(Exceptione){txManager.rollback();//throwe;}//dosomething......txManager.commit();}catch(Exceptione){txManager.rollback();throwe;}下面对事务管理器的接口进行说明:
voidbegin(int...attrs)其中参数attrs依次定义了事务的属性包括传播方式和隔离级别。
默认情况下不填写的话,事务传播用PROPAGATION_REQUIRED,隔离级别用数据库默认的隔离级别。
voidcommit()提交事务,实际动作依赖于当前事务的启动方式和状态。
voidrollback()回滚事务,实际动作依赖于当前事务的启动方式和状态。
如果当前事务不是新启动的事务,则设置事务状态为rollbackonly;否则对事务做回滚操作,并且若当前事务启动时,有外部事务被挂起,则完成回滚操作后还要恢复外部事务。
WebLogic环境下,在跨weblogicdomain之间的带JTA全局事务传播的ejb调用过程中,有两个限制条件:
EOS安装后,domain的名称都是eosdomain,并且所有的应用都是部署在AdminServer上的,因此在跨domain之间调用JTA事务时,需要手工创建一个domain,名称不同于eosdomian,在其上创建一个server,名称不同于AdminServer,然后把eos的应用部署在这个server上,并且建立该domain和eosdomain之间的信任。
可以通过事务同步注册接口把事务同步注册进来。
EOS6中事务有以下几种状态:
在定时器中,对定时器的管理可以参与到外部的事务中,也可以不参与外部的事务。
用户可以用两种编程方式,对定时器进行管理。
对于是否接受外部事务传递的定义,运算逻辑和服务总是接受外部事务传递的;逻辑流可以接受,也可以不接受外部事务传递。
在逻辑流中如果嵌套多个事务开始图元,那么实际上只有第一个事务开始图元起作用,内部嵌套的事务开始图元被忽略。
EOS元数据描述了EOSServer、EOS应用、构件包、逻辑流、数据实体这些对象在运行时的基本信息。
以上对象有一个树状层次关系:
IServerMetaData||---IApplicationMetaData||---ISDOTypeMetaData||---IContributionMetaData||---IBizMetaData由上图我们可以看出,通过元数据可以找出一个EOSServer中有多少EOS应用,每个应用中定义了多少数据实体和构件包,每个构件包中包含了多少逻辑流。
因为数据实体解析完成后,只有targetNamespace的信息,并没有保存它被定义时所在的构件包,所以通过元数据只能查找在每个应用下所有的数据实体。
通过下面的示例代码,可以获取eos-default的应用中有多少数据实体。
//获取EOSserver的所有应用时,如果传入的是null,则获取的是当前server的所有应用.IApplicationMetaData[]applications=MetaDataHelper.getApplicationMetaDatas(null);for(IApplicationMetaDataapplication:applications){if("eos-default".equals(application.getName())){ISDOTypeMetaData[]sdoTypeMetadatas=application.getSDOTypeMetaDatas();for(ISDOTypeMetaDatametadata:sdoTypeMetadatas)System.out.println(metadata.getName());}//endif}//endfor//下面的代码可以实现和上面一样的功能//获取应用的数据实体的时候,如果传入的是null,则获取的是当前应用的所有数据实体.ISDOTypeMetaData[]sdoTypeMetadatas=MetaDataHelper.getSDOTypeMetaDatas(null);for(ISDOTypeMetaDatametadata:sdoTypeMetadatas){System.out.println(metadata.getName());}说明
上面2段代码,第一段是通过获取应用,然后再获取应用所包含的数据实体;第二段是直接获取当前应用所包含的数据实体。通过MetaDataHelper的接口获取元数据是每次都会实时的去获取最新的元数据,这在应用被频繁动态更新时有效。但是每次都调用MetaDataHelper的接口会影响效率,为了提高效率,可以通过MetaDataHelper获取一个EOSServer或一个EOS应用的元数据(IServerMetaData或IApplicationMetaData),然后通过IServerMetaData或IApplicationMetaData获取其所包含的页面流、逻辑流、数据实体,这样就会缓存这些元数据。
MetaDataHelper类中的方法会每次都去获取Server运行时的最新元数据,这样会消耗性能。对于没有动态更新的应用来说,建议使用通过IServerMetaData或IApplicationMetaData获取所包含的逻辑流等元数据,因为这样会缓存这些元数据。
本章节所描述的是同一个系统内的不同应用之间的调用规范:
provider服务提供端
配置文件进行feign配置
feign.hystrix.enabled=true启动类是否有@EnableFeignClients和@EnableHystrix注解
feignClient编写
在api模块创建feignclient接口,代码示例如下:
在pom中引入provider-api模块即可
在开发中的用法
注解使用@autowrite注入即可,代码示例如下:
网关配置路由:
订阅凭证和网关path配置
因为服务调用端与服务提供端分属不同的系统,运行期不能直接对服务进行访问(会被拦截,无权访问),需要通过网关路由转发服务调用请求,因此需要配置网关的PATH,供服务调用端发送服务请求,调用服务提供端发布的API。在运行期,网关PATH和订阅凭证可能会变更,因此不宜写死在代码中,需要使用Governor微服务管理平台提供的能力对应用中的网关PATH配置进行动态刷新。要使用Governor的配置能力,
配置完成之后,在开发中使用@RefreshScope+@Value读取配置即可,如配置有变化应用会监听配置进行热更新处理。
增加sudkey配置类
packagecom.primeton.eos.config;importcom.primeton.eos.dap.sdk.api.webclient.SDKApiSubscriberProvider;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.beans.factory.annotation.Value;importorg.springframework.stereotype.Component;@Component@RefreshScopepublicclassApiSubKeyConfigimplementsSDKApiSubscriberProvider{@Value("${provider.gateway.path}")privateStringgatewayPath;@Value("${product-stock.subkey}")privateStringsubkey;@OverridepublicApiSubscribergetSubscriber(Stringhost,StringrequestPath){if(gatewayPath.indexOf(host)!=-1){returnnewApiSubscriber(subkey);}else{returnnewApiSubscriber();}}}使用sdkResttemplate进行远程调用:
@Service@RefreshScopepublicclassOrderServiceImpl{@Value("${provider.gateway.path}")privateStringgatewayPath;@Value("${product-stock.subkey}")privateStringsubkey;@Autowired@Qualifier("sdkRestTemplate")//sdkRestTemplate@LoadBalancedprivateRestTemplatesdkRestTemplate;@OverridepublicOrderVOqueryOrderDetail(LongorderId){OrderVOvo=newOrderVO();Orderorder=orderRepository.findById(orderId).orElse(null);try{StringversionIds=getVersionIds(order);//跨系统调用List