大多数程序员在进入一个规模稍大点的公司时,都会花一定时间熟悉公司的框架(也不是所有的公司都能开发出自己的框架,比如我们公司,就是把大多数主流的框做了一下优化和整合而已),当然我也不例外。公司是做移动支付开发的,使用的是自己的一套(整合)框架:struts+spring+abitis,log4j做调试,Ant做编译,CVS做版本控制。使用的IDE是 eclipse 3.2+MyEcilpse 5.1。整个框架struts做cs(表现层),使用spring的IOC功能和声明式事务,abitis做持久化。摒弃hibernate而选择 abatis的原因,我认为是看中了abatis的简洁、灵活且功能强大的原因吧。

 花了两天的时间把公司的框架研究了一下,想自己也做个简单的demo,于是说做就做,昨天花了一下午的时间,终于把它搞定。
struts做表现层,这和以前没什么区别,不一样的是,struts的 action没有使用spring管理起来,想想看,这样也对,action主要是根据struts的配置文件做一些派谴的工作,一般不会再给 action写实现,所以不管理也罢。action没有向以往一样继承Action,而是继承了自定义的BaseAction,而BaseAction又继承DispatchAction(分发action)。写一个BaseAction的目的是让所有的Action都共享BaseAction的代码(BaseAction里有两个属性spring的ApplicationContext和log4j的Logger,还有一个静态方法 getContext用来返回ApplicationContext)。

public class BaseAction extends DispatchAction {

/** Log4j 日志 */
protected static Logger logger = Logger.getLogger(BaseAction.class);

     /** Spring的上下文对象 */
     ApplicationContext context;

     protected synchronized ApplicationContext getContext(){
  
      return SpringUtils.getContext();
    }
}

这样一来,我们所有的action都要继承BaseAction:

public class LoginAction extends BaseAction {

@Override
public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {

     UserForm userForm = (UserForm) form;
     UserVo userVo = new UserVo();
     userVo.setUserName(userForm.getUserName());
     userVo.setUserPass(userForm.getUserPass());
  
     UserService userService =    (UserService) getContext().getBean ("userService");
    logger.debug(">> UserService " + userService);
    ...

不使用Action而改用 DispatchAction,这里谈一下我自己的看法。Action与DispatchAction的不同就是DispatchAction可以被请求多次(前提不继承execute方法)。以前继承Action时,对于每一个小功能,都得写一个action,项目一大,action多的满地爬。举个例子,对于一张用户表:tbl_user的增、删、改、查,以前我做项目的时候,就会做四个action:AddUserAction, DeleteUserAction,UpdateUserAction,QueryUserAction,相应的请求也是:addUser.do, deleteUser.do... ...

    而使用了DispatchAction后,可以在一个action中声明四个方法,分别为:add,delete,update,query,当然参数都得和execute方法的参数一致,且去掉execute方法,这样我只需要写一个action:UserAction,在配置action的时候,多加一个parameter="action",这样相应的请求变成了:user.do?action=add,user.do?action= delete,...

    有人说使用DispatchAction增加了程序的耦合性,而我认为这样的耦合性无所谓,因为它不是层与层之间的耦合,且action的扩展性也不大。

Spring 做的是IOC和声明式事务,spring管理了dataSource,管理了DataSourceTransactionManager,当然还要告诉 spring如果取得abatis的配置文件,所以要管理SqlMapClientFactoryBean类,通过其configLocation属性注入abatis配置文件的路径,通过dataSource属性注入dataSource。要使用spring的事务,还得管理 TransactionProxyFactoryBean,当然,DAO和Service都得管理。为了使用接口编程,我们每一个DAO和Service 都写一个接口和至少一个实现类,如:对于用户表tbl_user,接口为:UserDao,实现为了UserDaoAbatisImpl。在程序中声明的时候,使用接口声明,而在spring中管理的时候,管理实现类。我们再改动业务逻辑实现时,只需再写一个类实现接口,然后在配置文件中改动一小下就 OK。这样就符合了程序设计中的“开-闭”原则。

  有一点不同的是,之前我使用spring的声明式事务时,每一个Service都管理两个,举个我以前写的例子,还是对于用户表:

<bean id="UserServiceTarget" class="com.jak.hospital.service.UserServiceImpl">
    <property name="userDAO" ref="UserDAOLogProxy"/>
</bean>

然后再使用事务:

<bean id="UserService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
    <property name="target">
       <ref bean="UserServiceTarget"/>
    </property>
    <property name="transactionManager">
       <ref bean="TransactionManager"/>
    </property>
<property name="proxyInterfaces">
       <list>
         <value>com.jak.hospital.service.interfaces.UserService</value>
       </list>
    </property>
<property name="transactionAttributes">
       <props>
          <prop key="*">PROPAGATION_REQUIRED</prop>
       </props>
    </property>
</bean>

  
这样来说显然有点麻烦,在这个例子中,我们用到了spring中的一个类似继承的新属性,即先写一个管理事务的模板出来,显然这个模板只需要写一次

<bean id="txProxyTemplate"
    class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
    abstract="true">
    <property name="transactionManager" ref="transactionManager" />
    <property name="transactionAttributes">
     <props>
      <prop key="*">PROPAGATION_REQUIRED,-Exception</prop>
     </props>
    </property>
</bean>

还是对于用户表来说,我们可以这样管理Service:

<bean id="userService" parent="txProxyTemplate">
     <property name="target">
      <bean class="com.jak.service.UserServiceImpl">
       <property name="userDao" ref="userDao" />
      </bean>
     </property>
</bean>

   
parent属性了我们要继承哪个模板,然后在属性target下再管理我们自己的Service,这样一来显然减少了代码量。

 前面我们已经做了大量的工作,剩下的abatis就简单了,我们自己的DAO类只需继承SqlMapClientDaoSupport,即可使用abatis提供的强大功能。

 public class UserDaoImpl extends SqlMapClientDaoSupport implements UserDao {

public UserVo validate(UserVo userVo) throws DaoException {
  
   List list = null;
  
      Map map = new HashMap();
      map.put("userName", userVo.getUserName());
      map.put("userPass", userVo.getUserPass());
  
      try {

      list = this.getSqlMapClientTemplate().queryForList ("User.findUsers", map);
      logger.debug(">> size of return =    " + list.size());

   ...

       上面的代码非常简洁,queryForList方法要求传入至少一个参数,即sql-mql的映射字符串,如果说Hibernate是PO对象与数据库表的映射的话,那么aBatis就是PO对象与SQL语句的映射,每一个sql-mql文件中都是SQL语句的映射。来看一个最简单的例子:

 <select id="findScores" resultMap="result">
    select * from tbl_user
</select>

sql-mql配置文件中就是一堆这样的标签:select,delete,update,insert ...

id就是相当于一个key,我们就在程序中,通过这个key来调用不同的SQL语句的映射,resultMap当然就是PO对象是属性与数据库表字段的映射,当然如果它们的命名相同的话,它可以省略。记住,如果PO对象与对应的数据表的字段命名不同的话,aBatis不会报错,而是得到null对象。

那为什么我们还要传入一个Map呢,这也是aBatis的一个特性,叫做“动态SQL”,即提供一个标签,它会检查如果你传入对象是否为空来动态拼写SQL语句,如果没有传入的话,就按声明的去执行。

  写到这儿,我突然想到当时在做his项目时,寇勇问过我这样一个问题:如果我对一张表进行多条件查询时有没有什么简单的办法,就是既按姓名,又按年龄查。当然我一时没回答上来,如果这个问题让到此例中的话,显然是那么的容易,我们只需使用dynamic 标签,在程序中,想按什么查询,都把它们封装到Map中,abatis在后台会把它们一一解开动态拼写SQL语句。

  <select id="findUsers" parameterClass="java.util.Map" resultMap="result">
       select * from tbl_user where 1=1
       <dynamic>
         <isNotNull prepend="and" property="userId">
           user_id=#userId:VARCHAR#
         </isNotNull>
      </dynamic>
      <dynamic>
        <isNotNull prepend="and" property="userName">
          user_name=#userName:VARCHAR#
        </isNotNull>
      </dynamic>
      <dynamic>
         <isNotNull prepend="and" property="userPass">
           user_pass=#userPass:VARCHAR#
         </isNotNull>
      </dynamic>
      <dynamic>
          <isNotNull prepend="and" property="userAge">
             user_age=#userAge:NUMERIC#
          </isNotNull>
      </dynamic>      
</select>

   最后,log4j.properties,spring.xml, SqlMapConfig.xml都要放在src目录下,这样eclipse发布的时候才会把它们发布到类路径($WebRoot/WEB-INF/ classes)下。当然,公司的框架远比这复杂得多,这只不过是一个精简得不能再精简的例子

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐