idea整合restful風(fēng)格的ssm框架(二)

1. 前言

在這里的就不多啰嗦什么了,先來大概介紹一下項(xiàng)目的體系結(jié)構(gòu)等铺浇。還是上張圖

項(xiàng)目結(jié)構(gòu).png

整體架構(gòu)是ssm的前面已經(jīng)說過了却舀,下面來說一下代碼的架構(gòu)。因?yàn)槟壳绊?xiàng)目的規(guī)劃還比較小黑低,所以在com.bgy.ssm包下面就直接建了controller赘艳、service等。如果項(xiàng)目情況較復(fù)雜的話克握,在此包應(yīng)該根據(jù)功能模塊來進(jìn)行分包蕾管,然后再建立相應(yīng)的三層,這樣的話結(jié)構(gòu)更清晰菩暗。

在這里有個爭論:關(guān)于到底要不要repo層的問題

  • 有人贊成的是直接controller中接收用戶請求掰曾,調(diào)用service處理業(yè)務(wù)邏輯,然后所有的組裝數(shù)據(jù)也是在service中完成停团,最后service直接調(diào)用dao接口旷坦。
  • 另外有人贊成的是service中只處理業(yè)務(wù)邏輯;對于數(shù)據(jù)的組裝佑稠,如操作多表時要調(diào)用dao層的多個接口時秒梅,應(yīng)該在repo層中完成,這樣不會使service層顯得太過臃腫舌胶,并且能提高可復(fù)用性和符合設(shè)計(jì)原則中 “針對接口編程捆蜀,不針對實(shí)現(xiàn)編程”的理念四康。
  • 在這里我個人的話更傾向于后者踱阿,service層更注重的應(yīng)該是處理業(yè)務(wù)邏輯,不能因?yàn)閷?shí)現(xiàn)某個功能而在service中一個方法寫一堆組裝數(shù)據(jù)的代碼绸吸。當(dāng)然這是我個人的愚見婉烟,若有高見娩井,歡迎指正。

在這里這個項(xiàng)目較小似袁,也是從新手角度出發(fā)洞辣,所以我違心的使用了controller咐刨、service、dao的三層架構(gòu)扬霜。這樣思路較簡單和清晰定鸟。

另外說明一點(diǎn),下面的代碼中都有相關(guān)引用著瓶,使用時自行引入相關(guān)引用

2. 數(shù)據(jù)庫

這里為了演示功能和架構(gòu)联予,我就只使用一張表來實(shí)現(xiàn)操作。在這里材原,我建議在項(xiàng)目下建立sql文件夾沸久,里面放置建表的語句,方便以后查閱和修改余蟹。

sql.png

上面是建表語句文件卷胯、下面是有些表需要進(jìn)行初始化的初始化語句文件。

db_ddl.sql

DROP TABLE IF EXISTS `tUser`;
CREATE TABLE `tUser` (
  `ID` VARCHAR (45) NOT  NULL ,
  `UserName` VARCHAR (100) ,
  `NickName` VARCHAR (100),
  `PassWord` CHAR (32),
  `Email` VARCHAR (50),
  `Phone` VARCHAR (50),
  `Sex` ENUM('S_MALE','S_FEMALE','S_BM'),
  `Status` ENUM('S_OFF','S_NORMAL'),
  `Avatar` VARCHAR (100),
  `Remarks` VARCHAR (200),
  `AddAt` BIGINT,
  PRIMARY KEY (`ID`)
) DEFAULT CHARSET=utf8;

這里沒有寫備注和null這些的規(guī)范威酒,需要的可自行添加窑睁。
(后續(xù)添加:在這里的表名和字段名在下一篇文章中進(jìn)行了修改,原因可翻看下一篇文章)

3. 實(shí)體類

在這里為了簡便葵孤、我就建表完成后直接建立實(shí)體類User担钮。
User.java

public class User {
    private String id;

    private String userName;

    private String nickName;

    private String password;

    private String email;

    private String phone;

    private String sex;

    private String status;

    private String avatar;

    private String remarks;

    private Long addAt;

    public User(){

    }

    public User genarateID() {
        if (this.id == null) {
            this.id = UUID.randomUUID().toString();
        }
        return this;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getNickName() {
        return nickName;
    }

    public void setNickName(String nickName) {
        this.nickName = nickName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }

    public String getAvatar() {
        return avatar;
    }

    public void setAvatar(String avatar) {
        this.avatar = avatar;
    }

    public String getRemarks() {
        return remarks;
    }

    public void setRemarks(String remarks) {
        this.remarks = remarks;
    }

    public Long getAddAt() {
        return addAt;
    }

    public void setAddAt(Long addAt) {
        this.addAt = addAt;
    }
}

這個地方?jīng)]什么特別的,對于id是直接用genarateID()方法通過uuid生成的尤仍,若需要自己設(shè)計(jì)主鍵生成策略的可忽略這個箫津。

4. Controller

先上代碼

@Controller
@RequestMapping("/user")
public class UserController {
    private JSONObject json = new JSONObject();
    @Autowired
    private UserService userService;

    @RequestMapping(value = "",method = RequestMethod.POST, produces = "text/html;charset=UTF-8")
    @ResponseBody
    public String addUser(@RequestBody String userJson) throws Exception{
        String resultInfo = "";
        try{
            resultInfo = userService.addUser(userJson);
            return resultInfo;
        }catch (Exception e){
            throw new GcsjException(e);
        }
    }

    @RequestMapping(value = "{userName}",method = RequestMethod.GET,produces = "text/html;charset=UTF-8")
    @ResponseBody
    public String getUserByName(@PathVariable String userName) throws Exception{
        String resultInfo = "";
        try {
            resultInfo = userService.getUserByName(userName);
            return resultInfo;
        }catch (Exception e){
            throw new GcsjException(e);
        }
    }

    @RequestMapping(value = "/login", method = RequestMethod.POST, produces = "text/html;charset=UTF-8")
    @ResponseBody
    public String login(@RequestBody String userJson) throws Exception{
        String resultInfo = "";
        try{
            resultInfo = userService.login(userJson);
            return resultInfo;
        }catch (Exception e){
            throw new GcsjException(e);
        }
    }
}

controller這里首先用@Controller標(biāo)記這個類成為一個SpringMVC Controller對象。

因?yàn)槭鞘褂玫氖莚estful風(fēng)格鲤嫡,所以在類前面就通過@RequestMapping("/user")注解指定此類的所有請求方法的父路徑為/user送挑,類中第一個方法是添加用戶绑莺,所以路徑為空,由restful風(fēng)格指定的HTTP請求方式選擇進(jìn)入不同方法惕耕;然后直接以json字符串來交互纺裁。另外一個通過用戶名獲取用戶的方法,把用戶名帶在參數(shù)上司澎,所以value中寫了value = "{userName}"欺缘,然后用@PathVariable String userName接收參數(shù)。這個只是一個測試方法挤安,暫時無用谚殊。第三個方法是登錄,也是以json字符串來交互蛤铜。

另外就是使用@Autowired把service注入進(jìn)來了
在這里所有的方法統(tǒng)一用字符串返回嫩絮。

(另外丛肢,這里前后端以JSON字符串交互的方式有待商榷,以前認(rèn)知是用JSON字符串便于統(tǒng)一風(fēng)格剿干;不過最近一年以來的學(xué)習(xí)和工作蜂怎,現(xiàn)在會直接使用實(shí)體類進(jìn)行接收對象,免去JSON轉(zhuǎn)對象的步驟置尔。)

5. Service

service層主要處理業(yè)務(wù)邏輯杠步,首先使用@Service標(biāo)注這個類是業(yè)務(wù)層組件,然后也是用@Autowired注解注入Dao

@Service
public class UserService {
    @Autowired
    private UserDao userDao;

    private JSONObject json = new JSONObject();

    @Transactional
    public String addUser(String userJson) throws Exception {
        ResponseResult result = new ResponseResult();
        User user = json.parseObject(userJson, User.class);
        if (userDao.countCommon(user) > 0) {
            result.setCode("400");
            result.setMsg("用戶已存在");
            return json.toJSONString(result);
        }
        user.genarateID();//設(shè)置UUID
        int num = userDao.addUser(user);
        int numScore = userDao.addDefaultIntegral(user.getId());//注冊用戶送默認(rèn)積分
        if (num > 0 && numScore >0) {
            result.setCode("200");
            result.setMsg("添加成功");
            result.setData(user);
        } else {
            result.setCode("400");
            result.setMsg("添加失敗");
        }
        return json.toJSONString(result);
    }

    public String getUserByName(String userName) throws Exception {
        ResponseResult result = new ResponseResult();
        List<User> list = userDao.getUserList(userName);
        result.setCode("200");
        result.setMsg("Ok");
        result.setData(list);
        return json.toJSONString(result);
    }

    public String login(String userJson) throws Exception {
        ResponseResult result = new ResponseResult();
        User user = json.parseObject(userJson, User.class);

        if ("".equals(user.getUserName()) || "".equals(user.getPassword())) {
            result.setCode("400");
            result.setMsg("用戶名或密碼不能為空");
            return json.toJSONString(result);
        }
        List<User> list = userDao.getUserList(user.getUserName());
        if (list.size() == 0) {
            result.setCode("400");
            result.setMsg("用戶不存在");
        } else {
            if (!user.getPassword().equals(list.get(0).getPassword())) {
                result.setCode("400");
                result.setMsg("密碼錯誤");
            }else {
                result.setCode("200");
                result.setMsg("登錄成功");
            }
        }
        return json.toJSONString(result);
    }
}

在這里自己定義了一個ResponseResult類來作為所有的返回請求的格式榜轿。

public class ResponseResult implements Serializable {
    private static final long serialVersionUID = 4832771715671880043L;
    private String code;
    private String msg;
    private Object data;

    public ResponseResult(){
        this.code = "200";
        this.msg = "SUCCESS";
        this.data = null;
    }

    public ResponseResult(String msg) {
        this.code = "400";
        this.msg = msg;
        this.data = null;
    }

    public ResponseResult(String code, String msg, Object data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }

    public String getCode() {
        return this.code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getMsg() {
        return this.msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public Object getData() {
        return this.data;
    }

    public void setData(Object data) {
        this.data = data;
    }
}

這樣以統(tǒng)一的code幽歼,msg和data作為返回格式,前端也方便處理谬盐。根據(jù)邏輯設(shè)定好這個類后直接轉(zhuǎn)成json字符串返回即可试躏。

6. Dao

dao這里實(shí)現(xiàn)非常簡單,就是一個接口设褐,沒有另外去寫接口的實(shí)現(xiàn)颠蕴,很多系統(tǒng)是另外寫了實(shí)現(xiàn)類的,可以對數(shù)據(jù)庫查詢的數(shù)據(jù)再次進(jìn)行處理后返回助析。
這里我是通過自定義的MybatisSqlMapping注解犀被,讓有這個注解的interface被Mybatis掃描并生成Mapper對象,然后與sql-mapping文件夾下面的*.xml對應(yīng)外冀。

@Repository
@MybatisSqlMapping
public interface UserDao {

    public int countCommon(User user);

    public int addUser(User user);

    public int addDefaultIntegral(@Param("userId") String userId);

    public List<User> getUserList(@Param("userName") String userName);

}

首先還是使用@Repository標(biāo)注這個類為數(shù)據(jù)訪問組件寡键。
然后添加了自定義的@MybatisSqlMapping注解,使這個類能被掃描雪隧。

/**
 * 定義一個空注解西轩,用于MapperScannerConfigurer過濾接口
 * 有此注解的interface才會被Mybatis掃描并生成Mapper對象
 *
 */
public @interface MybatisSqlMapping {
}

7. *.xml

user.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.btt.gcsj.dao.UserDao">
    <resultMap id="userMap" type="com.btt.gcsj.model.User">
        <id column="ID" property="id"/>
        <result column="UserName" property="userName"/>
        <result column="NickName" property="nickName"/>
        <result column="PassWord" property="password"/>
        <result column="Email" property="email"/>
        <result column="Phone" property="phone"/>
        <result column="Sex" property="sex"/>
        <result column="Status" property="status"/>
        <result column="Avatar" property="avatar"/>
        <result column="Remarks" property="remarks"/>
        <result column="AddAt" property="addAt"/>
    </resultMap>

    <select id="countCommon" parameterType="com.btt.gcsj.model.User" resultType="int">
        SELECT COUNT(1) from tUser WHERE UserName = #{userName}
    </select>

    <insert id="addUser" parameterType="com.btt.gcsj.model.User">
        INSERT INTO tUser (ID,UserName,NickName,PassWord,Email,Phone,Sex,Status,Avatar,Remarks,AddAt)
        VALUES (#{id},#{userName},#{nickName},#{password},#{email},#{phone},#{sex},#{status},#{avatar},#{remarks},unix_timestamp(now()))
    </insert>

    <insert id="addDefaultIntegral" parameterType="String">
        INSERT INTO tIntegral (ID,UserID,ResidualIntegral)
        VALUES (uuid(),#{userId},10)
    </insert>

    <select id="getUserList" resultMap="userMap">
        SELECT ID,UserName,NickName,PassWord,Email,Phone,Sex,Status,Avatar,Remarks,AddAt FROM tUser WHERE UserName = #{userName}
    </select>

</mapper>

這是mybatis中sql的映射文件。命名空間namespace即為UserDao接口脑沿。
然后下面sql的id即為UserDao接口中的方法名藕畔,這樣即可實(shí)現(xiàn)mybatis的sql映射。

8. 上面提到的MybatisSqlMapping注解

/**
 * 定義一個空注解庄拇,用于MapperScannerConfigurer過濾接口
 * 有此注解的interface才會被Mybatis掃描并生成Mapper對象
 *
 */
public @interface MybatisSqlMapping {
}

9. 上面提到的ControllerAspect類的切面注服,用于實(shí)現(xiàn)捕獲異常等。

這里面用到了@Aspect注解措近,@Around環(huán)繞通知溶弟,proceed()執(zhí)行方法等,有興趣的可自行去了解AOP的相關(guān)知識瞭郑。

@Aspect
public class ControllerAspect {
    private static final Log log = LogFactory.getLog(ControllerAspect.class);
    private ObjectMapper mapper = new ObjectMapper();

    @Around("execution(public * com.btt.gcsj..controller.*.*(..))")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
        ResponseResult responseResult = new ResponseResult();
        String methodName = joinPoint.getSignature().getName();
        log.debug(String.format("%s start",methodName));
        String result;
        try{
            result = joinPoint.proceed().toString();
        }catch (Throwable e) {
            String errorMessage = String.format("%s error. %s", methodName, e.getMessage());
            log.error(errorMessage, e);
            responseResult.setCode("400");
            responseResult.setMsg(e.getMessage());
            result = JSON.toJSONString(responseResult);
        }
        log.debug(format("result: %s", result));
        log.debug(String.format("%s finish", methodName));
        return result;
    }
}

整個框架的基礎(chǔ)代碼已經(jīng)寫完辜御,并且實(shí)現(xiàn)了添加用戶,根據(jù)用戶名獲取用戶屈张、和用戶登錄的三個簡單功能的接口擒权。當(dāng)然里面的很多邏輯和細(xì)節(jié)沒有處理苇本,這里只是作為一個演示。

跑一下看看菜拓。

添加用戶

addUser.png
db1.png

根據(jù)用戶名獲取用戶

getUser.png

登錄

login1.png
login2.png

到此瓣窄,整個框架的搭建和一些基礎(chǔ)代碼的實(shí)現(xiàn)和測試都已完成,當(dāng)然其中還有很多很多細(xì)節(jié)問題纳鼎,也沒有去處理這些細(xì)節(jié)問題俺夕。不過作為一個框架的搭建學(xué)習(xí)和對SSM的初步認(rèn)識還是足夠了,畢竟是從新手的角度出發(fā)贱鄙。

文章大概講述了idea的配置劝贸,idea中項(xiàng)目的建立,框架的搭建逗宁,和功能代碼的編寫映九。各個步驟都還是比較詳細(xì),認(rèn)真一邊閱讀和一邊寫代碼的同學(xué)應(yīng)該是可以運(yùn)行的瞎颗。

編者水平有限件甥,若有錯誤或者更優(yōu)的建議歡迎指出。

目前全部文章列表:
idea整合restful風(fēng)格的ssm框架(一)
idea整合restful風(fēng)格的ssm框架(二)
idea整合spring boot+spring mvc+mybatis框架
idea整合springboot+redis
JVM學(xué)習(xí)之—Java內(nèi)存區(qū)域
JVM學(xué)習(xí)之—垃圾回收與內(nèi)存分配策略
專題整理之—不可變對象與String的不可變
專題整理之—String的字符串常量池

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末哼拔,一起剝皮案震驚了整個濱河市引有,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌倦逐,老刑警劉巖譬正,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異檬姥,居然都是意外死亡曾我,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進(jìn)店門健民,熙熙樓的掌柜王于貴愁眉苦臉地迎上來抒巢,“玉大人,你說我怎么就攤上這事荞雏∨扒兀” “怎么了平酿?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵凤优,是天一觀的道長。 經(jīng)常有香客問我蜈彼,道長筑辨,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任幸逆,我火速辦了婚禮棍辕,結(jié)果婚禮上暮现,老公的妹妹穿的比我還像新娘。我一直安慰自己楚昭,他們只是感情好栖袋,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著抚太,像睡著了一般塘幅。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上尿贫,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天电媳,我揣著相機(jī)與錄音,去河邊找鬼庆亡。 笑死匾乓,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的又谋。 我是一名探鬼主播拼缝,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼彰亥!你這毒婦竟也來了珍促?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤剩愧,失蹤者是張志新(化名)和其女友劉穎猪叙,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體仁卷,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡穴翩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了锦积。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片芒帕。...
    茶點(diǎn)故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖丰介,靈堂內(nèi)的尸體忽然破棺而出背蟆,到底是詐尸還是另有隱情,我是刑警寧澤哮幢,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布带膀,位于F島的核電站,受9級特大地震影響橙垢,放射性物質(zhì)發(fā)生泄漏垛叨。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一柜某、第九天 我趴在偏房一處隱蔽的房頂上張望嗽元。 院中可真熱鬧敛纲,春花似錦、人聲如沸剂癌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽佩谷。三九已至办铡,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間琳要,已是汗流浹背寡具。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留稚补,地道東北人童叠。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像课幕,于是被迫代替她去往敵國和親厦坛。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容