应用分层的迷惑
WebService及DAO三层划分就像西方国家的立法行政司法三权分立一样被奉为金科玉律甚至有开发人员认为如果要使用Spring事务管理就一定先要进行三层的划分这个看似荒唐的论调在开发人员中颇有市场更有甚者认为每层必须先定义一个接口然后再定义一个实现类其结果是一个很简单的功能也至少需要个接口个类再加上视图层的JSP和JS等打牌都可以转上两桌了这种误解贻害不浅
对将面向接口编程奉为圭臬认为放之四海而皆准的论调笔者深不以为然是的面向接口编程是MartinFowlerRodJohnson这些大师提倡的行事原则如果拿这条原则去开发架构开发产品怎么强调都不为过但是对于我们一般的开发人员来说做的最多的是普通工程项目往往最多的只是一些对数据库增删查改的功能此时面向接口编程除了带来更多的类文件外看不到更多其它的好处
Spring框架提供的所有附加的好处(AOP注解增强注解MVC等)唯一的前提就是让POJO的类变成一个受Spring容器管理的Bean除此以外没有其它任何的要求下面的实例用一个POJO完成所有的功能既是Controller又是Service还是DAO
清单MixLayerUserServicejava
packageusermixlayer;
importorgspringframeworkbeansfactoryannotationAutowired;
importorgsprireJdbcTemplate;
importorgspringframeworkstereotypeController;
importorgspringframeworkwebbindannotationRequestMapping;
//①将POJO类通过注解变成SpringMVC的Controller
@Controller
publicclassMixLayerUserService{
//②自动注入JdbcTemplate
@Autowired
privateJdbcTemplatejdbcTemplate;
//③通过SpringMVC注解映URL请求
@RequestMapping(/logondo)
publicStringlogon(StringuserNameStringpassword){
if(isRightUser(userNamepassword)){
Stringsql=UPDATEt_useruSETuscore=uscore+?WHEREuser_name=?;
jdbcTemplateupdate(sqluserName);
returnsuccess;
}else{
returnfail;
}
}
privatebooleanisRightUser(StringuserNameStringpassword){
//dosth
returntrue;
}
}
通过@Controller注解将MixLayerUserService变成Web层的Controller同时也是Service层的服务类此外由于直接使用JdbcTemplate访问数据所以MixLayerUserService还是一个DAO来看一下对应的Spring配置文件
清单applicationContextxml
<?xmlversionxmlversion=encoding=UTF?>
<beansxmlnsbeansxmlns=
xmlns:xsi=instance
xmlns:context=
xmlns:p=
xmlns:aop=
xmlns:tx=
xsi:schemaLocation=
beansxsd
contextxsd
aopxsd
txxsd>
<context:componentscanbasepackagecontext:componentscanbasepackage=usermixlayer/>
<beanclassbeanclass=orgspringframeworkwebservletmvcannotation
AnnotationMethodHandlerAdapter/>
<beanclassbeanclass=orgspringframeworkwebservletview
InternalResourceViewResolver
pp:prefix=/WEBINF/jsp/p:suffix=jsp/>
<beanidbeanid=dataSource
class=monsdbcpBasicDataSource
destroymethod=close
p:driverClassName=oraclejdbcdriverOracleDriver
p:url=jdbc:oracle:thin:@localhost::orcl
p:username=test
p:password=test/>
<beanidbeanid=jdbcTemplate
class=orgsprireJdbcTemplate
p:dataSourceref=dataSource/>
<beanidbeanid=jdbcManager
class=orgspringframeworkjdbcdatasourceDataSourceTransactionManager
p:dataSourceref=dataSource/>
<aop:configproxytargetclassaop:configproxytargetclass=true>
<aop:pointcutidaop:pointcutid=serviceJdbcMethod
expression=execution(public*usermixlayerMixLayerUserService*())/>
<aop:advisorpointcutrefaop:advisorpointcutref=serviceJdbcMethod
adviceref=jdbcAdviceorder=/>
</< span>aop:config>
<tx:adviceidtx:adviceid=jdbcAdvicetransactionmanager=jdbcManager>
<tx:attributes>
<tx:methodnametx:methodname=*/>
</< span>tx:attributes>
</< span>tx:advice>
</< span>beans>
在①处我们定义配置了AnnotationMethodHandlerAdapter以便启用SpringMVC的注解驱动功能而②和③处通过Spring的aop及tx命名空间以及Aspject的切点表达式语法进行事务增强的定义对MixLayerUserService的所有公有方法进行事务增强要使程序能够运行起来还必须进行webxml的相关配置
清单webxml
<?xmlversionxmlversion=encoding=GB?>
<webappversionwebappversion=xmlns=
xmlns:xsi=instance
xsi:schemaLocation=
app__xsd>
<contextparam>
<paramname>contextConfigLocation</< span>paramname>
<paramvalue>classpath*:user/mixlayer/applicationContextxml</< span>paramvalue>
</< span>contextparam>
<contextparam>
<paramname>logjConfigLocation</< span>paramname>
<paramvalue>/WEBINF/classes/logjproperties</< span>paramvalue>
</< span>contextparam>
<listener>
<listenerclass>
springframeworkwebutilLogjConfigListener
</< span>listenerclass>
</< span>listener>
<listener>
<listenerclass>
sprntextContextLoaderListener
</< span>listenerclass>
</< span>listener>
<servlet>
<servletname>user</< span>servletname>
<servletclass>
springframeworkwebservletDispatcherServlet
</< span>servletclass>
<initparam>
<paramname>contextConfigLocation</< span>paramname>
<paramvalue>classpath:user/mixlayer/applicationContextxml</< span>paramvalue>
</< span>initparam>
<loadonstartup></< span>loadonstartup>
</< span>servlet>
<servletmapping>
<servletname>user</< span>servletname>
<urlpattern>*do</< span>urlpattern>
</< span>servletmapping>
</< span>webapp>
这个配置文件很简单唯一需要注意的是DispatcherServlet的配置默认情况下SpringMVC根据Servlet的名字查找WEBINF下的servletxml作为SpringMVC的配置文件在此我们通过contextConfigLocation参数显式指定SpringMVC配置文件的确切位置
将orgspringframeworkjdbc及orgspringframeworktransaction的日志级别设置为DEBUG启动项目并访问应用MixLayerUserService#logon方法将作出响应查看后台输出日志
清单执行日志
::DEBUG(AbstractPlatformTransactionManagerjava:)
Creatingnewtransactionwithname
[usermixlayerMixLayerUserServicelogon]:PROPAGATION_REQUIREDISOLATION_DEFAULT
::DEBUG(DataSourceTransactionManagerjava:)
AcquiredConnection[monsdbcpPoolableConnection@ecbf]
forJDBCtransaction
::DEBUG(DataSourceTransactionManagerjava:)
SwitchingJDBCConnection
[monsdbcpPoolableConnection@ecbf]tomanualcommit
::DEBUG(JdbcTemplatejava:)
ExecutingpreparedSQLupdate
::DEBUG(JdbcTemplatejava:)
ExecutingpreparedSQLstatement
[UPDATEt_useruSETuscore=uscore+?WHEREuser_name=?]
::DEBUG(JdbcTemplatejava:)
SQLupdateaffectedrows
::DEBUG(AbstractPlatformTransactionManagerjava:)
Initiatingtransactioncommit
::DEBUG(DataSourceTransactionManagerjava:)
CommittingJDBCtransactiononConnection
[monsdbcpPoolableConnection@ecbf]
::DEBUG(DataSourceTransactionManagerjava:)
ReleasingJDBCConnection[monsdbcpPoolableConnection@ecbf]
aftertransaction
::DEBUG(DataSourceUtilsjava:)
ReturningJDBCConnectiontoDataSource
日志中粗体部分说明了MixLayerUserService#logon方法已经正确运行在事务上下文中Spring框架本身不应该是复杂化代码的理由使用Spring的开发者应该是无拘无束的从实际应用出发去除掉那些所谓原则性的接口去除掉强制分层的束缚简单才是硬道理