2020-06-03

本文發(fā)表于2016年6月入问,寫于作者學(xué)生時期。文中使用到的技術(shù)和框架可能不是當(dāng)下最佳實踐,甚至很不“優(yōu)雅”。但對于剛接觸JavaEE和Spring的同學(xué)來說锚扎,還是能有很多收獲的惯疙,大牛輕拍= =

我們看招聘信息的時候,經(jīng)常會看到這一點(diǎn),需要具備SSH框架的技能骑脱;而且在大部分教學(xué)課堂中岳瞭,也會把SSH作為最核心的教學(xué)內(nèi)容。

但是,我們在實際應(yīng)用中發(fā)現(xiàn),SpringMVC可以完全替代Struts摊沉,配合注解的方式苍柏,編程非彻卓茫快捷楼咳,而且通過restful風(fēng)格定義url,讓地址看起來非常優(yōu)雅烛恤。

另外母怜,MyBatis也可以替換Hibernate缚柏,正因為MyBatis的半自動特點(diǎn)苹熏,我們程序猿可以完全掌控SQL,這會讓有數(shù)據(jù)庫經(jīng)驗的程序猿能開發(fā)出高效率的SQL語句币喧,而且XML配置管理起來也非常方便轨域。

好了,如果你也認(rèn)同我的看法杀餐,那么下面我們一起來做整合吧干发!

在寫代碼之前我們先了解一下這三個框架分別是干什么的?

相信大以前也看過不少這些概念怜浅,我這就用大白話來講铐然,如果之前有了解過可以跳過這一大段,直接看代碼恶座!

SpringMVC:它用于web層搀暑,相當(dāng)于controller(等價于傳統(tǒng)的servlet和struts的action),用來處理用戶請求跨琳。舉個例子自点,用戶在地址欄輸入http://網(wǎng)站域名/login,那么springmvc就會攔截到這個請求脉让,并且調(diào)用controller層中相應(yīng)的方法桂敛,(中間可能包含驗證用戶名和密碼的業(yè)務(wù)邏輯,以及查詢數(shù)據(jù)庫操作溅潜,但這些都不是springmvc的職責(zé))术唬,最終把結(jié)果返回給用戶,并且返回相應(yīng)的頁面(當(dāng)然也可以只返回json/xml等格式數(shù)據(jù))滚澜。springmvc就是做前面和后面過程的活粗仓,與用戶打交道!设捐!

Spring:太強(qiáng)大了借浊,以至于我無法用一個詞或一句話來概括它。但與我們平時開發(fā)接觸最多的估計就是IOC容器萝招,它可以裝載bean(也就是我們java中的類蚂斤,當(dāng)然也包括service dao里面的),有了這個機(jī)制槐沼,我們就不用在每次使用這個類的時候為它初始化曙蒸,很少看到關(guān)鍵字new捌治。另外spring的aop,事務(wù)管理等等都是我們經(jīng)常用到的逸爵。

MyBatis:如果你問我它跟鼎鼎大名的Hibernate有什么區(qū)別具滴?我只想說,他更符合我的需求师倔。第一,它能自由控制sql周蹭,這會讓有數(shù)據(jù)庫經(jīng)驗的人(當(dāng)然不是說我啦捂臉)編寫的代碼能搞提升數(shù)據(jù)庫訪問的效率趋艘。第二,它可以使用xml的方式來組織管理我們的sql凶朗,因為一般程序出錯很多情況下是sql出錯瓷胧,別人接手代碼后能快速找到出錯地方,甚至可以優(yōu)化原來寫的sql棚愤。

SSM框架整合配置

好了搓萧,前面bb那么多,下面我們真正開始敲代碼了~

首先我們打開IED宛畦,我這里用的是eclipse(你們應(yīng)該也是用的這個瘸洛,對嗎?)次和,創(chuàng)建一個動態(tài)web項目反肋,建立好相應(yīng)的目錄結(jié)構(gòu)(重點(diǎn)!)

(打了馬賽克是因為這里還用不到踏施,你們不要那么污好不好石蔗?)

我說一下每個目錄都有什么用吧(第一次畫表格,我發(fā)現(xiàn)markdown的表格語法很不友好呀~)

這個目錄結(jié)構(gòu)同時也遵循maven的目錄規(guī)范~

文件名 作用

src 根目錄畅形,沒什么好說的养距,下面有main和test。

- main 主要目錄日熬,可以放java代碼和一些資源文件棍厌。

- - java 存放我們的java代碼,這個文件夾要使用Build Path -> Use as Source Folder碍遍,這樣看包結(jié)構(gòu)會方便很多定铜,新建的包就相當(dāng)于在這里新建文件夾咯。

- - resources 存放資源文件怕敬,譬如各種的spring揣炕,mybatis,log配置文件东跪。

- - - mapper 存放dao中每個方法對應(yīng)的sql畸陡,在這里配置鹰溜,無需寫daoImpl。

- - - spring 這里當(dāng)然是存放spring相關(guān)的配置文件丁恭,有dao service web三層曹动。

- - - sql 其實這個可以沒有,但是為了項目完整性還是加上吧牲览。

- - - webapp 這個貌似是最熟悉的目錄了墓陈,用來存放我們前端的靜態(tài)資源,如jsp js css第献。

- - - - resources 這里的資源是指項目的靜態(tài)資源贡必,如js css images等。

- - - - WEB-INF 很重要的一個目錄庸毫,外部瀏覽器無法訪問仔拟,只有羨慕內(nèi)部才能訪問,可以把jsp放在這里飒赃,另外就是web.xml了利花。你可能有疑問了,為什么上面java中的resources里面的配置文件不妨在這里载佳,那么是不是會被外部竊取到炒事?你想太多了,部署時候基本上只有webapp里的會直接輸出到根目錄刚盈,其他都會放入WEB-INF里面羡洛,項目內(nèi)部依然可以使用classpath:XXX來訪問,好像IDE里可以設(shè)置部署輸出目錄藕漱,這里扯遠(yuǎn)了~

- test 這里是測試分支欲侮。

- - java 測試java代碼,應(yīng)遵循包名相同的原則肋联,這個文件夾同樣要使用Build Path -> Use as Source Folder威蕉,這樣看包結(jié)構(gòu)會方便很多。

- - resources 沒什么好說的橄仍,好像也很少用到韧涨,但這個是maven的規(guī)范。

我先新建好幾個必要的包侮繁,并為大家講解一下每個包的作用虑粥,順便理清一下后臺的思路~

包名 名稱 作用

dao 數(shù)據(jù)訪問層(接口) 與數(shù)據(jù)打交道,可以是數(shù)據(jù)庫操作宪哩,也可以是文件讀寫操作娩贷,甚至是redis緩存操作,總之與數(shù)據(jù)操作有關(guān)的都放在這里锁孟,也有人叫做dal或者數(shù)據(jù)持久層都差不多意思彬祖。為什么沒有daoImpl茁瘦,因為我們用的是mybatis,所以可以直接在配置文件中實現(xiàn)接口的每個方法储笑。

entity 實體類 一般與數(shù)據(jù)庫的表相對應(yīng)甜熔,封裝dao層取出來的數(shù)據(jù)為一個對象,也就是我們常說的pojo突倍,一般只在dao層與service層之間傳輸腔稀。

dto 數(shù)據(jù)傳輸層 剛學(xué)框架的人可能不明白這個有什么用,其實就是用于service層與web層之間傳輸羽历,為什么不直接用entity(pojo)烧颖?其實在實際開發(fā)中發(fā)現(xiàn),很多時間一個entity并不能滿足我們的業(yè)務(wù)需求窄陡,可能呈現(xiàn)給用戶的信息十分之多,這時候就有了dto拆火,也相當(dāng)于vo跳夭,記住一定不要把這個混雜在entity里面,答應(yīng)我好嗎们镜?

service 業(yè)務(wù)邏輯(接口) 寫我們的業(yè)務(wù)邏輯币叹,也有人叫bll,在設(shè)計業(yè)務(wù)接口時候應(yīng)該站在“使用者”的角度模狭。額颈抚,不要問我為什么這里沒顯示!IDE調(diào)皮我也拿它沒辦法~

serviceImpl 業(yè)務(wù)邏輯(實現(xiàn)) 實現(xiàn)我們業(yè)務(wù)接口嚼鹉,一般事務(wù)控制是寫在這里贩汉,沒什么好說的覆获。

web 控制器 springmvc就是在這里發(fā)揮作用的灸异,一般人叫做controller控制器梆暖,相當(dāng)于struts中的action擂啥。

還有最后一步基礎(chǔ)工作凝果,導(dǎo)入我們相應(yīng)的jar包搓劫,我使用的是maven來管理我們的jar塑荒,所以只需要在pom.xml中加入相應(yīng)的依賴就好了党瓮,如果不使用maven的可以自己去官網(wǎng)下載相應(yīng)的jar浑侥,放到項目WEB-INF/lib目錄下姊舵。關(guān)于maven的學(xué)習(xí)大家可以看慕課網(wǎng)的視頻教程,這里就不展開了寓落。我把項目用到的jar都寫在下面括丁,版本都不是最新的,大家有經(jīng)驗的話可以自己調(diào)整版本號零如。另外躏将,所有jar都會與項目一起打包放到我的github上锄弱,喜歡的給個star吧~

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

<modelVersion>4.0.0</modelVersion>

<groupId>com.soecode.ssm</groupId>

<artifactId>ssm</artifactId>

<packaging>war</packaging>

<version>0.0.1-SNAPSHOT</version>

<name>ssm Maven Webapp</name>

<url>http://github.com/liyifeng1994/ssm</url>

<dependencies>

<!-- 單元測試 -->

<dependency>

<groupId>junit</groupId>

<artifactId>junit</artifactId>

<version>4.11</version>

</dependency>

<!-- 1.日志 -->

<!-- 實現(xiàn)slf4j接口并整合 -->

<dependency>

<groupId>ch.qos.logback</groupId>

<artifactId>logback-classic</artifactId>

<version>1.1.1</version>

</dependency>

<!-- 2.數(shù)據(jù)庫 -->

<dependency>

<groupId>mysql</groupId>

<artifactId>mysql-connector-java</artifactId>

<version>5.1.37</version>

<scope>runtime</scope>

</dependency>

<dependency>

<groupId>c3p0</groupId>

<artifactId>c3p0</artifactId>

<version>0.9.1.2</version>

</dependency>

<!-- DAO: MyBatis -->

<dependency>

<groupId>org.mybatis</groupId>

<artifactId>mybatis</artifactId>

<version>3.3.0</version>

</dependency>

<dependency>

<groupId>org.mybatis</groupId>

<artifactId>mybatis-spring</artifactId>

<version>1.2.3</version>

</dependency>

<!-- 3.Servlet web -->

<dependency>

<groupId>taglibs</groupId>

<artifactId>standard</artifactId>

<version>1.1.2</version>

</dependency>

<dependency>

<groupId>jstl</groupId>

<artifactId>jstl</artifactId>

<version>1.2</version>

</dependency>

<dependency>

<groupId>com.fasterxml.jackson.core</groupId>

<artifactId>jackson-databind</artifactId>

<version>2.5.4</version>

</dependency>

<dependency>

<groupId>javax.servlet</groupId>

<artifactId>javax.servlet-api</artifactId>

<version>3.1.0</version>

</dependency>

<!-- 4.Spring -->

<!-- 1)Spring核心 -->

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-core</artifactId>

<version>4.1.7.RELEASE</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-beans</artifactId>

<version>4.1.7.RELEASE</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-context</artifactId>

<version>4.1.7.RELEASE</version>

</dependency>

<!-- 2)Spring DAO層 -->

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-jdbc</artifactId>

<version>4.1.7.RELEASE</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-tx</artifactId>

<version>4.1.7.RELEASE</version>

</dependency>

<!-- 3)Spring web -->

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-web</artifactId>

<version>4.1.7.RELEASE</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-webmvc</artifactId>

<version>4.1.7.RELEASE</version>

</dependency>

<!-- 4)Spring test -->

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-test</artifactId>

<version>4.1.7.RELEASE</version>

</dependency>

<!-- redis客戶端:Jedis -->

<dependency>

<groupId>redis.clients</groupId>

<artifactId>jedis</artifactId>

<version>2.7.3</version>

</dependency>

<dependency>

<groupId>com.dyuproject.protostuff</groupId>

<artifactId>protostuff-core</artifactId>

<version>1.0.8</version>

</dependency>

<dependency>

<groupId>com.dyuproject.protostuff</groupId>

<artifactId>protostuff-runtime</artifactId>

<version>1.0.8</version>

</dependency>

<!-- Map工具類 -->

<dependency>

<groupId>commons-collections</groupId>

<artifactId>commons-collections</artifactId>

<version>3.2</version>

</dependency>

</dependencies>

<build>

<finalName>ssm</finalName>

</build>

</project>

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

下面真的要開始進(jìn)行編碼工作了,堅持到這里辛苦大家了~

第一步:我們先在spring文件夾里新建spring-dao.xml文件祸憋,因為spring的配置太多会宪,我們這里分三層,分別是dao service web蚯窥。

讀入數(shù)據(jù)庫連接相關(guān)參數(shù)(可選)

配置數(shù)據(jù)連接池

配置連接屬性掸鹅,可以不讀配置項文件直接在這里寫死

配置c3p0,只配了幾個常用的

配置SqlSessionFactory對象(mybatis)

掃描dao層接口拦赠,動態(tài)實現(xiàn)dao接口巍沙,也就是說不需要daoImpl,sql和參數(shù)都寫在xml文件上

spring-dao.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:context="http://www.springframework.org/schema/context"

xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans.xsd

http://www.springframework.org/schema/context

http://www.springframework.org/schema/context/spring-context.xsd">

<!-- 配置整合mybatis過程 -->

<!-- 1.配置數(shù)據(jù)庫相關(guān)參數(shù)properties的屬性:${url} -->

<context:property-placeholder location="classpath:jdbc.properties" />

<!-- 2.數(shù)據(jù)庫連接池 -->

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">

<!-- 配置連接池屬性 -->

<property name="driverClass" value="${jdbc.driver}" />

<property name="jdbcUrl" value="${jdbc.url}" />

<property name="user" value="${jdbc.username}" />

<property name="password" value="${jdbc.password}" />

<!-- c3p0連接池的私有屬性 -->

<property name="maxPoolSize" value="30" />

<property name="minPoolSize" value="10" />

<!-- 關(guān)閉連接后不自動commit -->

<property name="autoCommitOnClose" value="false" />

<!-- 獲取連接超時時間 -->

<property name="checkoutTimeout" value="10000" />

<!-- 當(dāng)獲取連接失敗重試次數(shù) -->

<property name="acquireRetryAttempts" value="2" />

</bean>

<!-- 3.配置SqlSessionFactory對象 -->

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">

<!-- 注入數(shù)據(jù)庫連接池 -->

<property name="dataSource" ref="dataSource" />

<!-- 配置MyBaties全局配置文件:mybatis-config.xml -->

<property name="configLocation" value="classpath:mybatis-config.xml" />

<!-- 掃描entity包 使用別名 -->

<property name="typeAliasesPackage" value="com.soecode.lyf.entity" />

<!-- 掃描sql配置文件:mapper需要的xml文件 -->

<property name="mapperLocations" value="classpath:mapper/*.xml" />

</bean>

<!-- 4.配置掃描Dao接口包荷鼠,動態(tài)實現(xiàn)Dao接口句携,注入到spring容器中 -->

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">

<!-- 注入sqlSessionFactory -->

<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />

<!-- 給出需要掃描Dao接口包 -->

<property name="basePackage" value="com.soecode.lyf.dao" />

</bean>

</beans>

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

因為數(shù)據(jù)庫配置相關(guān)參數(shù)是讀取配置文件,所以在resources文件夾里新建一個jdbc.properties文件允乐,存放我們4個最常見的數(shù)據(jù)庫連接屬性矮嫉,這是我本地的,大家記得修改呀~還有喜歡傳到github上“大頭蝦們”記得刪掉密碼牍疏,不然別人就很容易得到你服務(wù)器的數(shù)據(jù)庫配置信息蠢笋,然后干一些羞羞的事情,你懂的A墼伞昨寞!

jdbc.properties

jdbc.driver=com.mysql.jdbc.Driver

jdbc.url=jdbc:mysql://localhost:3307/ssm?useUnicode=true&characterEncoding=utf8

jdbc.username=root

jdbc.password=

1

2

3

4

友情提示:配置文件中的jdbc.username,如果寫成username厦滤,可能會與系統(tǒng)環(huán)境中的username變量沖突援岩,所以到時候真正連接數(shù)據(jù)庫的時候,用戶名就被替換成系統(tǒng)中的用戶名(有得可能是administrator)馁害,那肯定是連接不成功的窄俏,這里有個小坑,我被坑了一晚上5獠恕凹蜈!

因為這里用到了mybatis,所以需要配置mybatis核心文件忍啸,在recources文件夾里新建mybatis-config.xml文件仰坦。

使用自增主鍵

使用列別名

開啟駝峰命名轉(zhuǎn)換 create_time -> createTime

mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE configuration

? PUBLIC "-//mybatis.org//DTD Config 3.0//EN"

? "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>

<!-- 配置全局屬性 -->

<settings>

<!-- 使用jdbc的getGeneratedKeys獲取數(shù)據(jù)庫自增主鍵值 -->

<setting name="useGeneratedKeys" value="true" />

<!-- 使用列別名替換列名 默認(rèn):true -->

<setting name="useColumnLabel" value="true" />

<!-- 開啟駝峰命名轉(zhuǎn)換:Table{create_time} -> Entity{createTime} -->

<setting name="mapUnderscoreToCamelCase" value="true" />

</settings>

</configuration>

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

第二步:剛弄好dao層,接下來到service層了计雌。在spring文件夾里新建spring-service.xml文件悄晃。

掃描service包所有注解 @Service

配置事務(wù)管理器,把事務(wù)管理交由spring來完成

配置基于注解的聲明式事務(wù),可以直接在方法上@Transaction

spring-service.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:context="http://www.springframework.org/schema/context"

xmlns:tx="http://www.springframework.org/schema/tx"

xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans.xsd

http://www.springframework.org/schema/context

http://www.springframework.org/schema/context/spring-context.xsd

http://www.springframework.org/schema/tx

http://www.springframework.org/schema/tx/spring-tx.xsd">

<!-- 掃描service包下所有使用注解的類型 -->

<context:component-scan base-package="com.soecode.lyf.service" />

<!-- 配置事務(wù)管理器 -->

<bean id="transactionManager"

class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

<!-- 注入數(shù)據(jù)庫連接池 -->

<property name="dataSource" ref="dataSource" />

</bean>

<!-- 配置基于注解的聲明式事務(wù) -->

<tx:annotation-driven transaction-manager="transactionManager" />

</beans>

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

第三步:配置web層妈橄,在spring文件夾里新建spring-web.xml文件庶近。

開啟SpringMVC注解模式,可以使用@RequestMapping眷蚓,@PathVariable鼻种,@ResponseBody等

對靜態(tài)資源處理,如js沙热,css叉钥,jpg等

配置jsp 顯示ViewResolver,例如在controller中某個方法返回一個string類型的"login"篙贸,實際上會返回"/WEB-INF/login.jsp"

掃描web層 @Controller

spring-web.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:context="http://www.springframework.org/schema/context"

xmlns:mvc="http://www.springframework.org/schema/mvc"

xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans.xsd

http://www.springframework.org/schema/context

http://www.springframework.org/schema/context/spring-context.xsd

http://www.springframework.org/schema/mvc

http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">

<!-- 配置SpringMVC -->

<!-- 1.開啟SpringMVC注解模式 -->

<!-- 簡化配置:

(1)自動注冊DefaultAnootationHandlerMapping,AnotationMethodHandlerAdapter

(2)提供一些列:數(shù)據(jù)綁定投队,數(shù)字和日期的format @NumberFormat, @DateTimeFormat, xml,json默認(rèn)讀寫支持

-->

<mvc:annotation-driven />

<!-- 2.靜態(tài)資源默認(rèn)servlet配置

(1)加入對靜態(tài)資源的處理:js,gif,png

(2)允許使用"/"做整體映射

-->

<mvc:default-servlet-handler/>

<!-- 3.配置jsp 顯示ViewResolver -->

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">

<property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />

<property name="prefix" value="/WEB-INF/jsp/" />

<property name="suffix" value=".jsp" />

</bean>

<!-- 4.掃描web相關(guān)的bean -->

<context:component-scan base-package="com.soecode.lyf.web" />

</beans>

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

第四步:最后就是修改web.xml文件了,它在webapp的WEB-INF下爵川。

web.xml

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee

? ? ? ? ? ? ? ? ? ? ? http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"

version="3.1" metadata-complete="true">

<!-- 如果是用mvn命令生成的xml敷鸦,需要修改servlet版本為3.1 -->

<!-- 配置DispatcherServlet -->

<servlet>

<servlet-name>seckill-dispatcher</servlet-name>

<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<!-- 配置springMVC需要加載的配置文件

spring-dao.xml,spring-service.xml,spring-web.xml

Mybatis - > spring -> springmvc

-->

<init-param>

<param-name>contextConfigLocation</param-name>

<param-value>classpath:spring/spring-*.xml</param-value>

</init-param>

</servlet>

<servlet-mapping>

<servlet-name>seckill-dispatcher</servlet-name>

<!-- 默認(rèn)匹配所有的請求 -->

<url-pattern>/</url-pattern>

</servlet-mapping>

</web-app>

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

我們在項目中經(jīng)常會使用到日志,所以這里還有配置日志xml寝贡,在resources文件夾里新建logback.xml文件轧膘,所給出的日志輸出格式也是最基本的控制臺s呼出,大家有興趣查看logback官方文檔兔甘。

logback.xml

<?xml version="1.0" encoding="UTF-8"?>

<configuration debug="true">

<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">

<!-- encoders are by default assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder -->

<encoder>

<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>

</encoder>

</appender>

<root level="debug">

<appender-ref ref="STDOUT" />

</root>

</configuration>

1

2

3

4

5

6

7

8

9

10

11

12

13

到目前為止,我們一共寫了7個配置文件鳞滨,我們一起來看下最終的配置文件結(jié)構(gòu)圖洞焙。

SSM框架應(yīng)用實例(圖書管理系統(tǒng))

一開始想就這樣結(jié)束教程,但是發(fā)現(xiàn)其實很多人都還不會把這個SSM框架用起來拯啦,特別是mybatis部分澡匪。那我現(xiàn)在就以最常見的“圖書管理系統(tǒng)”中【查詢圖書】和【預(yù)約圖書】業(yè)務(wù)來做一個demo吧!

首先新建數(shù)據(jù)庫名為ssm褒链,再創(chuàng)建兩張表:圖書表book和預(yù)約圖書表appointment唁情,并且為book表初始化一些數(shù)據(jù),sql如下甫匹。

schema.sql

-- 創(chuàng)建圖書表

CREATE TABLE `book` (

? `book_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '圖書ID',

? `name` varchar(100) NOT NULL COMMENT '圖書名稱',

? `number` int(11) NOT NULL COMMENT '館藏數(shù)量',

? PRIMARY KEY (`book_id`)

) ENGINE=InnoDB AUTO_INCREMENT=1000 DEFAULT CHARSET=utf8 COMMENT='圖書表'

-- 初始化圖書數(shù)據(jù)

INSERT INTO `book` (`book_id`, `name`, `number`)

VALUES

(1000, 'Java程序設(shè)計', 10),

(1001, '數(shù)據(jù)結(jié)構(gòu)', 10),

(1002, '設(shè)計模式', 10),

(1003, '編譯原理', 10)

-- 創(chuàng)建預(yù)約圖書表

CREATE TABLE `appointment` (

? `book_id` bigint(20) NOT NULL COMMENT '圖書ID',

? `student_id` bigint(20) NOT NULL COMMENT '學(xué)號',

? `appoint_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '預(yù)約時間' ,

? PRIMARY KEY (`book_id`, `student_id`),

? INDEX `idx_appoint_time` (`appoint_time`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='預(yù)約圖書表'

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

在entity包中添加兩個對應(yīng)的實體甸鸟,圖書實體Book.java和預(yù)約圖書實體Appointment.java。

Book.java

package com.soecode.lyf.entity;

public class Book {

private long bookId;// 圖書ID

private String name;// 圖書名稱

private int number;// 館藏數(shù)量

// 省略構(gòu)造方法兵迅,getter和setter方法抢韭,toString方法

}

1

2

3

4

5

6

7

8

9

10

11

12

13

Appointment.java

package com.soecode.lyf.entity;

import java.util.Date;

/**

* 預(yù)約圖書實體

*/

public class Appointment {

private long bookId;// 圖書ID

private long studentId;// 學(xué)號

private Date appointTime;// 預(yù)約時間

// 多對一的復(fù)合屬性

private Book book;// 圖書實體

// 省略構(gòu)造方法,getter和setter方法恍箭,toString方法

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

在dao包新建接口BookDao.java和Appointment.java

BookDao.java

package com.soecode.lyf.dao;

import java.util.List;

import com.soecode.lyf.entity.Book;

public interface BookDao {

/**

* 通過ID查詢單本圖書

*

* @param id

* @return

*/

Book queryById(long id);

/**

* 查詢所有圖書

*

* @param offset 查詢起始位置

* @param limit 查詢條數(shù)

* @return

*/

List<Book> queryAll(@Param("offset") int offset, @Param("limit") int limit);

/**

* 減少館藏數(shù)量

*

* @param bookId

* @return 如果影響行數(shù)等于>1刻恭,表示更新的記錄行數(shù)

*/

int reduceNumber(long bookId);

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

AppointmentDao.java

package com.soecode.lyf.dao;

import org.apache.ibatis.annotations.Param;

import com.soecode.lyf.entity.Appointment;

public interface AppointmentDao {

/**

* 插入預(yù)約圖書記錄

*

* @param bookId

* @param studentId

* @return 插入的行數(shù)

*/

int insertAppointment(@Param("bookId") long bookId, @Param("studentId") long studentId);

/**

* 通過主鍵查詢預(yù)約圖書記錄,并且攜帶圖書實體

*

* @param bookId

* @param studentId

* @return

*/

Appointment queryByKeyWithBook(@Param("bookId") long bookId, @Param("studentId") long studentId);

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

提示:這里為什么要給方法的參數(shù)添加@Param注解呢扯夭?是因為該方法有兩個或以上的參數(shù)鳍贾,一定要加鞍匾,不然mybatis識別不了。上面的BookDao接口的queryById方法和reduceNumber方法只有一個參數(shù)book_id骑科,所以可以不用加 @Param注解橡淑,當(dāng)然加了也無所謂~

注意,這里不需要實現(xiàn)dao接口不用編寫daoImpl纵散, mybatis會給我們動態(tài)實現(xiàn)梳码,但是我們需要編寫相應(yīng)的mapper。

在mapper目錄里新建兩個文件BookDao.xml和AppointmentDao.xml伍掀,分別對應(yīng)上面兩個dao接口掰茶,代碼如下。

BookDao.xml

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE mapper

? ? PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"

? ? "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.soecode.lyf.dao.BookDao">

<!-- 目的:為dao接口方法提供sql語句配置 -->

<select id="queryById" resultType="Book" parameterType="long">

<!-- 具體的sql -->

SELECT

book_id,

name,

number

FROM

book

WHERE

book_id = #{bookId}

</select>

<select id="queryAll" resultType="Book">

SELECT

book_id,

name,

number

FROM

book

ORDER BY

book_id

LIMIT #{offset}, #{limit}

</select>

<update id="reduceNumber">

UPDATE book

SET number = number - 1

WHERE

book_id = #{bookId}

AND number > 0

</update>

</mapper>

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

AppointmentDao.xml

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE mapper

? ? PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"

? ? "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.soecode.lyf.dao.AppointmentDao">

<insert id="insertAppointment">

<!-- ignore 主鍵沖突蜜笤,報錯 -->

INSERT ignore INTO appointment (book_id, student_id)

VALUES (#{bookId}, #{studentId})

</insert>

<select id="queryByKeyWithBook" resultType="Appointment">

<!-- 如何告訴MyBatis把結(jié)果映射到Appointment同時映射book屬性 -->

<!-- 可以自由控制SQL -->

SELECT

a.book_id,

a.student_id,

a.appoint_time,

b.book_id "book.book_id",

b.`name` "book.name",

b.number "book.number"

FROM

appointment a

INNER JOIN book b ON a.book_id = b.book_id

WHERE

a.book_id = #{bookId}

AND a.student_id = #{studentId}

</select>

</mapper>

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

mapper總結(jié):namespace是該xml對應(yīng)的接口全名濒蒋,select和update中的id對應(yīng)方法名,resultType是返回值類型把兔,parameterType是參數(shù)類型(這個其實可選)沪伙,最后#{...}中填寫的是方法的參數(shù),看懂了是不是很簡單O睾谩围橡!我也這么覺得~ 還有一個小技巧要交給大家,就是在返回Appointment對象包含了一個屬性名為book的Book對象缕贡,那么可以使用"book.屬性名"的方式來取值翁授,看上面queryByKeyWithBook方法的sql。

dao層寫完了晾咪,接下來test對應(yīng)的package寫我們測試方法吧收擦。

因為我們之后會寫很多測試方法,在測試前需要讓程序讀入spring-dao和mybatis等配置文件谍倦,所以我這里就抽離出來一個BaseTest類塞赂,只要是測試方法就繼承它,這樣那些繁瑣的重復(fù)的代碼就不用寫那么多了~

BaseTest.java

package com.soecode.lyf;

import org.junit.runner.RunWith;

import org.springframework.test.context.ContextConfiguration;

import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**

* 配置spring和junit整合昼蛀,junit啟動時加載springIOC容器 spring-test,junit

*/

@RunWith(SpringJUnit4ClassRunner.class)

// 告訴junit spring配置文件

@ContextConfiguration({ "classpath:spring/spring-dao.xml", "classpath:spring/spring-service.xml" })

public class BaseTest {

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

因為spring-service在service層的測試中會時候到宴猾,這里也一起引入算了!

新建BookDaoTest.java和AppointmentDaoTest.java兩個dao測試文件叼旋。

BookDaoTest.java

package com.soecode.lyf.dao;

import java.util.List;

import org.junit.Test;

import org.springframework.beans.factory.annotation.Autowired;

import com.soecode.lyf.BaseTest;

import com.soecode.lyf.entity.Book;

public class BookDaoTest extends BaseTest {

@Autowired

private BookDao bookDao;

@Test

public void testQueryById() throws Exception {

long bookId = 1000;

Book book = bookDao.queryById(bookId);

System.out.println(book);

}

@Test

public void testQueryAll() throws Exception {

List<Book> books = bookDao.queryAll(0, 4);

for (Book book : books) {

System.out.println(book);

}

}

@Test

public void testReduceNumber() throws Exception {

long bookId = 1000;

int update = bookDao.reduceNumber(bookId);

System.out.println("update=" + update);

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

BookDaoTest測試結(jié)果

testQueryById

testQueryAll

testReduceNumber

AppointmentDaoTest.java

package com.soecode.lyf.dao;

import org.junit.Test;

import org.springframework.beans.factory.annotation.Autowired;

import com.soecode.lyf.BaseTest;

import com.soecode.lyf.entity.Appointment;

public class AppointmentDaoTest extends BaseTest {

@Autowired

private AppointmentDao appointmentDao;

@Test

public void testInsertAppointment() throws Exception {

long bookId = 1000;

long studentId = 12345678910L;

int insert = appointmentDao.insertAppointment(bookId, studentId);

System.out.println("insert=" + insert);

}

@Test

public void testQueryByKeyWithBook() throws Exception {

long bookId = 1000;

long studentId = 12345678910L;

Appointment appointment = appointmentDao.queryByKeyWithBook(bookId, studentId);

System.out.println(appointment);

System.out.println(appointment.getBook());

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

AppointmentDaoTest測試結(jié)果

testInsertAppointment

testQueryByKeyWithBook

嗯鳍置,到這里一切到很順利那么我們繼續(xù)service層的編碼吧可能下面開始信息里比較大,大家要做好心理準(zhǔn)備~

首先送淆,在寫我們的控制器之前税产,我們先定義幾個預(yù)約圖書操作返回碼的數(shù)據(jù)字典,也就是我們要返回給客戶端的信息。我們這類使用枚舉類辟拷,沒聽過的小伙伴要好好惡補(bǔ)一下了(我也是最近才學(xué)到的= =)

預(yù)約業(yè)務(wù)操作返回碼說明

返回碼 說明

1 預(yù)約成功

0 庫存不足

-1 重復(fù)預(yù)約

-2 系統(tǒng)異常

新建一個包叫enums撞羽,在里面新建一個枚舉類AppointStateEnum.java,用來定義預(yù)約業(yè)務(wù)的數(shù)據(jù)字典衫冻,沒聽懂沒關(guān)系诀紊,我們直接看代碼吧~是不是感覺有模有樣了!

AppointStateEnum.java

package com.soecode.lyf.enums;

/**

* 使用枚舉表述常量數(shù)據(jù)字典

*/

public enum AppointStateEnum {

SUCCESS(1, "預(yù)約成功"), NO_NUMBER(0, "庫存不足"), REPEAT_APPOINT(-1, "重復(fù)預(yù)約"), INNER_ERROR(-2, "系統(tǒng)異常");

private int state;

private String stateInfo;

private AppointStateEnum(int state, String stateInfo) {

this.state = state;

this.stateInfo = stateInfo;

}

public int getState() {

return state;

}

public String getStateInfo() {

return stateInfo;

}

public static AppointStateEnum stateOf(int index) {

for (AppointStateEnum state : values()) {

if (state.getState() == index) {

return state;

}

}

return null;

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

接下來隅俘,在dto包下新建AppointExecution.java用來存儲我們執(zhí)行預(yù)約操作的返回結(jié)果邻奠。

AppointExecution.java

package com.soecode.lyf.dto;

import com.soecode.lyf.entity.Appointment;

import com.soecode.lyf.enums.AppointStateEnum;

/**

* 封裝預(yù)約執(zhí)行后結(jié)果

*/

public class AppointExecution {

// 圖書ID

private long bookId;

// 秒殺預(yù)約結(jié)果狀態(tài)

private int state;

// 狀態(tài)標(biāo)識

private String stateInfo;

// 預(yù)約成功對象

private Appointment appointment;

public AppointExecution() {

}

// 預(yù)約失敗的構(gòu)造器

public AppointExecution(long bookId, AppointStateEnum stateEnum) {

this.bookId = bookId;

this.state = stateEnum.getState();

this.stateInfo = stateEnum.getStateInfo();

}

// 預(yù)約成功的構(gòu)造器

public AppointExecution(long bookId, AppointStateEnum stateEnum, Appointment appointment) {

this.bookId = bookId;

this.state = stateEnum.getState();

this.stateInfo = stateEnum.getStateInfo();

this.appointment = appointment;

}

// 省略getter和setter方法,toString方法

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

接著为居,在exception包下新建三個文件

NoNumberException.java

RepeatAppointException.java

AppointException.java

預(yù)約業(yè)務(wù)異常類(都需要繼承RuntimeException)碌宴,分別是無庫存異常、重復(fù)預(yù)約異常蒙畴、預(yù)約未知錯誤異常贰镣,用于業(yè)務(wù)層非成功情況下的返回(即成功返回結(jié)果,失敗拋出異常)膳凝。

NoNumberException.java

package com.soecode.lyf.exception;

/**

* 庫存不足異常

*/

public class NoNumberException extends RuntimeException {

public NoNumberException(String message) {

super(message);

}

public NoNumberException(String message, Throwable cause) {

super(message, cause);

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

RepeatAppointException.java

package com.soecode.lyf.exception;

/**

* 重復(fù)預(yù)約異常

*/

public class RepeatAppointException extends RuntimeException {

public RepeatAppointException(String message) {

super(message);

}

public RepeatAppointException(String message, Throwable cause) {

super(message, cause);

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

AppointException.java

package com.soecode.lyf.exception;

/**

* 預(yù)約業(yè)務(wù)異常

*/

public class AppointException extends RuntimeException {

public AppointException(String message) {

super(message);

}

public AppointException(String message, Throwable cause) {

super(message, cause);

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

咱們終于可以編寫業(yè)務(wù)代碼了碑隆,在service包下新建BookService.java圖書業(yè)務(wù)接口。

BookService.java

package com.soecode.lyf.service;

import java.util.List;

import com.soecode.lyf.dto.AppointExecution;

import com.soecode.lyf.entity.Book;

/**

* 業(yè)務(wù)接口:站在"使用者"角度設(shè)計接口 三個方面:方法定義粒度蹬音,參數(shù)上煤,返回類型(return 類型/異常)

*/

public interface BookService {

/**

* 查詢一本圖書

*

* @param bookId

* @return

*/

Book getById(long bookId);

/**

* 查詢所有圖書

*

* @return

*/

List<Book> getList();

/**

* 預(yù)約圖書

*

* @param bookId

* @param studentId

* @return

*/

AppointExecution appoint(long bookId, long studentId);

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

在service.impl包下新建BookServiceImpl.java使用BookService接口,并實現(xiàn)里面的方法著淆。

BookServiceImpl

package com.soecode.lyf.service.impl;

import java.util.List;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

import org.springframework.transaction.annotation.Transactional;

import com.soecode.lyf.dao.AppointmentDao;

import com.soecode.lyf.dao.BookDao;

import com.soecode.lyf.dto.AppointExecution;

import com.soecode.lyf.entity.Appointment;

import com.soecode.lyf.entity.Book;

import com.soecode.lyf.enums.AppointStateEnum;

import com.soecode.lyf.exception.AppointException;

import com.soecode.lyf.exception.NoNumberException;

import com.soecode.lyf.exception.RepeatAppointException;

import com.soecode.lyf.service.BookService;

@Service

public class BookServiceImpl implements BookService {

private Logger logger = LoggerFactory.getLogger(this.getClass());

// 注入Service依賴

@Autowired

private BookDao bookDao;

@Autowired

private AppointmentDao appointmentDao;

@Override

public Book getById(long bookId) {

return bookDao.queryById(bookId);

}

@Override

public List<Book> getList() {

return bookDao.queryAll(0, 1000);

}

@Override

@Transactional

/**

* 使用注解控制事務(wù)方法的優(yōu)點(diǎn): 1.開發(fā)團(tuán)隊達(dá)成一致約定楼入,明確標(biāo)注事務(wù)方法的編程風(fēng)格

* 2.保證事務(wù)方法的執(zhí)行時間盡可能短,不要穿插其他網(wǎng)絡(luò)操作牧抽,RPC/HTTP請求或者剝離到事務(wù)方法外部

* 3.不是所有的方法都需要事務(wù),如只有一條修改操作遥赚,只讀操作不需要事務(wù)控制

*/

public AppointExecution appoint(long bookId, long studentId) {

try {

// 減庫存

int update = bookDao.reduceNumber(bookId);

if (update <= 0) {// 庫存不足

//return new AppointExecution(bookId, AppointStateEnum.NO_NUMBER);//錯誤寫法

throw new NoNumberException("no number");

} else {

// 執(zhí)行預(yù)約操作

int insert = appointmentDao.insertAppointment(bookId, studentId);

if (insert <= 0) {// 重復(fù)預(yù)約

//return new AppointExecution(bookId, AppointStateEnum.REPEAT_APPOINT);//錯誤寫法

throw new RepeatAppointException("repeat appoint");

} else {// 預(yù)約成功

Appointment appointment = appointmentDao.queryByKeyWithBook(bookId, studentId);

return new AppointExecution(bookId, AppointStateEnum.SUCCESS, appointment);

}

}

// 要先于catch Exception異常前先catch住再拋出扬舒,不然自定義的異常也會被轉(zhuǎn)換為AppointException,導(dǎo)致控制層無法具體識別是哪個異常

} catch (NoNumberException e1) {

throw e1;

} catch (RepeatAppointException e2) {

throw e2;

} catch (Exception e) {

logger.error(e.getMessage(), e);

// 所有編譯期異常轉(zhuǎn)換為運(yùn)行期異常

//return new AppointExecution(bookId, AppointStateEnum.INNER_ERROR);//錯誤寫法

throw new AppointException("appoint inner error:" + e.getMessage());

}

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

下面我們來測試一下我們的業(yè)務(wù)代碼吧~因為查詢圖書的業(yè)務(wù)不復(fù)雜凫佛,所以這里只演示我們最重要的預(yù)約圖書業(yè)務(wù)=部病!

BookServiceImplTest.java

package com.soecode.lyf.service.impl;

import static org.junit.Assert.fail;

import org.junit.Test;

import org.springframework.beans.factory.annotation.Autowired;

import com.soecode.lyf.BaseTest;

import com.soecode.lyf.dto.AppointExecution;

import com.soecode.lyf.service.BookService;

public class BookServiceImplTest extends BaseTest {

@Autowired

private BookService bookService;

@Test

public void testAppoint() throws Exception {

long bookId = 1001;

long studentId = 12345678910L;

AppointExecution execution = bookService.appoint(bookId, studentId);

System.out.println(execution);

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

BookServiceImplTest測試結(jié)果

testAppoint

首次執(zhí)行是“預(yù)約成功”愧薛,如果再次執(zhí)行的話晨炕,應(yīng)該會出現(xiàn)“重復(fù)預(yù)約”,哈哈毫炉,我們所有的后臺代碼都通過單元測試?yán)瞺~是不是很開心~

咱們還需要在dto包里新建一個封裝json返回結(jié)果的類Result.java瓮栗,設(shè)計成泛型。

Result.java

package com.soecode.lyf.dto;

/**

* 封裝json對象,所有返回結(jié)果都使用它

*/

public class Result<T> {

private boolean success;// 是否成功標(biāo)志

private T data;// 成功時返回的數(shù)據(jù)

private String error;// 錯誤信息

public Result() {

}

// 成功時的構(gòu)造器

public Result(boolean success, T data) {

this.success = success;

this.data = data;

}

// 錯誤時的構(gòu)造器

public Result(boolean success, String error) {

this.success = success;

this.error = error;

}

// 省略getter和setter方法

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

最后费奸,我們寫web層弥激,也就是controller,我們在web包下新建BookController.java文件愿阐。

BookController.java

package com.soecode.lyf.web;

import java.util.List;

import org.apache.ibatis.annotations.Param;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Controller;

import org.springframework.ui.Model;

import org.springframework.web.bind.annotation.PathVariable;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestMethod;

import org.springframework.web.bind.annotation.ResponseBody;

import com.soecode.lyf.dto.AppointExecution;

import com.soecode.lyf.dto.Result;

import com.soecode.lyf.entity.Book;

import com.soecode.lyf.enums.AppointStateEnum;

import com.soecode.lyf.exception.NoNumberException;

import com.soecode.lyf.exception.RepeatAppointException;

import com.soecode.lyf.service.BookService;

@Controller

@RequestMapping("/book") // url:/模塊/資源/{id}/細(xì)分 /seckill/list

public class BookController {

private Logger logger = LoggerFactory.getLogger(this.getClass());

@Autowired

private BookService bookService;

@RequestMapping(value = "/list", method = RequestMethod.GET)

private String list(Model model) {

List<Book> list = bookService.getList();

model.addAttribute("list", list);

// list.jsp + model = ModelAndView

return "list";// WEB-INF/jsp/"list".jsp

}

@RequestMapping(value = "/{bookId}/detail", method = RequestMethod.GET)

private String detail(@PathVariable("bookId") Long bookId, Model model) {

if (bookId == null) {

return "redirect:/book/list";

}

Book book = bookService.getById(bookId);

if (book == null) {

return "forward:/book/list";

}

model.addAttribute("book", book);

return "detail";

}

//ajax json

@RequestMapping(value = "/{bookId}/appoint", method = RequestMethod.POST, produces = {

"application/json; charset=utf-8" })

@ResponseBody

private Result<AppointExecution> appoint(@PathVariable("bookId") Long bookId, @RequestParam("studentId") Long studentId) {

if (studentId == null || studentId.equals("")) {

return new Result<>(false, "學(xué)號不能為空");

}

//AppointExecution execution = bookService.appoint(bookId, studentId);//錯誤寫法微服,不能統(tǒng)一返回,要處理異常(失斢Ю)情況

AppointExecution execution = null;

try {

execution = bookService.appoint(bookId, studentId);

} catch (NoNumberException e1) {

execution = new AppointExecution(bookId, AppointStateEnum.NO_NUMBER);

} catch (RepeatAppointException e2) {

execution = new AppointExecution(bookId, AppointStateEnum.REPEAT_APPOINT);

} catch (Exception e) {

execution = new AppointExecution(bookId, AppointStateEnum.INNER_ERROR);

}

return new Result<AppointExecution>(true, execution);

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

因為我比較懶以蕴,所以我們就不測試controller了,好討厭寫前端,嗚嗚嗚~

到此辛孵,我們的SSM框架整合配置丛肮,與應(yīng)用實例部分已經(jīng)結(jié)束了,我把所有源碼和jar包一起打包放在了我的GitHub上觉吭,需要的可以去下載腾供,喜歡就給個star吧,這篇東西寫了兩個晚上也不容易啊鲜滩。

完整代碼下載地址:https://github.com/liyifeng1994/ssm

2017-02-28更新(感謝網(wǎng)友EchoXml發(fā)現(xiàn)):

修改預(yù)約業(yè)務(wù)代碼伴鳖,失敗時拋異常,成功時才返回結(jié)果徙硅,控制層根據(jù)捕獲的異常返回相應(yīng)信息給客戶端榜聂,而不是業(yè)務(wù)層直接返回錯誤結(jié)果。上面的代碼已經(jīng)作了修改嗓蘑,而且錯誤示范也注釋保留著须肆,之前誤人子弟了,還好有位網(wǎng)友前幾天提出質(zhì)疑桩皿,我也及時做了修改豌汇。

2017-03-30更新(感謝網(wǎng)友ergeerge1建議):

修改BookController幾處錯誤

1.detail方法不是返回json的,故不用加@ResponseBody注解

2.appoint方法應(yīng)該加上@ResponseBody注解

3.另外studentId參數(shù)注解應(yīng)該是@RequestParam

4.至于controller測試泄隔,測試appoint方法可不必寫jsp拒贱,用curl就行,比如

curl -H “Accept: application/json; charset=utf-8” -d “studentId=1234567890” localhost:8080/book/1003/appoint

————————————————

版權(quán)聲明:本文為CSDN博主「李奕鋒」的原創(chuàng)文章佛嬉,遵循CC 4.0 BY-SA版權(quán)協(xié)議逻澳,轉(zhuǎn)載請附上原文出處鏈接及本聲明。

原文鏈接:https://blog.csdn.net/qq598535550/java/article/details/51703190

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末暖呕,一起剝皮案震驚了整個濱河市斜做,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌湾揽,老刑警劉巖瓤逼,帶你破解...
    沈念sama閱讀 218,451評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件笼吟,死亡現(xiàn)場離奇詭異,居然都是意外死亡抛姑,警方通過查閱死者的電腦和手機(jī)赞厕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來定硝,“玉大人皿桑,你說我怎么就攤上這事∈叻龋” “怎么了诲侮?”我有些...
    開封第一講書人閱讀 164,782評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長箱蟆。 經(jīng)常有香客問我沟绪,道長,這世上最難降的妖魔是什么空猜? 我笑而不...
    開封第一講書人閱讀 58,709評論 1 294
  • 正文 為了忘掉前任绽慈,我火速辦了婚禮,結(jié)果婚禮上辈毯,老公的妹妹穿的比我還像新娘坝疼。我一直安慰自己,他們只是感情好谆沃,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,733評論 6 392
  • 文/花漫 我一把揭開白布钝凶。 她就那樣靜靜地躺著,像睡著了一般唁影。 火紅的嫁衣襯著肌膚如雪耕陷。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,578評論 1 305
  • 那天据沈,我揣著相機(jī)與錄音哟沫,去河邊找鬼。 笑死锌介,一個胖子當(dāng)著我的面吹牛嗜诀,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播掏湾,決...
    沈念sama閱讀 40,320評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼肿嘲!你這毒婦竟也來了融击?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,241評論 0 276
  • 序言:老撾萬榮一對情侶失蹤雳窟,失蹤者是張志新(化名)和其女友劉穎尊浪,沒想到半個月后匣屡,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,686評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡拇涤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,878評論 3 336
  • 正文 我和宋清朗相戀三年捣作,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鹅士。...
    茶點(diǎn)故事閱讀 39,992評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡券躁,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出掉盅,到底是詐尸還是另有隱情也拜,我是刑警寧澤,帶...
    沈念sama閱讀 35,715評論 5 346
  • 正文 年R本政府宣布趾痘,位于F島的核電站慢哈,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏永票。R本人自食惡果不足惜卵贱,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,336評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望侣集。 院中可真熱鬧键俱,春花似錦、人聲如沸肚吏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,912評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽罚攀。三九已至党觅,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間斋泄,已是汗流浹背杯瞻。 一陣腳步聲響...
    開封第一講書人閱讀 33,040評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留炫掐,地道東北人魁莉。 一個月前我還...
    沈念sama閱讀 48,173評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像募胃,于是被迫代替她去往敵國和親旗唁。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,947評論 2 355