應用分層的迷惑
Web、Service及DAO三層劃分就像西方國家的立法、行政、司法三權分立一樣被奉為金科玉律,甚至有開發人員認為如果要使用Spring事務管理就一定先要進行三層的劃分.這個看似荒唐的論調在開發人員中頗有市場.更有甚者,認為每層必須先定義一個介面,然後再定義一個實現類.其結果是:一個很簡單的功能,也至少需要3個介面,3個類,再加上視圖層的JSP和JS等,打牌都可以轉上兩桌了,這種誤解貽害不淺.
對將「面向介面編程」奉為圭臬,認為放之四海而皆準的論調,筆者深不以為然.是的,「面向介面編程」是MartinFowler,RodJohnson這些大師提倡的行事原則.如果拿這條原則去開發架構,開發產品,怎麼強調都不為過.但是,對於我們一般的開發人員來說,做的最多的是普通工程項目,往往最多的只是一些對資料庫增、刪、查、改的功能.此時,「面向介面編程」除了帶來更多的類文件外,看不到更多其它的好處.
Spring框架提供的所有附加的好處(AOP、註解增強、註解MVC等)唯一的前提就是讓POJO的類變成一個受Spring容器管理的Bean,除此以外沒有其它任何的要求.下面的實例用一個POJO完成所有的功能,既是Controller,又是Service,還是DAO:
清單5.MixLayerUserService.java
62.packageuser.mixlayer;
63.importorg.springframework.beans.factory.annotation.Autowired;
64.importorg.springframework.jdbc.core.JdbcTemplate;
65.importorg.springframework.stereotype.Controller;
66.importorg.springframework.web.bind.annotation.RequestMapping;
67.//①.將POJO類通過註解變成SpringMVC的Controller
68.@Controller
69.publicclassMixLayerUserService{
70.
71.//②.自動注入JdbcTemplate
72.@Autowired
73.privateJdbcTemplatejdbcTemplate;
74.
75.//③.通過SpringMVC註解映URL請求
76.@RequestMapping("/logon.do")
77.publicStringlogon(StringuserName,Stringpassword){
78.if(isRightUser(userName,password)){
79.Stringsql="UPDATEt_useruSETu.score=u.score ?WHEREuser_name=?";
80.jdbcTemplate.update(sql,20,userName);
81.return"success";
82.}else{
83.return"fail";
84.}
85.}
86.privatebooleanisRightUser(StringuserName,Stringpassword){
87.//dosth...
88.returntrue;
89.}
90.}
通過@Controller註解將MixLayerUserService變成Web層的Controller,同時也是Service層的服務類.此外,由於直接使用JdbcTemplate訪問數據,MixLayerUserService還是一個DAO.來看一下對應的Spring配置文件:
清單6.applicationContext.xml
91.<?xmlversionxmlversion="1.0"encoding="UTF-8"?>
92.<beansxmlnsbeansxmlns="http://www.springframework.org/schema/beans"
93.xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
94.xmlns:context="http://www.springframework.org/schema/context"
95.xmlns:p="http://www.springframework.org/schema/p"
96.xmlns:aop="http://www.springframework.org/schema/aop"
97.xmlns:tx="http://www.springframework.org/schema/tx"
98.xsi:schemaLocation="http://www.springframework.org/schema/beans
99.http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
100.http://www.springframework.org/schema/context
101. http://www.springframework.org/schema/context/spring-context-3.0.xsd
102. http://www.springframework.org/schema/aop
103. http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
104. http://www.springframework.org/schema/tx
105.http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
106.
107.<context:component-scanbase-packagecontext:component-scanbase-package="user.mixlayer"/>
108.
109.<beanclassbeanclass="org.springframework.web.servlet.mvc.annotation
110. .AnnotationMethodHandlerAdapter"/>
111.
112.
113.<beanclassbeanclass="org.springframework.web.servlet.view
114. .InternalResourceViewResolver"
115.pp:prefix="/WEB-INF/jsp/"p:suffix=".jsp"/>
116.
117.
118.<beanidbeanid="dataSource"
119.class="org.apache.commons.dbcp.BasicDataSource"
120.destroy-method="close"
121.p:driverClassName="oracle.jdbc.driver.OracleDriver"
122.p:url="jdbc:oracle:thin:@localhost:1521:orcl"
123.p:username="test"
124.p:password="test"/>
125.
126.<beanidbeanid="jdbcTemplate"
127.class="org.springframework.jdbc.core.JdbcTemplate"
128.p:dataSource-ref="dataSource"/>
129.
130.
131.<beanidbeanid="jdbcManager"
132.class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
133.p:dataSource-ref="dataSource"/>
134.
135.
136.<aop:configproxy-target-classaop:configproxy-target-class="true">
137.<aop:pointcutidaop:pointcutid="serviceJdbcMethod"
138.expression="execution(public*user.mixlayer.MixLayerUserService.*(..))"/>
139.<aop:advisorpointcut-refaop:advisorpointcut-ref="serviceJdbcMethod"
140.advice-ref="jdbcAdvice"order="0"/>
141.</< span>aop:config>
142.<tx:adviceidtx:adviceid="jdbcAdvice"transaction-manager="jdbcManager">
143.<tx:attributes>
144.<tx:methodnametx:methodname="*"/>
145.</< span>tx:attributes>
146.</< span>tx:advice>
147.</< span>beans>
在①處,我們定義配置了AnnotationMethodHandlerAdapter,以便啟用SpringMVC的註解驅動功能.而②和③處通過Spring的aop及tx命名空間,以及Aspject的切點表達式語法進行事務增強的定義,對MixLayerUserService的所有公有方法進行事務增強.要使程序能夠運行起來還必須進行web.xml的相關配置:
清單7.web.xml
1.<?xmlversionxmlversion="1.0"encoding="GB2312"?>
2.<web-appversionweb-appversion="2.4"xmlns="http://java.sun.com/xml/ns/j2ee"
3.xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4.xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
5.http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
6.<context-param>
7.<param-name>contextConfigLocation</< span>param-name>
8.<param-value>classpath*:user/mixlayer/applicationContext.xml</< span>param-value>
9.</< span>context-param>
10.<context-param>
11.<param-name>log4jConfigLocation</< span>param-name>
12.<param-value>/WEB-INF/classes/log4j.properties</< span>param-value>
13.</< span>context-param>
14.
15.<listener>
16.<listener-class>
17.org.springframework.web.util.Log4jConfigListener
18.</< span>listener-class>
19.</< span>listener>
20.<listener>
21.<listener-class>
22.org.springframework.web.context.ContextLoaderListener
23.</< span>listener-class>
24.</< span>listener>
25.
26.<servlet>
27.<servlet-name>user</< span>servlet-name>
28.<servlet-class>
29.org.springframework.web.servlet.DispatcherServlet
30.</< span>servlet-class>
31.
32.<init-param>
33.<param-name>contextConfigLocation</< span>param-name>
34.<param-value>classpath:user/mixlayer/applicationContext.xml</< span>param-value>
35.</< span>init-param>
36.<load-on-startup>1</< span>load-on-startup>
37.</< span>servlet>
38.<servlet-mapping>
39.<servlet-name>user</< span>servlet-name>
40.<url-pattern>*.do</< span>url-pattern>
41.</< span>servlet-mapping>
42.</< span>web-app>
這個配置文件很簡單,唯一需要注意的是DispatcherServlet的配置.默認情況下SpringMVC根據Servlet的名字查找WEB-INF下的-servlet.xml作為SpringMVC的配置文件,在此,我們通過contextConfigLocation參數顯式指定SpringMVC配置文件的確切位置.
將org.springframework.jdbc及org.springframework.transaction的日誌級別設置為DEBUG,啟動項目,並訪問http://localhost:8088/logon.do?userName=tom應用,MixLayerUserService#logon方法將作出響應,查看後台輸出日誌:
清單8執行日誌
43.13:24:22,625DEBUG(AbstractPlatformTransactionManager.java:365)-
44.Creatingnewtransactionwithname
45. [user.mixlayer.MixLayerUserService.logon]:PROPAGATION_REQUIRED,ISOLATION_DEFAULT
46.13:24:22,906DEBUG(DataSourceTransactionManager.java:205)-
47.AcquiredConnection[org.apache.commons.dbcp.PoolableConnection@6e1cbf]
48. forJDBCtransaction
49.13:24:22,921DEBUG(DataSourceTransactionManager.java:222)-
50.SwitchingJDBCConnection
51. [org.apache.commons.dbcp.PoolableConnection@6e1cbf]tomanualcommit
52.13:24:22,921DEBUG(JdbcTemplate.java:785)-
53.ExecutingpreparedSQLupdate
54.13:24:22,921DEBUG(JdbcTemplate.java:569)-
55.ExecutingpreparedSQLstatement
56. [UPDATEt_useruSETu.score=u.score ?WHEREuser_name=?]
57.13:24:23,140DEBUG(JdbcTemplate.java:794)-
58.SQLupdateaffected0rows
59.13:24:23,140DEBUG(AbstractPlatformTransactionManager.java:752)-
60.Initiatingtransactioncommit
61.13:24:23,140DEBUG(DataSourceTransactionManager.java:265)-
62.CommittingJDBCtransactiononConnection
63. [org.apache.commons.dbcp.PoolableConnection@6e1cbf]
64.13:24:23,140DEBUG(DataSourceTransactionManager.java:323)-
65.ReleasingJDBCConnection[org.apache.commons.dbcp.PoolableConnection@6e1cbf]
66. aftertransaction
67.13:24:23,156DEBUG(DataSourceUtils.java:312)-
68.ReturningJDBCConnectiontoDataSource
日誌中粗體部分說明了MixLayerUserService#logon方法已經正確運行在事務上下文中.Spring框架本身不應該是複雜化代碼的理由,使用Spring的開發者應該是無拘無束的:從實際應用出發,去除掉那些所謂原則性的介面,去除掉強制分層的束縛,簡單才是硬道理.
[火星人 ] Spring事務管理高級應用難點剖析(2)已經有894次圍觀