金蝶Apusic应用服务器的数据源管理(转)

1.           前言

在基于 J2EE 平台的应用开发中,大多数的应用都需要跟数据库打交道;而自从接触 JDBC 起,我们便不止一次的被告之:数据库资源是十分宝贵的系统资源,一定要谨慎使用。但令人遗憾的是,在笔者见过的大部分跟数据库相关的应用开发中,针对数据库资源的使用总是充斥着这样或者那样的问题。在本文中,笔者针对常见的一些错误或者不当的使用数据库资源的案例进行介绍与分析,并阐述金蝶 Apusic 应用服务器提供的一些增值特性,通过这些特性能够有效的避免某些错误的发生。

2.           常见数据库资源错误/不当用法的案例分析

2.1.  未正确的关闭数据库连接

申请了数据库连接,却没有及时的关闭它,这几乎是最常见的数据库连接使用错误。犯这种错误的原因有很多,以下是常见的一种低级错误:

publicvoidfoo(){

       Connectionconn=getConnection();

       Statementstmt=null;

       try{

       conn=getConnection();

       stmt=conn.createStatement();

       }catch(Exceptione){

       }finally{

       close(stmt,conn);

       }

}

< 示例代码一 >

在上述案例中的第 2 行代码中,作者已经申请了一个 Connection ,但在第 5 行代码中,又申请了一个新的 Connection ,并且丢失了第一次申请的 connection 的引用,至此,当程序每调一次 foo 方法,将导致申请一个新的 Connection 而没有释放它,如此一来,当数据库达到能够承受的最大连接数时,将导致整个应用的运行失败。

避免这种错误的方法有很多,譬如,可采用类似于 FindBugs( 注 1) 的代码分析工具对应用的源码进行分析,找出可能产生错误的代码。

此外,在应用中,我们需要非常频繁的对申请的数据库连接进行关闭与释放,此时,建议封装成某些工具类使用,并且要尽可能安全的关闭数据库连接。下面,我们以关闭 Statement 及 Connection 的通用 close 方法的不同实现方案来比较:

不安全的关闭方法:

privatevoidclose(Statementstmt,Connectionconn){

       try{

              stmt.close();

              conn.close();

       }catch(Exceptione){}

}

< 示例代码二 >

在上述代码中,倘若第 3 行代码中的 stmt 为空,或者 stmt.close() 方法出错并抛出异常,都将使第 4 行代码不能够正常调用,从而导致数据库连接无法释放,那么,更安全的写法应该是:

安全的关闭数据库资源方法:

privatevoidclose(Statementstmt,Connectionconn){

       try{

              if(stmt!=null)stmt.close();

       }catch(Exceptione){}

       try{

              if(conn!=null)conn.close();

       }catch(Exceptione){}

}

< 示例代码三 >

在修订后的代码中,我们可以看到,无论第 3 行代码中关闭 stmt 是否成功,程序都能够保证向下执行,从而正确的关闭 conn 。

这些常用的数据库资源操作公用类,可以使用 Apache 的 CommonsDbUtils( 注 2) 组件。

2.2.  任意的申请数据库连接

不考虑事务上下文,任意的申请数据库连接资源,也是常见的一种不当用法。但这种问题往往是难以克服的,根源在于 Java 是一种面向对象的语言,而数据库的事务却是一种批量化的操作过程。我们以常见的“序列号”的实现方案为例:在某些应用场景中,我们需要一种自增长的整数型字段,但由于不同的数据库有不同的实现,所以,为达到各个数据库兼容的目的,我们常用的解决方案是,新建一张 T_SEQUENCE 表,它可能包含的字段有: NAMEvarchar(100),CURRENT_VALnumber(10) ;其中, NAME 存放序列的名称,而 CURRENT_VAL 存放序列的当前值。假设某一业务对象 Customer 需要新增一笔记录时,为获得不重复且自增长的 CustomerID ,需要将 T_SEQUENCE 表中的与该业务表对应的序列号加 1 并更新,然后将更新后的值作为 Customer 的 ID ,如下述表格所示:

T_SEQUENCE

NAME

CURRENT_VAL

CUSTOMER

10

 

T_CUSTOMER

ID

CUSTOMER_NAME

9

Kevin

10

Mary

 

于是,在 Java 语言中,我们以面向对象的方法来实现,可能会是这样(常见写法,未必是最优实现):

publicclassCustomer{

publicvoidsequencePlus(){
Connectionconn=null;
Statementstmt=null;
try{
conn=getConnection();
stmt=conn.createStatement();
Stringsql="updateT_SEQUENCEsetCURRENT_VAL

=CURRENT_VAL+1"
+"whereNAME='CUSTOMER'";
stmt.execute(sql);
}catch(Exceptione){
e.printStackTrace();
}finally{
DbUtils.closeQuietly(stmt);
DbUtils.closeQuietly(conn);
}
}

publicintgetSequenceCurrentVal(){
Connectionconn=null;
Statementstmt=null;
ResultSetrset=null;
intid=0;
try{
conn=getConnection();
stmt=conn.createStatement();
Stringsql="selectCURRENT_VALfromT_SEQUENCE

whereNAME='CUSTOMER'";
rset=stmt.executeQuery(sql);
if(rset.next()){
id=rset.getInt(1);
}
}catch(Exceptione){
e.printStackTrace();
}finally{
DbUtils.closeQuietly(conn,stmt,rset);
}
returnid;
}

publicvoidaddCustomer(Stringname){
Connectionconn=null;
PreparedStatementstmt=null;
ResultSetrset=null;
try{
sequencePlus();
intid=getSequenceCurrentVal();
conn=getConnection();
stmt=conn.prepareStatement(
"insertintoT_CUSTOMER(ID,CUSTOMER_NAME)values(?,?)");
stmt.setInt(1,id);
stmt.setString(2,name==null?"":name);
stmt.execute();
}catch(Exceptione){
e.printStackTrace();
}finally{
DbUtils.closeQuietly(stmt);
DbUtils.closeQuietly(conn);
}
}
}

< 示例代码四 >

针对这种应用场景,我们首先需要认识到:上述的三个方法应该属于同一个数据库事务,否则,在并发情况下,将出现由于主键重复而导致数据插入失败的情况。但同时,我们也需要看到:即便上述三个方法的执行位于同一个事务中,但三个方法使用的是不同的数据库连接,虽然在 sequencePlus 方法中将 T_SEQUENCE 表中的数据加 1 ,但在事务并未提交的情况下,由于 Connection 隔离级别的原因,在 getSequenceCurrentVal 方法中,是看不到 sequencePlus 方法中更新以后的数据的,这样,也将导致数据插入失败,因为主键势必跟旧有 ID 值重复。

因此,传统的编程方法中,为克服上述问题,只有在上述的方法中使用同一个 Connection ,才能够保证业务数据的正确。但这样一来,将影响我们以 OO 方法分析问题时的“纯洁”性,很容易让人厌倦。

2.3.  将Connection作为成员变量

另外一种常见的不当编程模式是将 Connection 作为类的成员变量。一般来说,针对 Connection ,我们采取的策略是:用时再申请,用完立即释放。而将 Connection 作为成员变量,将是对该规则的严重挑战,容易引起若干编程错误。举例而言:成员变量级的 Connection ,何时创建?何时释放?倘若在每一个方法体内进行 Connection 的创建与释放,那么将 Connection 作为成员变量又失去了意义;倘若在类的构造期内进行 Connection 的创建,那么又在何时释放它呢?因为在 Java 语言内,你是无法控制对象的生命周期的。

将 Connection 作为成员变量还会产生另外一个问题:资源的闲置浪费。因为在申请连接以后,该资源将在这个对象的生命之期之内一直有效,即使该对象处于非使用状况,这无疑是一种资源的浪费。更有甚者,倘若这种对象过多,将造成数据库达到最大连接数,造成应用运行失败。

3.           金蝶Apusic应用服务器的数据源管理

金蝶 Apusic 应用服务器支持业界主流的各种数据库,在 Apusic 应用服务器之内进行数据源的配置与使用都非常简单,同时,它提供了许多增值特性,能够为应用的正常运行提供额外的保障。

3.1.  数据库连接池的逻辑连接与物理连接

我们注意到: java.sql.Connection 是一个 Interface ,那么,真正实现这个接口的类是什么呢?

我们可以做一个简单的测试案例,在普通的 JavaApplication 中,调用如下方法:

publicvoidshowConnection(){
Connectionconn=null;
try{
Class.forName("oracle.jdbc.driver.OracleDriver");
conn=DriverManager.getConnection(

"jdbc:oracle:thin:@localhost:1521:KEVINORA",

"system","manager");
System.out.println("ConnectionClassis:"+conn.getClass().getName());
}catch(Exceptione){
e.printStackTrace();
}finally{
DbUtils.closeQuietly(conn);
}
}

< 示例代码五 >

得到的输出结果是: ConnectionClassis:

oracle.jdbc.driver.T4CConnection

而在 Apusic 应用服务器中运行如下方法:

publicvoidshowConnection(){
Connectionconn=null;
try{
Contextctx=newInitialContext();
ds=(DataSource)ctx.lookup("jdbc/oracle");
conn=ds.getConnection();
System.out.println("ConnectionClassis:"+

conn.getClass().getName());
}catch(Exceptione){
e.printStackTrace();
}finally{
DbUtils.closeQuietly(conn);
}
}

< 示例代码六 >

得到的输出结果是: ConnectionClassis:com.apusic.jdbc.adapter.ConnectionHandle

明明用相同的 JDBCDriver 连接同一个数据库,为什么取得的 Connection 却是不同的类呢?事实上,通过 Apusic 应用服务器获得的数据库连接其实只是一个逻辑连接,真正的物理连接隐藏在该逻辑连接之内,这是一个典型的 Delegate 模式,而恰恰是这个模式,通过 Apusic 应用服务器对数据源进行管理,将给我们的应用开发带来很多好处:

3.2.  当事务结束以后,在该事务上下文中申请的物理连接,都将主动释放

我们以一个最简单的 StatelessSessionBean 为例:

publicclassSimpleBeanimplementsSessionBean{

publicvoidfoo(){
Connectionconn=null;
try{
Contextctx=newInitialContext();
DataSourceds=(DataSource)ctx.lookup("jdbc/oracle");
conn=ds.getConnection();
System.out.println("notreleaseconnection");
}catch(Exceptione){
e.printStackTrace();
}finally{
//Notclosetheconnection
//DbUtils.closeQuietly(conn);
}
}
}

< 示例代码七 >

SimpleBean 中的 foo 方法的事务属性设置为 Required ,在该方法中,我们申请了一个数据库连接,但并没有释放它,在运行之前,我们通过 SQLPlus 观察 Oracle 数据库的 Session ,得到的结果是:

SQL> select count(*) from v$session;

  COUNT(*)

----------

        18

 

< 图一执行方法之前的 OracleSession>

而在执行完 SimpleBean 的 foo 方法之后,我们再次观察 Oracle 数据库的 Session ,得到的结果是:

SQL> select count(*) from v$session;

 

  COUNT(*)

----------

        18

 

< 图二:执行方法之后的 OracleSession>

由此,我们可以得知:即便由于程序的书写错误,没能够释放申请的数据库连接,但 Apusic 应用服务器在事务完成之后,能够把该事务上下文中申请的物理连接主动释放,这对提升应用的容错性带来一定的好处。

3.3.  当jsp/servlet运行结束以后,在jsp/servlet中申请的物理连接,都将主动释放

同事务中申请的数据库连接会主动释放一样,在 jsp/servlet 中申请的数据库物理连接,当 jsp/servlet 运行完毕以后,如果用户没有释放这些连接, Apusic 应用服务器也将予以主动释放。读者可以尝试自己做一个案例:在 jsp 中申请一个连接,故意不释放,在 jsp 执行完毕以后,可以通过 SQLPlus 或者 Apusic 性能监控工具,查看连接是否已经被应用服务器主动释放。

由上述两节内容我们可以看到, Apusic 应用服务器能够有效避免 2.1 节中所描述的问题。

3.4.  ConnectionSharing:同一个事务上下文中申请的物理连接可以共享

通过共享连接可以更有效地使用资源及提高性能,并且可以防止连接之间的资源锁定问题。

例如两个 EJB 组件 A 和 B ,它们的事务属性都设置为 Required 。在调用 EJBA 的方法时打开了一个数据库连接,并对数据库中的某个表进行了更新操作,而在关闭连接之前 EJBA 调用了 EJBB 的某个方法,同样 EJBB 打开同一个数据库的连接,也对数据库中同一个表进行了更新操作。倘若没有连接共享机制,这两个连接指向的是两个不同的物理连接,在其上执行的数据库操作将会互相锁定,而这种死锁状态是无法恢复的。现在有了连接共享机制可以有效地解决这个问题。在 EJBA 和 B 中所获得的连接对象实际上都指向同一个物理连接。这一个过程可以简单描述如下:

con1=getConnection();
Transaction.begin
performdatabaseoperationoncon1
con2=getConnection();
performdatabaseoperationoncon2
con2.close();
Transaction.commit();
con1.close();

< 示例代码八 >

无论两个连接是在事务边界之内或之外打开和关闭都没有问题。只有在一个事务边界之内连接才会被共享,如果一个连接是在事务边界之外打开的,那么在事务开始时会将此连接参与到事务中,并找到一个具有正确事务场景的物理连接和连接对象相关联。在离开事务场景之后如果连接对象仍未关闭,则将其关联到一个不具有事务场景的物理连接。

可以在部署描述中指定一个资源引用的 res-sharing-scope 属性来允许或禁止连接共享,属性值 shareable 为允许共享, unshareable 为禁止共享,缺省情况下为允许共享。

回到 2.2 节中 Customer 那个测试案例,我们已经说过, Customer 的 sequencePlus 方法、 getSequenceCurrentVal 方法、以及 addCustomer 方法,需要放在一个事务中处理。但在这三个方法中,使用的是不同的 Connection ,而由于 Connection 的隔离级别,将导致插入 T_CUSTOMER 表中的 ID 主键将重复,最终导致事务回滚。利用 Apusic 应用服务器连接共享特性,能够很好的解决这个问题。也就是说:虽然这三个方法申请的逻辑连接是不同的,但逻辑连接内部所使用的物理连接是同一个,这样,将保证不同方法中对数据库的操作结果相见可见,从而保证事务的正常提交。

举例如下:假设在一个 jsp 文件中,这样调用:

<%
     InitialContext ctx =  new  InitialContext();
     String txName =  "java:comp/UserTransaction" ;
     UserTransaction tx = (UserTransaction)ctx.lookup(txName);
     tx.begin();
     new  Customer().addCustomer( "eric" );
     tx.commit();
%>

 

< 示例代码九 >

在上述代码中,通过 UserTransaction 启动一个事务,然后在该事务上下文中,增加一笔 Customer 的记录,我们发觉,在不需要更改 Customer 类的情况下,上述方法能够正常完成。

由此可以得知:在 Apusic 应用服务器中进行应用的开发,我们无需因为考虑数据库 Connection 的隔离级别而影响我们对系统的面向对象的分析方法, Apusic 应用服务器将替我们保证在同一事务上下文中,使用相同的物理连接。

通过 Apusic 应用服务器的这个特性,能够有效的解决 2.2 节中描述的问题。

3.5.  Lazy Connection Association Optimization:数据库连接延迟关联的优化机制

在 3.1 节中我们谈到:通过 Apusic 应用服务器管理的数据库连接分逻辑连接与物理连接,物理连接隐藏在逻辑连接的背后。那么,逻辑连接何时与一个真正的物理连接相关联的呢?在关联的过程之中, Apusic 应用服务器又提供了哪些优化机制呢?举例如下:

J2EE 组件可能会将连接对象保存在其实例变量中从而可以在多个事务之间重复使用,但是如果这个组件在使用一次之后就很少再被用到,那么系统资源将会被组件白白占用而得不到释放,当连接池被占满时就再也无法获得新的连接。 Lazy Connection Association Optimization 是这样一种机制,当 J2EE 组件方法调用完成时,释放连接对象所指向的物理连接以供其他组件使用,连接对象进入一个 Inactive 状态,在这个状态下它不和任何物理连接相关联。当 J2EE 组件需要使用该连接对象时,容器将其激活,将其和一个实际的物理连接相关联。这一过程对于应用组件来说是完全透明的。 J2EE 程序员经常犯的一个错误是忘记关闭连接,特别是发生异常时没有执行正确的清理,过去我们解决这一问题是在方法调用完成时强制关闭所有的连接,现在有了 Lazy Connection Association Optimization 机制可以更完美地解决这一问题。

ConnectionSharing 和 Lazy Connection Association Optimization 是同时起作用的,例如,当一个连接被激活时,它将被包含在当前事务场景中,并与同一事务场景中的其他逻辑连接共享同一个物理连接。

我们在 2.3 节中强调:将 Connection 作为成员变量是一种糟糕的设计模式,但同时,我们也看到:哪怕用户旧有系统中存在这样的用法, Apusic 应用服务器也能够很好的解决由于这种糟糕的设计所带来的缺陷。

4.           总结

本文首先与读者分析了一些错误或者不当的数据库资源使用方法,然后简要介绍了金蝶 Apusic 应用服务器在数据源管理上的一些特性。这些特性,对应用的健壮性及容错性带来一定的好处。但需要再次提醒的是:应用服务器提供的一些增值特性,仅能够当作保障我们应用正常运行的最后一道屏障,我们切不可依赖于这些特性而忽视程序自身的编码质量。一个 J2EE 应用能否正常的运行,程序自身的设计与编码永远是主要因素。

5.           参考资料

注 1 : FindBugs : Sourceforge 上的一个开源工具,能够对源码进行分析从而发现可能出现的编程错误, http://findbugs.sourceforge.net/

注 2 : CommonsDbUtils:ApacheJakarta 项目的 Commons 组件, http://jakarta.apache.org/commons/index.html

注 3 :金蝶 Apusic 应用服务器:国内首家通过 J2EE1.4 认证的应用服务器,请参考 http://www.apusic.com/

 

转载于:https://www.cnblogs.com/zhuyx/archive/2007/06/07/10402058.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/381745.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CodeForce 180 C ——Letter

题意&#xff1a;给定一定长度的字符串&#xff0c;要求规则必须是所有大写字母必须在小写字母的前面&#xff0c;也就是所谓的11111000模式。 思路&#xff1a;暴力&#xff0c;用O&#xff08;n&#xff09;的算法处理一下字符串&#xff0c;得到每个字符位前面有多少位小写以…

CodeForce 168 C——Wizards and Trolleybuses

题意&#xff1a;给定n个火车&#xff0c;加速度&#xff0c;和铁轨长度&#xff0c;然后是每个火车的最大速度和开始出发的时间&#xff0c;问每辆火车到达终点的时刻。 思路&#xff1a;暴力。考虑路上的状态可能不太容易思考&#xff0c;那就直接考虑起点和终点&#xff0c;…

树形数据查询示例

--树形数据查询示例--作者: 邹建if exists (select * from dbo.sysobjects where id object_id(N[tb]) and OBJECTPROPERTY(id, NIsUserTable) 1)drop table [tb]GO --示例数据create table [tb]([id] int identity(1,1),[pid] int,name varchar(20))insert [tb] select 0,中…

UVA 10588—— Queuing at the doctors

题意&#xff1a;给定n个工人和m个医生&#xff0c;然后进行体检&#xff0c;每个医生每秒接待一个工人&#xff0c;每个人都有一个体检项目顺序和时间&#xff0c;问最后一个员工完成体检的时间。 思路&#xff1a;优先队列模拟&#xff0c;建立m个项目的优先队列&#xff0c;…

[导入]javascript总结

1.动态添加一行&#xff0c;和删除当前行<script> var count0; function ff() { var txt1document.getElementById("Text1"); var table1document.getElementById("table1"); rowNotable1.rows.length; Trtable1.insertRow(rowNo); Tr.id"tr&qu…

UVA 10410——Tree Reconstruction

题意&#xff1a;给定一颗树的BFS和DFS&#xff0c;求这棵的每个节点。 思路&#xff1a;用栈模拟维护。对应的BFS为每个节点到根节点的距离&#xff0c;然后比较当前节点和栈顶节点与根的距离&#xff0c;如果当前节点大&#xff0c;则为栈顶节点的孩子&#xff0c;否则弹出继…

求助:DataGrid加行号的问题

我的数据是fname,lnameprivatevoidPage_Load(objectsender, System.EventArgs e) { if(!IsPostBack) { myConnectionnew SqlConnection("server127.0.0.1;uidsa;pwdsa;databaseqqq;"); strSQL"SELE…

UVA 10895——Matrix Transpose

题意&#xff1a;给定一个矩阵&#xff08;每一行有几个非0的数据&#xff0c;对应的位置以及数值&#xff09;&#xff0c;输出这个矩阵的转置矩阵。 思路&#xff1a;直接模拟&#xff0c;用两个vector&#xff0c;一个维护数值&#xff0c;另外一个维护行号。注意长度为0时输…

UVA 514——Rails

题意&#xff1a;给定两个序列A和一到n的排列B&#xff0c;问能否通过一个栈的push和pop操作使得A变成B。 思路&#xff1a;直接构造一个栈模拟即可&#xff0c;注意换行。 code&#xff1a; #include <bits/stdc.h> using namespace std;int v[1005],n;int main() {whi…

UVA 536——Tree Recovery

题意&#xff1a;给定一颗树的先根遍历和中根遍历&#xff0c;然后求后根遍历。 思路&#xff1a;先根遍历的第一个为root&#xff0c;然后找到root在中根的位置&#xff0c;进而递归左右儿子求解。 code&#xff1a; #include <iostream> #include <cstdio> #incl…

CVS的使用教程(转)

、什么是CVS? CVS - Concurrent Versions System&#xff08;并发版本管理系统&#xff09;是一个版本控制管理系统&#xff0c;它是目前最为广泛使用的一个系统。 在多人共同开发一个大型项目时&#xff0c;源代码的维护和版本维护是一件令人头疼的事情&#xff0c;由于多人开…

6.22打包建立ISS虚拟目录,安装完运行你想运行的程序

http://installshield.jaron.cn/forum/dispbbs.asp?boardID3&ID284614&page1#include "ifx.h" #define Emty "" //宏定义DOS功能把Emty 替换为"" prototype RegUnInstall(STRING);string szDir, szVirtual;prototype void CheckReq…

UVA 11991——Easy Problem from Rujia Liu?

题意&#xff1a;给定一个数组&#xff0c;然后有若干组询问&#xff0c;每次询问求第k个v出现的位置。 思路&#xff1a;用vector构造模拟&#xff0c;吧相同的数的位置放在同一个vector里&#xff0c;对于每次查询输出mp[v][x-1]; code&#xff1a; #include <bits/stdc.h…

压缩图片上传到数据库

保存到数据库public int DyfcListInsert(int id,string name,string username,string content,Byte[] photo) { string sql "S_DyfcList_Insert"; SqlCommand sqlcmd new SqlCommand(sql,DwzxConfiguration.ConnectDB() ,DwzxConfigu…

支持.NET的分布式缓存系统memcached

http://www.infoq.com/news/2007/07/memcached 转载于:https://www.cnblogs.com/didasoft/archive/2007/07/17/821766.html

UVA 1160——X-Plosives

题意&#xff1a;给定一些化合物&#xff08;含有两个元素&#xff09;&#xff0c;当满足k个化合物且有k个元素的时候会发生爆炸&#xff0c;问多少个化合物是不能装车的。 思路&#xff1a;并查集的简单应用。实际上满足条件的时候是一个环&#xff0c;因此用并查集简单判环即…

UVA 1329——Corporative Network

题意&#xff1a;有n个节点&#xff0c;然后执行I u&#xff0c;v&#xff08;把u的父节点设为v&#xff09;和E u&#xff08;询问u到根节点的距离&#xff09;。 思路&#xff1a;并查集。加了信息的并查集&#xff0c;在路径压缩的同时维护距离d[i]; code&#xff1a; #inc…

Atlas 不仅仅是异步

最近学习研究了一下微软的AJAX框架,Atlas.这个框架对于实现AJAX里的异步请求,无刷新等技术非常的简便,功能也很强大,当然这些都是建立在DOTNET平台上. 对于这个框架,给我印象很深的就是,作为一个新的框架,能够与现有的ASP.NET技术实现几乎无缝的整合,并且只需要添加若干行…

UVA 11988——Broken Keyboard (a.k.a. Beiju Text)

题意&#xff1a;给定一个字符串&#xff0c;然后【会将光标跳转到头&#xff0c;】会将光标调到尾&#xff0c;问最后正确的输入。 思路&#xff1a;直接用list来模拟即可&#xff0c;【的时候就在头插&#xff0c;】就在尾插&#xff0c;也可根据递归顺序解。 code&#xff1…

使用CodeDom生成程序集

usingSystem;usingMicrosoft.CSharp;usingSystem.CodeDom.Compiler;usingSystem.CodeDom;namespaceTest.CUI{ class Program { static void Main() { // 创建编译器对象 CSharpCodeProvider p new CSharpCodeProvider(); ICodeCompiler cc p.CreateCo…