posts - 195, comments - 34, trackbacks - 0, articles - 1

jndi全局注册表和enc的基本概念
   通俗的将jndi就是对ejb容器中所有的资源和组件进行管理的花名册,通过该服务,可以很方便的找到希望使用的资源。当组件被部署到服务器上后,该组件就会被自动注册到花名册上,以便用户使用,如果组件被卸载,花名册上的记录就会自动消失。
    jndi中注册的所有的资源和组件可以被服务器内外的各种程序请求或者访问,是一种全局性的资源!但是ejb中不同的组件通过全局性的jndi服务器形成依赖关系,则会给系统造成不稳定的因素!因此就引入了enc(ejb组件的企业名称上下文)的概念!通过她来实现不同组件之间的引用关系!!!!
在上一篇文章中写到了通过标注的方式实现方向依赖注入!还以上篇为例:
有接口:HelloWordRemote  HelloWordLocal ICal(Remote)
而HelloWordBean实现了分别实现了他们
另外又有了个远程接口:
@Remote
public Interface MyService{
public String helloWord();
public int add(int a,int b);
}
一个类想实现这个接口并且在这个类中引用了...如下:
@Stateless
public class FacedServcie implements MyService{
private HelloWordLocal myserv1;
private ICal ical;
.....
....
}
可以通过配置文件实现反向依赖注入:
<ejb-jar
       xmlns="http://java.sun.com/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
                           http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd"
       version="3.0">
   <enterprise-beans>
      <session>
         <ejb-name>FacedServcie</ejb-name>
         <ejb-ref>
           <ejb-ref-name>abc1</ejb-ref-name>
           <ejb-ref-type>Session</ejb-ref-type>
           <remote>com.ICal</remote>
           <mapped-name>HelloWordBean1/remote</mapped-name>
           <injection-target>
              <injection-target-class>
                 xiaoxiao.FacedService
              </injection-target-class>
              <injection-target-name>
                 ical
              </injection-target-name>
           </injection-target>
         </ejb-ref>
.........(对于另一个组件的配置)
      </session>
   </enterprise-beans>
</ejb-jar>
还可以通过检索的方式来实现:
@Stateless

public class FacedServcie implements MyService{
private HelloWordLocal myserv1;
private ICal ical;
public String helloWord(){
try{
InitialContext ctx=new InitalContext();
ical=(ICal)ctx.lookup("java:comp/env/abc1");
//其中java:comp/env是组件局部enc所有记录的根路径而abc1是在配置文件中注册的名字!
}catch(NamingException e){}
if(ical!=null){
return ical.helloWord();
}else{
return "error!";
}
}
....
}
配置文件如下:
<ejb-jar
       xmlns="http://java.sun.com/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
                           http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd"
       version="3.0">
   <enterprise-beans>
      <session>
         <ejb-name>FacedServcie</ejb-name>
         <ejb-ref>
           <ejb-ref-name>abc1</ejb-ref-name>
           <ejb-ref-type>Session</ejb-ref-type>
           <remote>com.ICal</remote>
           <mapped-name>HelloWordBean1/remote</mapped-name>         
         </ejb-ref>
.........(对于另一个组件的配置)
      </session>
   </enterprise-beans>
</ejb-jar>
本人建议使用第一种反向依赖注入的方式!
还有其他的一些注入:如持久化单元注入,资源型注入 数据源类型的注入。。。

posted @ 2009-03-24 10:26 小强摩羯座 阅读(408) | 评论 (0)编辑 收藏

EJB事务
在ejb组件设计中,组件行为有时需要具备事务特征,即使用事务保证组件行为的完整性,使组件要么执行成功,要么回到起点.等价没有执行!
讨论事务时要明确两个概念:
事务范围
算法的可恢复性与事务性:如果事务中的算法动作结果受事务提交或者回滚影响,则该算法具有事务可恢复性,否则就不具备事务可恢复性.
事务在本质上就是一个管理系列算法动作一致性的对象!ejb提供了两种事务类型:声明性和程序性事务
声明性事务:由ejb容器负责事务对象的 生成,管理,销毁;具体算法在事务上的注册,事务的提交和回滚等
主要介绍声明性的事务:
   ejb容器为其中的所有的ejb组件提供了一种默认的事务模式:Requires
在该模式下面,组件中的方法如果在事务环境下被调用(即客户端调用了该组件的方法),则方法中的逻辑会被添加到客户端的事务环境中,和客户端的程序使用相同的事务控制对象.如果不在事务环境中调用(在客户端没有调用该组件中的方法),ejb容器就创建新的事务对象,管理该方法中的所有逻辑.
例如:
ejb组件:
   @Statefull
public class personmanager inplements IPersonAdmin{
@PersistenceContext(unitName="mydb")
private EntityManager manager;
@EJB(mappedName="MySession/remote")
private IPersonAdmin mytools;
public List<person> QueryAll(){
....
}
public void createPerson(Person p){
Person pobj=new Person();
pobj.setName("ziaoxioa");
#1 manager.persist(pobj);
#2 mytools.createPerson(p);
#3 manager.persist(null);
}
客户代码:
...
myinf.createPerson(p);
...
默认情况下,ejb读物起会为ejb组件中的所有方法配置一中requires型的事务服务.在这种事务模式下面,如果方法被在事务环境下调用,方法就使用客户端事务对象和事务环境ruguo不在事务环境中被调用,则ejb容器就会创建新的事务对象和环境来负责管理该方法的逻辑完整性!由于上面的#3错误,所以数据库中不会添加任何记录!
ejb容器为ejb组件添加的默认的事务策略能够满足多数情况下的算法要求,特殊情况下,可以通过@TransactionAttribute和<container-transaction>标记修改组件的默认事务策略!如下:
<ejb-jar
       xmlns="http://java.sun.com/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
                           http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd"
       version="3.0">
<assembly-descriptor>
   <container-transaction>
      <method>
        <ejb-name>MySession</ejb-name>
        <method-name>createPerson</method-name>
      </method>
      <trans-attribute>RequiresNew</trans-attribute>
   </container-transaction>
</assembly-descriptor>
</ejb-jar>
@TransactionAttribute(REQUIRES_NEW)
public void createPerson(Person p){
manager.persist(p);
}
这样MySession中的createPerson方法事务修改成了RequiesNew模式,在这种模式下,ejb容器不论客户端是否具有事务特征,为createPerson创建一个新的事务对象,此时两个createPerson在完全不同的事务对象控制下,所以#2可以提交到数据库中,#1,#3则回滚
声明性事务模式:
 Required  ejb容器为ejb组件方法提供的默认事务模式,在这种模式下,如果调用程序具备自身的事务对象,则组件方法就和客户程序使用相同的事务对象,否则ejb容器就会创建新的事务对象来管理组件方法逻辑
 Supports  在这种模式下,如果调用程序具备事务特征,则组件方法就会调用调用程序事务对象管理自身逻辑,如果调用程序不包含任何的事务对象,则组件方法也处于无事务管理状态
 NotSupported  这种模式下,组件方法不支持事务,同时也会忽略掉调用程序中的事务对象,组件方法中的逻辑不受客户程序事务的提交或回滚的影响
 RequiresNew  在这种模式下,忽略调用程序具备的事务特征,ejb容器会新创建一个新的事务对象管理组件方法逻辑
 Mandatory  ejb容器不会为该方法创建新的事务管理对象。该组件方法的调用程序必须提供事务对象,处于事务环境中,否则程序将产生异常javax.ejb.EJBTransactionRequiredException
 Never  这种模式下,该方法不会被ejb容器提供事务对象服务,并且调用程序也不能处于事务环境中,否则将产生EJBException异常

下面的程序首先清楚组件上的默认事务模式,然后在为方法设定具体事务属性:
<assembly-descriptor>
   <container-transaction>
      <method>
        <ejb-name>MySession</ejb-name>
        <method-name>*</method-name>
      </method>
      <trans-attribute>NotSupported</trans-attribute>
   </container-transaction>
   <container-transaction>
      <method>
        <ejb-name>MySession</ejb-name>
        <method-name>createPerson</method-name>
      </method>
      <trans-attribute>Required</trans-attribute>
   </container-transaction>
</assembly-descriptor>
声明性事务的控制:
@Stateless
/*10*/public class PersonManager implements IPersonAdmin
/*11*/{
/*12*/     @Resource
/*13*/     private EJBContext ctx;
/*14*/     @PersistenceContext(unitName="mydb")     
/*15*/   private EntityManager manager;
/*16*/   @EJB(mappedName="MySession/remote")
/*17*/   private IPersonAdmin mytools;
/*18*/   public List<Person> QueryAll()
/*19*/   {     
/*20*/         Query q=manager.createQuery("from Person c");
/*21*/         List results=q.getResultList();
/*22*/                 List<Person> result=(List<Person>)results;
/*23*/                 return result;
/*24*/   }
/*25*/   public void createPerson(Person p)
/*26*/   {
/*27*/         Person pobj=new Person();     
/*28*/             pobj.setName("zhanghao");     
/*29*/             pobj.setAge(new Integer(32)); 
/*30*/             pobj.setSex("male");          
/*31*/         manager.persist(pobj);
/*32*/         boolean result=ctx.getRollbackOnly();
/*33*/         if(!result)
/*34*/         {
/*35*/            ctx.setRollbackOnly();                 
/*36*/         }
/*37*/   }
/*38*/}
该方法使用gejb容器提供的默认的事务模式,事务会在方法结束时自动提交。
getRollbackOnly()返回房前事务的状态,true表示已经回滚,false表示没有回滚。

posted @ 2009-03-24 10:25 小强摩羯座 阅读(1249) | 评论 (0)编辑 收藏

视频总结-jndi
jndi:java命名和目录接口
     jndi把object和context(还可以有subcontext)组织成一个jndi树
这样object就可一被绑定到不同的context上面
jndi是一种查找服务,用于查找:
    web应用环境变量
    EJB和他们的环境变量
    通过DataSource的数据库连接池
    JMS没有表和连接工厂
    其他服务
不要将jndi当做数据库使用
    jndi对象存储在内存中
    访问jndi对象与网络性能有关
jndi树:
InitialContext是JNDI树所有搜索的起点
对象绑定到jndi树上,java对象是树叶,context是节点
一个绑定把对象与一个逻辑名和一个context关联起来
创建Initial Context
从服务器端对象连接到jndi
//创建一个InitialContext
context ctx=new InitialContext();//链接到当前的jndi树上
从任何一个地方连接到jndi
  .创建Environment对象
   weblogic.jndi.Enviroment env=new Weblogix.jndi.Enviroment();
   填写Enviroment
   env.setProviderurl("t3://127.0.0.1:7001");//weblogic server的位置
   env.setSecurityPrincipal("system");//安全信息
   env.setSecurityCredentiais("password");
   //使用Environment对象创建InitialContext
   context ctx=env.getInitialContext();
  .hashtable的用法....
创建subcontext
  创建一个用于绑定对象的subcontext
  创建Environment对象
  context ctx=env.getInitialContext();//首先获得已经存在的subcontext或者initialcontext
  填写Environment
  context subcontext=ctx.createSubcontext("newSubcontext");
  subcontext.rebind("boundobject",object);
  subcontext.close();
  ctx.close();
其他链接到jndi的方法:
 
使用ConstantProperties名和hashtable类:
 HashTable env=new HashTable();
 env.put(Context.initial_context_factory,"weblogic.jndi.WLInitialContextFactory");
 env.put(Context.procider_url,"t3://localhost:7001");
 env.put(Context.security_principal,"system");
 env.put(Context.crrdentials,"password");
 Context ctx=new InitialContext(env);
另一个例子:使用hardcoded名和properties类
 Properties env=new Properties();
 env.setProperties("java.naming.factory.initial","weblogic.jndi.WLInitialContextFactory");
 env.setProperties("java.naming.provider.url","t3://localhost:7001")
 env.setProperties("java.naming.security.principal","system");
 env.setProperties("java.naming.security.credentials","password");
 Context ctx=new InitialContext(env);
jndi.properties
.jndi.properties文件为所有的Initial Contexts设置默认的属性
.jndi.properties文件的搜索顺序
    .classpath
    .$JAVA_HOME/lib
实例:
 java.naming.factoyr.initial=weblogic.jndi.WLInitialContextFactory
 java.naming.security.url=t3://localhost:7001
 java.naming.security.pricipal=system
 java.naming.security.credentials=password
 使用默认值:
 Context ctx=new InitialContext();
从jndi查找:
.lookup()从jndi树获得对象
.通过lookup()返回的对象必须映射到他们合适的类型
try{
//先获得InitialContext
//Context ic=new Context();
Object obj;
obj=ic.lookup("javax.transation.UserTransaction");
UserTransaction ut=(UserTransaction)obj;
ut.begin();
.....
ic.close();
}catch(NamingEcxeption e){....}
远程绑定对象:
.远程绑定到远程命名服务的对象必须是序列化的
.访问命名服务时,对象是采用复制机制
查找的对象是序列化到客户端的
一个binding实例:
public static Context getInitialContext()throws NamingException{
   创建Environment对象
   weblogic.jndi.Environment env=new Weblogix.jndi.Environment();
   填写Enviroment
   env.setProviderurl("t3://127.0.0.1:7001");//weblogic server的位置
   env.setSecurityPrincipal("system");//安全信息
   env.setSecurityCredentiais("password");
   //使用Environment对象创建InitialContext
   context ctx=env.getInitialContext();
   return ctx;
}
//获得Initial Context
Context ctx=getInitialContext();
//创建名为band的对象
Bank myBank=new Bank();
//把对象绑定到jndi树
ctx.rebind("theBank",myBank);
ctx.close();

posted @ 2009-03-24 10:22 小强摩羯座 阅读(299) | 评论 (0)编辑 收藏

EJB3.0中的依赖注入,截获器及其在WebLogic Server 10中的扩展2007-09-05 来自:lizhe1985  [收藏到我的网摘]



1 前言

  与EJB2.1相比,EJB3.0规范引入了两个重要概念:依赖注入(DI:Dependency Injection)和截获器(Interceptor),本文首先介绍了这两个概念并给出相关示例,然后分析了EJB3.0规范在这两方面的不足之处,最终深入探讨了WebLogic Server 10对它们的支持和扩展。

2 依赖注入

  2.1 基本概念

  依赖注入是从开源社区的一些著名轻量级容器(如Spring、Pico container)中所发展出来的概念,其主要思想就是由容器而不是对象本身来负责处理对象之间的依赖关系。与传统的服务定位器相比,依赖注入具有易测试、弱侵入性等优点,这也就是为什么在最新的Java EE 5规范中引入它的原因。

  对于EJB3.0来说,依赖注入就是由容器负责查找被依赖的对象并注入到依赖bean中,而bean本身不再需要进行JNDI或者context查询。此外,依赖注入发生在任何业务方法被调用之前,而且支持setter方法注入和域注入两种方式。

  通过与标注结合使用,在bean类中声明依赖注入是非常简单的(当然,也可以在部署描述符文件中声明依赖注入):

    @EJB用于注入EJB业务对象
    @PersistenceUnit 用于注入EntityManagerFactory
    @PersistenceContext 用于注入EntityManager
    @Resource 用于注入其它资源对象,如连接工厂、消息目标等

  2.2 示例

@Stateless
public class ServiceBean implements Service {
private javax.sql.DataSource myDS;
@Resource(mappedName=“LocalDataSource")
public void setMyDS(javax.sql.DataSource ds) {this.myDS = ds; }
@EJB(beanName=“AccountBean")
private Account account;
}

  在无状态会话bean ServiceBean中,声明了两个依赖:一个是数据源,一个是业务接口。在运行期间,EJB3.0容器一旦创建了ServiceBean的实例,就会分别通过方法注入和域注入将数据源对象和业务对象注入到ServiceBean中。

3 截获器

  3.1 基本概念

  作为EJB3.0中提出的新概念,截获器是可以对bean的业务方法和生命周期事件进行拦截的组件。截获器需要由@Interceptors 或发布描述符文件中相关的标签指定。截获器可以带有状态而且可以进行依赖注入,其生命周期与其所绑定的EJB bean实例的生命周期一致。

  定义在截获器中用于拦截目的的方法被称为截获器方法,其中,针对业务方法的截获器方法通过@AroundInvoke标注或发布描述符文件中相关的标签指定;针对生命周期回调的截获器方法通过@PostConstruct, @PreDestroy等标注或发布描述符文件中对应的标签指定。

  截获器分为四类:

    缺省截获器:可作用于ejb-jar中定义的所有EJB bean。缺省截获器只能定义在DD中,不存在相应的标注
    类级截获器:只能作用于所指定的EJB bean
    方法级截获器:只能作用于所指定的某个EJB bean业务方法,方法级截获器不能用于拦截bean的生命周期事件
    bean 级截获器:又被称为自我截获器,因为截获器同时就是EJB bean本身,此时相关的截获器方法只能作用于该EJB

  3.2 截获器链的调用顺序

  因为可以为EJB定义多个截获器,所以存在截获器链的调用顺序问题,缺省情况下,以下原则被遵循:

    调用顺序依次是缺省截获器、类级截获器、方法级截获器以及bean级截获器
    对于同类别的截获器,按照声明的顺序调用
    总是优先调用父类的截获器方法。

  此外,EJB3.0规范还提供了更灵活的选项,详细信息请参考EJB3.0规范中“截获器”一节:

    在发布描述符文件中设置“exclude-default-interceptors” 可以取消对缺省截获器的调用,而应用“exclude-class-interceptors”则取消对类级截获器的调用
    为了替换缺省的截获器链调用顺序,可以设置发布描述符文件的“interceptor-order”。

  3.3 示例

@Stateless(name="Trader")
@Interceptors(ClassInterceptor.class)
public class TraderBean implements Trader {
@Interceptors(MethodInterceptor.class)
public String sell(String stockSymbol) {

}
@AroundInvoke
public Object selfInterceptor(InvocationContext) throws Exception {

}
}

  在无状态会话bean TraderBean中,声明了两个截获器:一个类级截获器,一个方法级截获器,此外,通过使用@AroundInvoke标注,TraderBean的方法selfInterceptor被声明为截获器方法,所以TraderBean就是bean级截获器。

4 EJB3.0规范在依赖注入和截获器方面的不足

  从依赖注入方面来说,EJB3.0目前支持的注入类型是非常有限的,诸如List、Map之类的集合类型都不能被注入。

  另一方面,EJB3.0的截获器可被看作是一种AOP模型,截获器类相当于方面(Aspect),而截获器方法相当于通知(Advice),但这种AOP是非常简单的:

    不支持引入(Introduction);
    不支持切入点(Pointcut)模式
    通过标注告诉容器某个EJB需要使用截获器,从而使得这种AOP模型具有一定的侵入性

5 WebLogic Server 10 EJB3.0容器简介

   5.1 Pitchfork 简介

  Pitchfork是由Interface21与BEA合作开发的一个具有Apache License的开源项目,作为Spring的“增值”项目,开发Pitchfork有两个目的:

    当前应用服务器可使用Pitchfork实现Java EE 5中的依赖注入,标注处理以及EJB3.0中的截获器。
    在Spring容器中支持Java EE 5的标注

  5.2 WebLogic Server 10 EJB3.0容器的架构

  WebLogic Server 10完全支持Java EE 5和EJB3.0,其EJB3.0 容器是基于Pitchfork来实现依赖注入与截获器的,而Pitchfork又利用了Spring的依赖注入和AOP。此时,WebLogic Server 10的用户有两种选择:

    使用EJB3.0标准模型: EJB bean实例由EJB容器创建,然后由Pitchfork实施Java EE 5 的依赖注入和截获器,这是WebLogic Server 10的缺省配置。
    使用Spring扩展模型:EJB bean实例由Spring容器创建并实施Spring的依赖注入,然后由Pitchfork实施Java EE 5的依赖注入,EJB3.0截获器和Spring AOP。

6 Spring扩展模型

  6.1 使用Spring扩展模型的主要开发步骤

    下载某个版本的Spring类库(最好是由BEA指定的版本),至少需要包括以下jar文件:spring.jar, aspectjweaver.jar, commons-logging.jar, log4j-1.2.14.jar,如果需要,还可以增加其它jar文件。
    将这些jar文件加入到WebLogic Server 10的类路径中。
    创建文本文件Weblogic.application.ComponentFacotry以标识当前应用将使用Spring扩展模型,其内容只有一行:    Weblogic.application.SpringComponentFactory。
    创建/META-INF/services子目录,然后放入前面创建的文件Weblogic.application.ComponentFacotry
    建立Spring 配置文件,并放入到/META-INF目录下,如果是EJB应用程序,Spring配置文件的名字应该是spring-ejb-jar.xml,如果是Web应用程序,Spring配置文件的名字则是spring-web.xml.
    为了使EJB bean能利用Spring的依赖注入与AOP,需要同时将其定义在spring-ejb-jar.xml(或spring-web.xml)中,从Spring角度来看,这就是一个普通的Spring bean,只不过它的id或name必须与对应的EJB name相同。
    将上述目录文件、相关的Java类、部署描述符等打包成标准的jar文件、war文件或ear文件。

  需要注意的是,在Spring配置文件中定义的bean,如果它也是一个EJB bean,则该Spring bean的class属性会被忽略以避免潜在的冲突。

  6.2 示例

  示例一:利用Spring的依赖注入。本例将演示两点,(1) 注入一个集合类型,(2) 注入一个Spring POJO对象

  (1) 开发一个bean类

@Stateless(name="AccountManagementBean")
@Remote({examples.AccountManagement.class})
public class AccountManagementBean
{
private List accountList;
private BusinessObject businessObject;
public boolean doBusiness(String account) {
if (accountList.contains(account)) {
businessObject.doBusiness();
return true;
}
return false;
}
public BusinessObject getBusinessObject() {
return businessObject;
}
public void setBusinessObject(BusinessObject businessObject) {
this.businessObject = businessObject;
}

public List getAccountList() {
return accountList;
}
public void setAccountList(List accountList) {
this.accountList = accountList;
}
}
  (2) 一个POJO

public class BusinessObjectImpl implements BusinessObject
{
private String result;

public void doBusiness() {
System.out.println("I am doing business, the result is: " + result);
}

public String getResult() {
return result;
}

public void setResult(String result) {
this.result = result;
}
}
  (3) Spring-ejb-jar.xml


<beans>
<bean name="BusinessObject" class="examples.BusinessObjectImpl">
<property name="result" value="success" />
</bean>
<bean id="AccountManagementBean">
<property name="businessObject" ref="BusinessObject" />
<property name="accountList">
<list>
<value>Safin</value>
<value>Amy</value>
<value>Albert</value>
<value>Yang</value>
</list>
</property>
</bean>
</beans>

  示例二:利用Spring 的AOP。本例演示了混合使用JEE interceptor、基于代理的Spring AOP以及AspectJ的Aspect

  (1) 开发一个bean类

@Stateless(name="AccountManagementBean")
@Remote({examples.AccountManagement.class})
public class AccountManagementBean
{
public AccountManagementBean() {}
@Interceptors({examples.JEEInterceptor.class})
public boolean doBusiness(String account) {
System.out.println("I am doing business " + account);
return true;
}
}

  (2) 开发一个JEE interceptor

public class JEEInterceptor {
@AroundInvoke
private Object intercept(InvocationContext inv) throws Exception{
System.out.println("I am in JEEInterceptor.intercept()");
return inv.proceed();
}
}

  (3) 开发一个Advice

public class SimpleAdvice implements MethodInterceptor{
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("I am in Spring AOP interceptor");
return invocation.proceed();
}
}

  (4) 开发一个Pointcut

public class SimpleStaticPointcut extends StaticMethodMatcherPointcut {
public boolean matches(Method method, Class cls) {
return "doBusiness".equals(method.getName());
}
public ClassFilter getClassFilter() {
return new ClassFilter() {
public boolean matches(Class cls) {
return AccountManagementBean.class.isAssignableFrom(cls);
}
};
}
}

  (5) 开发一个AspectJ Aspect

@Aspect
public class Aspects {
@Around("execution(* examples.AccountManagementBean.doBusiness(..))")
public Object around(ProceedingJoinPoint pjp) {
System.out.println(“I am in AspectJ aspect);
Object retVal = null;
try {
retVal = pjp.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
return retVal;
}
}

  (5) Spring-ejb-jar.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
<bean id="AccountManagementBean" />
<bean id="myAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="advice">
<bean class=”examples.SimpleAdvice” />
</property>
<property name="pointcut">
<bean class="examples.SimpleStaticPointcut" />
</property>
</bean>
<bean id="MyAspect" class="examples.Aspects" />
</beans>

  6.3 优点

  提供Spring扩展模型具有如下好处:

    可以在同一个发布单元中混合使用EJB bean和Spring POJO,而且一个EJB bean可以同时是一个Spring POJO,因此被EJB容器和Spring容器同时管理
    有效地克服了EJB3.0 规范在依赖注入和截获器方面的一些缺陷
    为用户提供更灵活的选择:
      如果标准的EJB 依赖注入以及截获器可以满足应用需求,或者可移植性是优先考虑的因素,则选用标准模型
      如果标准的EJB依赖注入以及截获器不能满足应用需求,或者不用考虑可移植性,或者需要集成一些Spring应用,则应选用Spring扩展模型

7 Spring AOP在WebLogic Server 10 Spring扩展模型中的使用限制及说明

  7.1 代理生成方式

  Spring AOP采用的是一种基于代理的框架,目前有两种生成代理的方式:JDK动态代理和CGLIB。在WebLogic Server 10的Spring扩展模型中,目前不支持CGLIB方式,这主要是因为生成的EJB类都是final的

  7.2 代理创建

  在Spring中,代理或者是通过诸如ProxyFactoryBean之类的工厂bean明确创建,或者是通过“自动代理”创建器隐含创建。

  在WebLogic Server 10中,Pitchfork已经隐含提供了类似的“自动代理”机制,所以原有的Spring代理创建方式是无效的,换句话说,用户只需要在Spring配置文件中定义目标bean以及对应的Spring Advisor和AspectJ Aspect, 不需要再定义ProxyFactoryBean和“自动代理”创建器,Pitchfork会自动为我们创建AOP代理

  7.3 截获的顺序问题

  在Spring扩展模型中,可以混合使用JEE截获器、Spring Advisor以及AspectJ Aspect,此时的调用顺序为JEE截获器、Spring Advisor以及AspectJ Aspect。

  7.4 bean的实例化模式

  缺省方式下,Spring bean是Singleton的,即Spring容器总是返回同一个bean实例,在Spring扩展模型中,这意味着同一个Spring Advisor或AspectJ Aspect实例将被应用到不同的EJB bean实例上,如果Advisor或Aspect实例是有状态的,这可能会产生问题,为此我们推荐如下的使用方式:

   SLSB & MDB
    带有状态的Advisor或Aspect
     不推荐用带有状态的Advisor或Aspect,除非用户意识到相关的后果并认为这种后果是可以接收的,此时的实例化模式取决于具体应用
    没有状态的Advisor或Aspect
     采用Singleton实例化模式
   SFSB
    带有状态的Advisor或Aspect
     正常情况下应该采用的实例化模式是Prototype,除非用户意识到采用Singletong的后果并认为这种后果是可以接收的
    没有状态的Advisor或Aspect
     采用Singleton实例化模式

8 结束语

  依赖注入和截获器的引入,使得EJB3.0克服了很多EJB2.1的缺点,但与开源社区中流行的轻量级容器相比,EJB3.0无论是在依赖注入的范围还是在AOP的易用性方面都还存在一定的不足。而最近BEA所推出的WebLogic Server 10不但完全支持EJB3.0,而且对其进行了有效扩展,这使得一方面可以充分利用在开源社区中出于领导地位的Spring在依赖注入和AOP方面的强大能力,另一方面又紧密依托WebLogic Server 10在事务、安全、集群方面领先的技术,从而为EJB3.0用户开发出易维护、可扩展、高效率的企业应用程序打下坚实的基础。

作者简介

  李钢 dev2devid: cc_masterli ,BEA研发中心WebLogic Server 研发团队成员,西安交通大学计算机专业博士,一直从事java中间件领域的研发,2005年加入BEA,现为EJB3 中国研发小组的负责人。

posted @ 2009-03-23 18:49 小强摩羯座 阅读(1007) | 评论 (0)编辑 收藏

为什么要读博ZZ
 
 
宇.凡
     
  首先,谈谈为什么要读博?

  1、如果你压根就没想好这个问题,完全是随波逐流,或者证明自己是一个"好学生",或者认为考博可以带来生活的翻天复地的变化,或者认为读博就是混个学位,为了以后好提升,那么,请你谨慎考虑。
  首先,博士并不一定意味着高收入。收入更多的是与行业与职位相关,而不是与学位相关的。

  其次,既便为了想进高校而读博,也要事先考虑一下是不是喜欢那种生活方式,能否接受那种清苦?能不能坐冷板凳?不然,高校的日子也不好过,竞争一样激烈。而考博只不过是"多米诺骨牌"的第一个环节。等你博士毕业之后,不继续自己的科研,可是又已经付出那么多青春、热情和心血,放弃是否舍得?如果继续从事科研,就要考虑自己是否热爱这种寂寞的无人喝彩的工作,并且身边都是精英,你能否承受那种压力?要知道,真正的名专家名学者毕竟是少数,大多数人是要默默无闻、平平淡淡过一生的。你也可以有更好的未来,但那意味着加倍的付出和努力。

  2、如果已经想好了,比如就是为了提高自己的科研能力,扩大自己的知识面,或者仅仅是体验一下这种生活,或者证明一下自己,或者临时没有更好的出路,想过渡一下,或者干脆就是喜欢从事科研,那么,无论任何理由,只要是坚定的,能说服自己的,那么你就可以选择自己的活法。祝贺你,你就可以尝试。

  接下来,我们看一下读博士可能需要什么?

  1、高智商就不要说了,还要有高的情商。这样才能保证自己高处能胜寒,或者寂寞的时候依然美丽着。

  但凡读到博士的人,总是一些人群中的佼佼者。要么是绝顶聪明,要么是极端有毅力,要么就是非常灵活,总之,可谓八仙过海、各显神通。我在读博期间的感受之一就是,周围的人的确不简单,都有令人非常佩服的一面,可能再"混"日子的人,跟一般人相比也有一份自律和坚持。所以这是一个可以相互约束、相互见证、共同进步的群体。高智商基本是读博的第一要素。

  但是不要以为光有高智商就有好的生活。高情商也很重要,比如你要经常想一下自己是什么样的人?想过和适合过什么样的生活?你怎样让自己在纷繁的乱世中保持一份清醒,远离物质诱惑?你怎样让自己保持快乐?因为,的确是有一些人没想好就进来了,所以学得很累,日子过得很牵强,时不时可能还觉得"活着没劲"。如果这样的话,倒不如及早融入社会洪流更让你能安静下来。既能出世也能入世是最好的,如果压根做不到出世,就绝对入世一些,让自己实际"生活"起来,也就运转自如了。

  因为如果没有高情商,那么读博不但可能不会给你带来更多的快乐,而且会带来很多负累。因为你可能会跟社会上的人比物质,跟潜心做学问的人比成果,跟那些滋润的"小家庭"比幸福,等等。那就比较麻烦。因为老天爷毕竟不会把所有的好事都给一个人。你得到了一些,就要坦然面对可能因此失去的。

  2、读博需要有点毅力,沉得住气,至少一段时间内是这样。

  读博可不是说着玩的。如果只是想混混,也不是那么好混的。现在的毕业论文盲审制度,还是能卡住不少没下功夫或者功夫下得不到,或者下了功夫但没下到当处的人的。而且,平时博士还有论文发表的数量和档次的要求,动不动就是国家核心期刊几篇,理工科的可能还需要在国际刊物上发表。虽然大多数博士都完成任务,拿到了学位,但那是付出了很多焦虑、睡不着觉、掉头发等等代价的。当然,视个人天资和基础不同,可能各有差异,但不努力就可以完成任务的还真没听说过。

  平时上课、读书的要求就不要说了。为了完成博士论文,或者在那样一个氛围中,为了自己至少像个博士,理工科的人要无数次地做实验,甚至通宵达旦,而文科的学生则要天天泡在书堆里,基本上3年下来,看小说的时间是没有的。大家都比较训练有素,就是自己浪费了时间会自责。所以为了逃避自责的痛苦和外在的压力,大家一般还是要选择一点点的去面对和积累。没有点做冷板凳的功夫是不行的,至少在一段时间内是这样。

  博士同学中已经是硕导的,在博士论文的冲刺阶段,也会形容枯槁,也有老师们形容"做完博士论文就像扒层皮",毕竟抛却客观要求不说,就自身而言,大家也知道专心在学校搞点研究不容易,所以基本上博士论文就是一段时间内的最高水平。何况博士论文的最高要求,也是最基本的,就是"创新"。这两个字,可是会把人折磨坏的。结果是,做博士论文的时候,大多数同学都"自然减肥"。一个抱着侥幸心理,本来以为混混就可以过关的同学,在博士论文写完之后,说了这样一句深刻的推翻自己以前逻辑的话——"以后,谁要再说博士是混出来的,我跟他急!"  

  接下来看一下读博可能带来的一些负面影响。

  1、读博可能会改变一个人的生活节奏和轨迹。比如恋爱、婚姻和家庭。

  说这个好象是废话。但是我们可以算笔账,一个人中间一直不停地读书,等博士毕业也是接近30的人了。这时候的社会工作经验还可能是零。如果不是足够幸运地在读书期间遇到合适的另一半并且已经成家的话,那么就要在毕业之后不但面临找工作,适应新环境的问题,也要面对建立一个小家,适应婚姻和家庭的问题,还要考虑是先在工作上干出点成绩还是抓紧要孩子的问题。这时候大多数小家庭都是无产阶级,可能早先工作的同龄人已经拥有了自己的房子、车子和孩子,心理上的落差需要自己摆平。

  也有很大一部分人是有了一定的工作、家庭、孩子之后又去读博的,这种辛苦就不要说了。面对家庭和自己的前途之间的不调和,需要自己判断、选择。有时候,有所选择是痛苦的。所以有很多人说,早知如此,宁愿不要选择的机会。可是时光是无法倒流的。校园里有不少带着孩子读书的母亲,有的将孩子放在周边的幼儿园或者学校里,自己面对多重压力,孩子也跟着受苦。精力、财力和爱心、责任之间的冲突和压力,让很多母亲说:"等我拿到学位的那一天,我可能最想做的就是找个没人的地方大哭一场。"

  有的时候,鱼和熊掌的确不可得兼,这是事实。所以大多数博士在毕业后都会多多少少有些尴尬,来自情感的、家庭的、工作的,等等。

  2、读博可能会加重取舍之间的难度。

  有人认为,读完博士,工作等等可选择的余地更大了。实际上不是这样的。博士阶段着重培养的是在某一领域具有独立进行研究和解决问题的能力的人。它不是一个通才教育,只会将人的研究领域和学习领域越限越窄。所以,除非是在高校或做科研,不然,博士并不是最好的选择。

  而且,读博可能多多少少还会产生这样的效应,就是你在长期的研究和投入中已经多多少少对这些研究产生兴趣。明知继续研究可能热情和动力不足,但是放弃却着实可惜,那时该怎么办?其实周围很多同学都是这样的。最后选择的未必是最初想选择的生活道路。我学的是文科,我的很多同学,本来想进新闻媒体,或者出版单位,或者机关等等,机会并不是没有,但是真正抉择的时候,却又割舍不下对专业培养起来的感情,最终继续选了高校进行教学科研,也有的虽然进了新闻出版,却做的并不开心,有点后悔的意思。不得不搞学术的时候不觉得热爱,反倒是真正离开了觉得自己"还是有点"喜欢学术的。这是一种悖论。会带来很多困惑和尴尬,这些问题的解决需要智慧、对自己的了解,还需要决断能力。一旦上了这贼船就必须面对,由不得你了。

  说了这些,有人可能退缩了,觉得后脊梁骨嗖嗖发凉。其实,运用最简单的逻辑想想,如果真的读博就像地狱,哪还有现在的趋之若骛呢?不否认有一些头脑发昏,没想明白就混进来的人,但是一定也是有人是真正考虑好了并认可这种方式的。权且说其一二:

  1、读博是人的一种生活方式,而且很奢侈,不是每个人都可以享受的。

  相比于钱来说,读博的机会显得更是一种稀缺资源。有机会读博的人可能不觉得,但是有很多人是压根没有机会考虑和尝试的,他们的羡慕和憧憬可能不是你读博的理由,但是至少有一点是肯定的,你可以体会跟别人不一样的人生。这也是一种生活方式。而且这种生活体验是非常奢侈的,有限的,难得的,所以自然也是有价值的有意义的。如果说有人选择冒险、挑战生命极限是一种生活方式,那么读博也是。读博完成和实现的是一种精神追求。享受这种方式本身就是一种经历、一种财富、一种收获。

  有一个同事,我在博客中提到过的,海归,放弃了原来在外企的高薪工作,跑到国外读博士,最后回到高校。就是因为她觉得在外企的工作,使人变得没有时间思考,人像机器。所以为了进高校,她选择了读博并为这个目标奋斗了10几年。她说,人是要有点追求的。斯夫!当人们解决了基本的生存问题之后,对精神的渴望和寄托就会浮出水面,而读博是使人更接近思想本身的一种方式。

  2、读博可以有机会接近和聆听大师的声音,并与最聪明的人相处。想不提高都难!

  有博士点的地方,大都是师资力量相当强的地方,各种资源也很多。读博士可以使人轻而易举地就接近原来只闻其名、不见其人的一些大家,聆听他们在学术、做人等等方面的一些感受和教诲。这将是受益终生的。这种视界的打开和融合对一个人的提升来说至关重要。因为这些学者的点拨,可能会使人少走很多弯路,并在一些领域迅速接近国内或国外同行。某种意义上,这有"鲤鱼跳龙门"之效。特别是在一些综合性的名牌大学,这种优势就更加明显。用原来硕导的话说:"就像从省队进了国家队"。各种讲座、交流以及图书等等资源,使人进步飞快。  

再说周围的同学,都是来自各地的人中龙凤。依我的经验,每个读到博士的人,都是有一些品质可能是一般的老百姓没有的,比如那种悟性、那种天资、那种刻苦、那种执着、那种毅力,等等。所以,与这样的一群聪明人交往,无论别人是不是有一些很难容忍的缺点,但也总是有一些优点是值得学习的,在这样的一种碰撞和交流中,无论是对学术的,还是对生活的看法,都会让人获益匪浅。真的是想不提高都难!

3、读博可以积累一些资源。

  博士期间会遇到很多名师,也会有很多优秀的同学,这些都是一些潜在的资源。就算日后你的同学里出不了王侯将相,但至少以他们的起点,在各行各业上都会是不错的骨干。至少就我而言,很多同学遍布在很多城市的很多高校,无论对搞研究,还是作为了解风土人情的交流,都是颇有助益的。博士期间的师生关系有助于建立广泛的学术联系。

  毕业之后,如果选择一个中等城市或者发展中的学校或单位,那么可能意味着你会瞬间拥有别人奋斗10几年才能拥有的东西,比如各种津贴补助、比如房子,或者配偶的工作,都能得到解决。生活会有明显提高。从这个意义上,用原来同事的话说,读博就是赚钱。或者,读博还极有可能在毕业后进入一个博士成群的工作团体。这样,你就有可能使自己一直处于一个不断学习不断上升不断进步的状态。与智者的交流毕竟是令人愉快的。对此我受益良多。

  另外,比较重要的一点,也是一个老教授曾经说过的,做研究工作,所有的科研成果的发表,名利都是自己的,不象在机关等单位工作,在年轻的时候多是为别人做嫁衣,而到了退休,往往是人一走茶就凉。而现在看来,读博直接地与以后是否有机会搞科研密切相关。

  4、读博可以愉悦身心,是难得的生命体验。

  因为博士不象本科生那样有天天上不完的课程,有各种各样的活动的约束,有各种检查和评比,所以基本上属于比较天马行空的、自由的一群。而高校大都有优美的环境和小资的气氛,博士生里面,大家基本上要么是有收入的,要么是可以自己赚些外快的,思想相对比较成熟了,各方面处于学校和社会的结合带,并可以暂时脱离家庭的束缚,做一些自由自在的个性的事情,是难得的生命体验。对于工作多年的人来说,这是一种放松、调整和休憩,而对于从未走上工作岗位的人来说,也从别人那里直接间接得到一些启迪。

  我的博士生活就是丰富多彩的。有一帮朋友,大家经常轮流坐庄,出去吃饭、喝茶、野餐,或者就在校园里散散步、打打球,或者在学校的食堂里进行"学术午餐"和"学术晚餐",关于学术的、人生的、社会的,无所不谈。现在回想起来,我的同学和朋友都颇为留恋那段时光。我们都曾说过这样的类似"痴人说梦"之类的话,"如果条件允许的话,真想一辈子当学生,一辈子在校园里待下去。让读书变成一种生活方式。"

  ......

  我想说了这么多,不知那些想了解些什么的人是更清醒了还是更晕乎了?其实,大家都是时而清醒时而糊涂的,这都正常。关键是,主要问题想明白了就行,小事不妨糊涂一点。某一时间和地点,我们只能有一次选择人生的机会。是否读博?这本身就是一个考卷。想好了再下笔,一旦下笔就坚持下去。相信,你会看到传说中的彩虹

相关回复:

作者: zhangtikun   发布日期: 2008-11-16
说的不错
想考博的人应该仔细权衡自己到底适不适合读博,已读博的人应该再多些自律和坚持,既然选择了这种生活方式就没有必要后悔。
抵制些诱惑,抗拒些浮躁,忙碌的同时也要学会享受生活,在小天地里也要活得精彩。自勉!;)

作者: sunnybrucelee   发布日期: 2008-11-16
说的很好啊:D:D

作者: 康熙来了   发布日期: 2008-11-16
共勉!!!

作者: kumu   发布日期: 2008-11-16
非常好,借鉴中。。。

作者: 无锋   发布日期: 2008-11-16
:P学习!

作者: luzzy   发布日期: 2008-11-16
赞这种生活方式,一直想提高自己,但实在没有那样的毅力,也许过惯了这种懒散的生活了.

作者: pigeon0411   发布日期: 2008-11-16
楼主写的很好,很实际,帮助很大!谢谢

作者: 红冰2009   发布日期: 2008-11-17
很中肯也很实在的体验

作者: hanbin618   发布日期: 2008-11-17
很中肯,学习!

作者: shtjww   发布日期: 2008-11-17
是啊 每月不到2000的生活方式 哈哈

作者: tent1215   发布日期: 2008-11-17
有相同的观点

作者: jinlchen   发布日期: 2008-11-17
现在有时间体验这样的生活,单身一人不用考虑太多的问题。 就是怕坚持不下来啊

作者: xuht   发布日期: 2008-11-17
一语中的,说得太好了,就是一种生活方式

作者: pengyuan209   发布日期: 2008-11-17
很中肯也很实在的体验

作者: wuyan9537   发布日期: 2008-11-18
学习了:)

作者: hitwyb1538   发布日期: 2008-11-19
"读博是一种生活方式"!

作者: 姆粒   发布日期: 2008-11-21
嗯:)同意

作者: cnzxy   发布日期: 2008-11-21
要是三年前就看到这篇文章,我想我会义无反顾放弃读博进入社会。

作者: 临渊者慕鱼   发布日期: 2008-11-21
:o我还打算直博呢!
可以借鉴一下哎!

作者: 大雪人   发布日期: 2008-11-21
说的很好,我想读博,很有帮助,谢谢了

作者: xiebin6592   发布日期: 2008-11-22
:P:P:P

作者: zhaoyan198   发布日期: 2008-11-28
是写的很好,就是倒数第二段哪些说的可能比较不切理工学生的实际,我们可没有那个精力去喝茶聊天潇洒去,学术讨论好像也不太能形容成晚餐那么美好,也许等我毕业了会怀念,但是现在肯定还是想尽快毕业。

作者: bsklwwy   发布日期: 2008-11-28
说的不错,很实际,也不偏激。
和别的看待博士的文章不同,真正写出了读博的感受。

想要读博或正在读博的人可以看看,有很大的启迪!

作者: xjlk0000   发布日期: 2008-11-28
我也赞一下,一直在为自己的考博寻找合适的理由,心中总有那么一些文中谈到的体会,却不能明确的表达出来,谢谢楼主

作者: tsingo   发布日期: 2008-11-28
hao学习学习

作者: qingqingcao288   发布日期: 2008-11-30
很好,值得借鉴。

posted @ 2009-02-27 02:26 小强摩羯座 阅读(222) | 评论 (1)编辑 收藏

dy @ 2005-12-06 20:02
我们不逾法,但我们张扬,每一种性感都尖叫嘶喊.
我们不色情,但我们激荡,每一种宣泄都淋漓酣畅.
我们不畸形,但我们包容,每一种存在都值得尊重.
我们原创  我们独特  我们精髓  我们唯美               ------------rock



dy @ 2007-10-14 18:07

一輩子有多少的來不及
發現已經失去最重要的東西
恍然大悟早已遠去
為何總是在犯錯之後
才肯相信錯的是自己.
他們說這就是人生.試著體會
試著忍住眼淚還是躲不開應該有的情緒

我不會奢求世界停止轉動
我知道逃避一點都沒有用
只是這段時間裡尤其在夜裡
還是會想起難忘的事情
我想我的思念是一種病
久久不能痊癒

當你在穿山越領的另一邊
我在孤獨的路上沒有盡頭
時常感覺你在耳後的呼吸
卻未曾感覺你在心口鼻息

汲汲營營忘記身邊的人需要愛的關心
藉口總是拉遠了距離不知不覺地無聲無息
我們總是在抱怨事與願違卻願意回頭看看自己
想想自己到底做了甚麼蠢事情.

也許是上帝給我一個試煉
只是這傷口需要花點時間
只是會想念過去的一切
那些人事物會離我遠去
而我們終究也會遠離變成回憶

當你在穿山越領的另一邊
我在孤獨的路上沒有盡頭
時常感覺你在耳後的呼吸
卻未曾感覺你在心口的鼻息
oh思念是一種病

多久沒有說愛你
多久沒有擁抱你所愛的人
當這個世界不再那麼美好
只有愛可以讓他更好
我相信一切都來得及
別管那些紛紛擾擾
別讓不開心的事停下了腳步
就怕你不說.就怕你不做
別讓遺憾繼續一切都來得及

posted @ 2009-02-27 01:43 小强摩羯座 阅读(134) | 评论 (0)编辑 收藏

dy @ 2008-12-01 22:50

能做多少就做多少
不卑 不抗 不扯淡
多和别人交流
多倾听别人的想法

坚持下去一定会有收获

看情况形势  看场合说话
不清楚的不能瞎说
清楚的点到为止
讨论的时候  拿小本记好

坚持下去一定会有收获

时常提醒自己  你还刚刚上路
别刚会走 就要轮滑
尽量减少抱怨 避免攀比
不在别人耳后说三道四

坚持下去一定会有收获

一天一个苹果
睡前读一个小时书
早上醒后 想想一天的计划
努力 让自个保持清醒

坚持下去一定会有收获

posted @ 2009-01-04 00:29 小强摩羯座 阅读(159) | 评论 (1)编辑 收藏

泛型编程:源起、实现与意义
 By 刘未鹏 C++的罗浮宫(http://blog.csdn.net/pongba)(去年12月《程序员》的约稿)
(以前也写过一篇相关的文章:Generic Programming - What Are You, anyway? )
 为什么泛型
泛型编程(Generic Programming)最初提出时的动机很简单直接:发明一种语言机制,能够帮助实现一个通用的标准容器库。所谓通用的标准容器库,就是要能够做到,比如用一个List类存放所有可能类型的对象,这样的事情;熟悉一些其它面向对象的语言的人应该知道,如Java里面这是通过在List里面存放Object引用来实现的。Java的单根继承在这里起到了关键的作用。然而单根继承对C++这样的处在语言链底层的语言却是不能承受之重。此外使用单根继承来实现通用容器也会带来效率和类型安全方面的问题,两者都与C++的理念不相吻合。
 
于是C++另谋他法——除了单根继承之外,另一个实现通用容器的方案就是使用“参数化类型”。一个容器需要能够存放任何类型的对象,那干脆就把这个对象的类型“抽”出来,参数化它[1]
 
template<class T> class vector {
 T* v;
 int sz;
public:
 vector(int);
 T& operator[](int);
 T& elem(int i) { return v[i]; }
 // ...
};
 
一般来说看到这个定义的时候,每个人都会想到C的宏。的确,模板和宏在精神上的确有相仿之处。而且的确,也有人使用C的宏来实现通用容器。模板是将一个定义里面的类型参数化出来,而宏也可以做到参数化类型。甚至某种意义上可以说宏是模板的超集——因为宏不仅可以参数化类型,宏实质上可以参数化一切文本,因为它本来就是一个文本替换工具。然而,跟模板相比,宏的最大的缺点就是它并不工作在C++的语法解析层面,宏是由预处理器来处理的,而在预处理器的眼里没有C++,只有一堆文本,因此C++的类型检查根本不起作用。比如上面的定义如果用宏来实现,那么就算你传进去的T不是一个类型,预处理器也不会报错;只有等到文本替换完了,到C++编译器工作的时候才会发现一堆莫名其妙的类型错误,但那个时候错误就已经到处都是了。往往最后会抛出一堆吓人的编译错误。更何况宏基本无法调试。
 
1
实际上,还有一种实现通用容器的办法。只不过它更糟糕:它要求任何能存放在容器内的类型都继承自一个NodeBase,NodeBase里面有pre和next指针,通过这种方式,就可以将任意类型链入一个链表内了。但这种方式的致命缺点是(1)它是侵入性的,每个能够放在该容器内的类型都必须继承自NodeBase基类。(2)它不支持基本内建类型(int、double等),因为内建类型并不,也不能继承自NodeBase。这还姑且不说它是类型不安全的,以及效率问题。
 
我们再来看一看通用算法,这是泛型的另一个动机。比如我们熟悉的Cqsort
 
void qsort(void *base, size_t nmemb, size_t size,
int (*compar)(const void *, const void *));
 
这个算法有如下几个问题:
 
1.      类型安全性:使用者必须自行保证base指向的数组的元素类型和compar的两个参数的类型是一致的;使用者必须自行保证size必须是数组元素类型的大小。
2.      通用性:qsort对参数数组的二进制接口有严格要求——它必须是一个内存连续的数组。如果你实现了一个巧妙的、分段连续的自定义数组,就没法使用qsort了。
3.      接口直观性:如果你有一个数组char* arr = new arr[10];那么该数组的元素类型其实就已经“透露”了它自己的大小。然而qsort把数组的元素类型给“void”掉了(void *base),于是丢失掉了这一信息,而只能让调用方手动提供一个size。为什么要把数组类型声明为void*?因为除此之外别无它法,声明为任意一个类型的指针都不妥(compar的参数类型也是如此)。qsort为了通用性,把类型信息丢掉了,进而导致了必须用额外的参数来提供类型大小信息。在这个特定的算法里问题还不明显,毕竟只多一个size参数而已,但一旦涉及的类型信息多了起来,其接口的可伸缩性(scalability)问题和直观性问题就会逐渐显现。
4.      效率:compar是通过函数指针调用的,这带来了一定的开销。但跟上面的其它问题比起来这个问题还不是最严重的。
 
泛型编程
泛型编程最初诞生于C++中,由Alexander Stepanov[2]David Musser[3]创立。目的是为了实现C++STL(标准模板库)。其语言支持机制就是模板(Templates)。模板的精神其实很简单:参数化类型。换句话说,把一个原本特定于某个类型的算法或类当中的类型信息抽掉,抽出来做成模板参数T。比如qsort泛化之后就变成了:
 
template<class RandomAccessIterator, class Compare>
void sort(RandomAccessIterator first, RandomAccessIterator last,
        Compare comp);
其中firstlast这一对迭代器代表一个前闭后开区间,迭代器和前开后闭区间都是STL的核心概念。迭代器建模的是内建指针的接口(解引用、递增、递减等)、前开后闭区间是一个简单的数学概念,表示从first(含first)到last(不含last)的区间内的所有元素。此外,comp是一个仿函数(functor)。仿函数也是STL的核心概念,仿函数是建模的内建函数的接口,一个仿函数可以是一个内建的函数,也可以是一个重载了operator()的类对象,只要是支持函数调用的语法形式就可成为一个仿函数。
 
通过操作符重载,C++允许了自定义类型具有跟内建类型同样的使用接口;又通过模板这样的参数化类型机制,C++允许了一个算法或类定义,能够利用这样的接口一致性来对自身进行泛化。例如,一个原本操作内建指针的算法,被泛化为操纵一切迭代器的算法。一个原本使用内建函数指针的算法,被泛化为能够接受一切重载了函数调用操作符(operator())的类对象的算法。
 
让我们来看一看模板是如何解决上面所说的qsort的各个问题的:
 
1.      类型安全性:如果你调用std::sort(arr, arr + n, comp);那么comp的类型就必须要和arr的数组元素类型一致,否则编译器就会帮你检查出来。而且comp的参数类型再也不用const void*这种不直观的表示了,而是可以直接声明为对应的数组元素的类型。
2.      通用性:这个刚才已经说过了。泛型的核心目的之一就是通用性。std::sort可以用于一切迭代器,其compare函数可以是一切支持函数调用语法的对象。如果你想要将std::sort用在你自己的容器上的话,你只要定义一个自己的迭代器类(严格来说是一个随机访问迭代器,STL对迭代器的访问能力有一些分类,随机访问迭代器具有建模的内建指针的访问能力),如果需要的话,再定义一个自己的仿函数类即可。
3.      接口直观性:跟qsort相比,std::sort的使用接口上没有多余的东西,也没有不直观的size参数。一个有待排序的区间,一个代表比较标准的仿函数,仅此而已[4]
4.      效率:如果你传给std::sortcompare函数是一个自定义了operator()的仿函数。那么编译器就能够利用类型信息,将对该仿函数的operatpr()调用直接内联。消除函数调用开销。
 
动态多态与静态多态
泛型编程的核心活动是抽象:将一个特定于某些类型的算法中那些类型无关的共性抽象出来,比如,在STL的概念体系里面,管你是一个数组还是一个链表,反正都是一个区间,这就是一层抽象。管你是一个内建函数还是一个自定义类,反正都是一个Callable(可调用)的对象(在C++里面通过仿函数来表示),这就是一层抽象。泛型编程的过程就是一个不断将这些抽象提升(lift)出来的过程,最终的目的是形成一个最大程度上通用的算法或类。
 
有人肯定会问,既然同是抽象,那为什么不用基于多态的面向对象抽象呢?比如STLstd::for_each,用Java的多态机制也可以解决:
 
interface IUnaryFun
{
void invoke(Object o);
}
 
interface IInputIter
{
IInputIter preIncrement();
boolean equals(IInputIter otherIter);
… // other methods
}
 
IUnaryFun for_each(IInputIter first, IInputIter last, IUnaryFun func)
{
 for(;!first.equals(last); first.preIncrement())
func.invoke(*first);
 return func;
}
 
其实,这里最主要的原因很简单,效率。面向对象的多态引入了间接调用。当然,并不是说间接调用不好,有些时候,比如确实需要运行期多态的时候,只能诉诸继承这样的手段。但当能够利用编译期类型信息的时候,为什么要付出不必要的间接调用开销呢?比如这里的for_each,利用接口来实现其通用性,就付出了所谓的“抽象惩罚”(abstraction penalty)。而C++的模板,就是为了消除这样的抽象惩罚。利用模板编写的std::for_each,对于每一个特定的参数类型组合都有一个独立的,最高效的实例化版本,就跟你手写一个特定于这些类型的专门的for_each算法一样[5]。于是抽象惩罚消失了,而这也正是C++模板库能够真正被工业界广泛用在C++最擅长的领域(重视效率的领域)的重要原因之一。
 
另一方面,对于每一组参数类型组合实例化一个版本出来的做法增加了代码空间,这是一个典型的以空间换时间的例子,不过对于一门静态并追求效率的语言来说,这个代码空间的开销反正也是必不可少的,因为即便你手动为各种不同的参数类型组合编写特定的算法版本的话,也是付出一样的代码空间开销,而且还顺便违反了DRY原则[6]。此外,由于在抽象的时候不用总想着要建立的接口,所以泛型算法编写起来也更为直观。
 
C++泛型的另一个好处就是,跟面向对象编程的基于继承和虚函数的运行时多态机制不同,C++模板是非侵入性的。你不需要让你的类继承自某个特定的接口才能用于某个算法,只要支持特定的语法接口就行了(比如只要支持begin()调用)。这也被称为结构一致性(Structural Conformance),意即只要语法结构一致即可。而另一方面,基于接口继承的面向对象多态则必须要显式地声明继承自一个接口,这就是所谓的名字一致性(Named Conformance)。
 
当然,泛型支持的静态多态和虚函数支持的动态多态并没有任何程度上绝对的优劣之分。它们适用于不同的场合。当类型信息可得的时候,利用编译期多态能够获得最大的效率和灵活性。当具体的类型信息不可得,就必须诉诸运行期多态了。Bjarne Stroustrup曾经用了一个典型的例子来澄清这个区别:
 
std::vector<Shape*> v;
… // fill v
std::for_each(v.begin(), v.end(), std::mem_fun(&Shape::draw));
 
这里,v里面到底将来会存放什么类型的Shape,编译期无法知道,因而必须求助于动态多态。另一方面,编译器倒的确知道它们都继承自Shape,利用这仅有的静态类型信息,我们使用了泛型算法std::for_each和泛型容器std::vector。这里尤其值得注意的是for_each的静态多态行为:for_each只有一份模板实现,然而根据传给它的第三个参数(本例中是std::mem_fun(&Shape::draw))的不同,for_each的行为也不同(这里最终被for_each调用的是Shape::draw,但实际上你可以包装任何函数,只要这个函数接受一个Shape*型的参数),for_each这种“行为不同”是发生在编译期的,所以是静态多态。
 
前面说过,模板与接口继承比较,模板是非侵入的。这是C++泛型与面向对象的多态机制的本质区别之一。但实际上,面向对象未必就意味着一定要用接口来实现动态的多态。一些动态类型的脚本语言,如Ruby,它的多态机制就既是运行期(动态)的,又是非倾入性的(不用通过继承自某个特定的接口来达到复用)。人们把这个叫做Duck Typing[7]。如果不是因为效率问题,其实这样的多态机制才是最直观的,从使用方面来说,它既有非侵入性,又没有只能工作在编译期的限制。但效率至少在可见的将来、在某些领域仍是一个顾虑。因此像C++这种区分编译期和运行期多态的语言,仍有其独特的优势。
 
此外,泛型编程的类型安全优势也让它从C++走入了其它主流的静态类型语言当中,尤其是C家族的JavaC#,在前几年相继接纳泛型。
 
特化,图灵完备性,元编程
C++的模板是支持特化的,这就给了它实现编译期控制结构的可能性,进而带来了一个图灵完备的子语言。模板特化的引入原本只是为了效率目的——针对不同的类型提供不同的实现。但后来却被发现能够实现编译期的if/else和递归等控制结构。
 
模板元编程最初由Erwin Unruh1994年的一次会议上提出;当时他写了一个程序,在编译错误里面打印出一个素数序列。这个事件在C++历史上的地位就仿佛哥伦布发现新大陆。用Bjarne Stroustrup的话来说就是当时他当时和其他几个人觉得太神奇了。实际上,这个事情正标志了C++模板系统的图灵完备性被发现;后来Todd Veldhuizen写了一篇paper,用C++模板构建了一个元图灵机,从而第一次系统证明了C++模板的图灵完备性。接下来的事情就顺理成章了——一些ad hoc的模板元编程技巧不断被发掘出来,用于建造高效、高适应性的通用组件。最终,David Abrahams编写了boost库中的基本组件之一:Boost.MPL库。Boost.MPL以类型和编译期常量为数据,以模板特化为手段,实现了一个编译期的STL。你可以看到常见的vector,你可以看到transform算法,只不过算法操纵的对象和容器存放的对象不再是运行期的变量或对象,而是编译期的类型和常量。想知道模板元编程是如何用在库构建中的,可以打开一个Boost的子库(比如Boost.TupleBoost.Variant)看一看。
 
然而,毕竟C++的模板元编程是一门被发现而不是被发明的子语言。一方面,它在构建泛型库的时候极其有用。然而另一方面,由于它并非语言设计初始时考虑在内的东西,所以不仅在语法上面显得不那么first-class(比较笨拙);更重要的是,由于本不是一个first-class的语言特性,所以C++编译器并不知道C++元编程的存在。这就意味着,比如对下面这样一个编译期的if/else设施[8]
 
template<bool b, class X, class Y>
struct if_ {
 typedef X type; // use X if b is true
};
template<class X, class Y>
struct if_<false,X,Y> {
typedef Y type; // use Y if b is false
};
 
typedef if_<(sizeof(Foobar)<40), Foo, Bar>::type type;
 
编译器并没有真的去进行if/else的分支选择,而是按部就班毫不知情地进行着模板的特化匹配。如果遇到Boost.MPL这样的模板元编程非常重的库,就会严重拖累编译速度,编译器进行了一重一重的特化匹配,实例化了一个又一个的模板实例,就是为了去获取里面定义的一个typedef,完了之后中间所有实例化出来的模板实例类型全都被丢弃[9]
 
模板元编程最全面深入的介绍是Boost.MPL库的作者David Abrahams的《C++ Template Metaprogramming》,其中的样章(第三章)[10]对模板元编程作了一个非常高屋建瓴的概览[11]
 
关于模板元编程,需要提醒的是,它并不属于“大众技术”。日常编程中极少需要用到元编程技巧。另一方面,C++模板里面有大量ad hoc的技巧,如果一头扎进去的话,很容易只见树木不见森林,所以需要提醒初学者的是,即便要学习,也要时刻保持“高度”,始终记得元编程的意义和目的,这样才不至于迷失在无穷无尽的语言细节中。
 
C++09——进化
泛型编程在C++中取得了工业程度上的成功,得益于其高效性和通用性。但同时,在经过了近十年的使用之后,C++模板,这个作为C++实现泛型的机制的缺点也逐渐暴露出来。比如其中对于初学者最严重的一个问题就是在使用一个模板库时常常遇到无法理解的编译错误,动辄长达上K字节。这些编译错误很容易把一个初学者吓走。究其本质原因,为什么编译器会报出令人难以卒读的编译错误,是因为在编译器的眼里,只有类型,而没有“类型的类型”。比如说,迭代器就是一个类型的类型,C++里面也把它称为“概念”(Concept)。例如,std::sort要求参数必须是随机访问迭代器,如果你一不小心给了它一个非随机访问的迭代器,那么编译器不是抱怨“嘿!你给我的不是随机访问迭代器”,而是抱怨找不到某某重载操作符(典型的比如operator+(int))。因为在编译器眼里,没有“迭代器”这么个概念,这个概念只存在于程序员脑子里和STL的文档里。为了帮助编译器产出更友好的信息(当然,还有许多非常重要的其它原因[12]),C++09将对“类型的类型”加入first-class的支持,其带来的众多好处会将C++中的泛型编程带向一个新的高度:更友好、更实用、更直观。
 
此外,C++的模板元编程尚没有first-class的语言支持,一方面是因为其运用不像一般的模板编程这么广泛,因而没有这么紧急。另一方面,C++09的时间表也等不及一个成熟的提案。如果以后模板元编程被运用得越来越广泛的话,那first-class的语言支持是难免的。
 
总结
本文对C++模板,以及C++模板所支持的泛型编程作了一个概览。着重介绍了泛型编程诞生的原因,泛型编程的过程和意义,与其它抽象手段的比较。并对C++中的模板元编程做了一些介绍。最后介绍了C++模板在C++09中的增强。
 


[1] B. Stroustrup: A History of C++: 1979-1991. Proc ACM History of Programming Languages conference (HOPL-2). March 1993.
[2] http://en.wikipedia.org/wiki/Alexander_Stepanov
[3] http://www.cs.rpi.edu/~musser
[4] 实际上,STL的区间概念被证明是一个不完美的抽象。你有没有发现,要传递一个区间给一个函数,如std::sort,你需要传递两个参数,一个是区间的开头,一个是区间的末尾。这种分离的参数传递方式被证明是不明智的,在一些场合会带来使用上不必要的麻烦。比如你想迭代一组文件,代表这组文件的区间由一个readdir_sequence函数返回,由于要分离表达一个区间,你就必须写:
readdir_sequence entries(".", readdir_sequence::files);
std::for_each(entries.begin(), entries.end(), ::remove);
如果你只想遍历这个区间一次的话,你也许不想声明entries这个变量,毕竟多一个名字就多一个累赘,你也许只想:
std::for_each(readdir_sequence(".", readdir_sequence::files), ::remove);
下一代C++标准(C++09)会解决这个问题(将区间这个抽象定义为一个整体)。
[5] 当然,语言并没有规定模板实例化的底层实现一定要是对每组参数类型组合实例化一个版本出来。但目前的实现,这种方案是最高效的。完全消除了抽象惩罚。另一方面,One size fit all的方案也不是不可行,但总会有间接调用。这也正说明了静态类型系统的一个典型优点:帮助编译器生成更高效的代码。
[6] http://en.wikipedia.org/wiki/Don't_repeat_yourself
[7] http://en.wikipedia.org/wiki/Duck_typing
[8] 摘自Bjarne Stroustrup的paper:Evolving a language in and for the real world: C++ 1991-2006
[9] 也正因此,D语言加入了语言直接对模板元编程的支持,比如真正工作在编译期的static if-else语句。
[10] http://www.boost-consulting.com/mplbook/
[11] 第三章的翻译见我的blog:《深度探索元函数》http://blog.csdn.net/pongba/archive/2004/09/01/90642.aspx
[12] 由于篇幅原因,这里无法展开详述Concept对C++泛型编程的其它重要意义,有兴趣的可以参见我的一篇blog:《C++0x漫谈系列之:Concept! Concept!》。http://blog.csdn.net/pongba/archive/2007/08/04/1726031.aspx

posted @ 2009-01-04 00:28 小强摩羯座 阅读(186) | 评论 (0)编辑 收藏


常去看的几个博客
1、刘未鹏|C++的罗浮宫
http://blog.csdn.net/pongba
假如你对人工智能、机器学习、知识发现和认知科学有兴趣,这个博客绝对是一个宝藏!

 

2、笑对人生,傲立寰宇 的博客:
http://dahua.spaces.live.com/blog/
一个非常好的技术博客,博主林大华是中科大本科、香港中文大学硕士,目前在MIT读博。博客里有很多深入浅出、诙谐有趣的技术文章和读书心得,常让我拍案叫绝,那些干涩无味的数学原理也可以聊侃得如此趣味横生,呵呵。数学和matlab方面的朋友不妨多上去看看 O(∩_∩)O

 

3、不交作业 的博客:
http://blog.163.com/oneoctopus@126/blog/
一位在华南理工大学读研的学生,和他不相识,但每周都会去他的博客看看。也许是因为同龄,看他的博客、他的经历,让自己有很大的共鸣。研二一年,就集中精力写一篇文章,近期才完稿,投到著名的ACC(American Control Conference)会议去,有点‘十年磨一剑’的感觉;目前研三在申请美国的名校,祝他马到成功啦。

同龄人前进的脚步,最能鞭策自己不懈前行……

 

4、晃晃悠悠 的博客:
http://dy1981.ycool.com/
以前学习小波的时候接触到的一个博客,挺不错的,而且还有对音乐和现实生活的一些体会和感受,每个人的人生故事都有特别的精彩之处。

 

5、萝卜驿站 的博客:
http://luobo.ycool.com/
matlab强人,博客里有N多小波、混沌等方面的matlab代码共享,还有很多matlab编程技巧方面的解答。

 

6、天上有宫阙 的博客:
http://www.myclub2.com/blog/realghost/category/1396.html
里面有不少matlab和数学的学习资料,特别是代码共享,呵呵。


posted @ 2009-01-04 00:27 小强摩羯座 阅读(230) | 评论 (0)编辑 收藏

让人心疼的12句话...哪句说到你...-

1有些事,我们明知道是错的,也要去坚持,因为不甘心;有些人,我们明知道是爱的,也要去放弃,因为没结局;有时候,我们明知道没路了,却还在前行,因为习惯了 

2以为蒙上了眼睛,就可以看不见这个世界;以为捂住了耳朵,就可以听不到所有的烦恼;以为脚步停了下来,心就可以不再远行;以为我需要的爱情,只是一个拥抱 
 

3那些已经犯过的错误,有一些是因为来不及,有一些是因为刻意躲避,更多的时候是茫然地站到了一边我们就这样错了一次又一次,却从不晓得从中汲取教训,做一些反省 

4你不知道我在想你,是因为你不爱我,我明明知道你不想我,却还爱你,是因为我太傻也许有时候,逃避不是因为害怕去面对什么,而是在等待什么 

5天空没有翅膀的痕迹,但鸟儿已经飞过;心里没有被刀子割过,但疼痛却那么清晰这些胸口里最柔软的地方,被爱人伤害过的伤口,远比那些肢体所受的伤害来得犀利,而且只有时间,才能够治愈 

6很多人,因为寂寞而错爱了一人,但更多的人,因为错爱一人,而寂寞一生我们可以彼此相爱,却注定了无法相守不是我不够爱你,只是我不敢肯定,这爱,是不是最正确的 

7如果背叛是一种勇气,那么接受背叛则需要一种更大的勇气前者只需要有足够的勇敢就可以,又或许只是一时冲动,而后者考验的却是宽容的程度,绝非冲动那么简单,需要的唯有时间 

8生命无法用来证明爱情,就像我们无法证明自己可以不再相信爱情在这个城市里,诚如劳力士是物质的奢侈品,爱情则是精神上的奢侈品可是生命脆弱无比,根本没办法承受那么多的奢侈 

9人最大的困难是认识自己,最容易的也是认识自己很多时候,我们认不清自己,只因为我们把自己放在了一个错误的位置,给了自己一个错觉所以,不怕前路坎坷,只怕从一开始就走错了方向 

10生活在一个城市里,或者爱一个人,又或者做某件事,时间久了,就会觉得厌倦,就会有一种想要逃离的冲动也许不是厌倦了这个城市爱的人坚持的事,只是给不了自己坚持下去的勇气 

11多少次又多少次,回忆把生活划成一个圈,而我们在原地转了无数次,无法解脱总是希望回到最初相识的地点,如果能够再一次选择的话,以为可以爱得更单纯
 
12如果你明明知道这个故事的结局,你或者选择说出来,或者装作不知道,万不要欲言又止有时候留给别人的伤害,选择沉默比选择坦白要痛多了 

posted @ 2008-12-24 16:05 小强摩羯座 阅读(162) | 评论 (0)编辑 收藏

仅列出标题
共20页: First 上一页 5 6 7 8 9 10 11 12 13 下一页 Last