# 案例分析

在开始项目前,我们先来了解下项目框架的整体构思以及依赖关系,在《目录结构》这一章节中我们详细的展示和介绍了框架结构及相关配置信息,框架最新核心的模块是javaweb-common,其他几个模板底层依赖都是他,可以这样理解他是整个框架的地基,没有他其他模块将毫无意义,下面我们会对框架进行一个整体的分析。

# 自定义注解

在SpringBoot项目开发中,我们会发现注解是常用的工具,比如常见的注解@Controller@RequestMapping@Autowired@ResponseBody@PostMapping等等,目前我们所使用的大部分注解都是SpringBoot框架所提供的,实际使用中发现注解确实是一个非常实用的东西,不能帮我我们实现了原先需要些大量代码才能完成的功能,而且让我们少写了很多代码的同时让我们的程序变得简洁、易读,根据项目的实际业务场景我们框架中也自定义了部分注解来帮我们完成相应的功能:

  • @Excel: 导出Excel注解,在很多模块中我们都需要对外导出Excel的功能,那么如何才能方便简洁的导出Excel呢?@Excel帮我们实现了目标,使用也非常简单,只需要在需要导出数据的类字段上添加这个注解即可,举例说明,如:@Excel(name = "添加时间"),@Excel(name = "角色状态", readConverterExp = "1=正常,2=停用")等等,代码非常简洁,添加完注解后,导出Excel时只需调用框架中封装好的ExcelUtil工具类即可,此工具类是动态解析上述注解信息,具体使用可查看项目,直接看使用案例。
  • @Log:日志注解,项目研发中或多或少都需要实现诸如:登录日志操作日志的功能,那么我们是否有简便的方法去实现呢?JAVA其实已经帮我考虑到了,基于AOP的思想去实现日志记录的功能,框架中@Log就是基于这一思想,采取线程异步的方式来实现了系统日志功能,注解使用也非常简单,只需要在需要记录操作日志的方法上加上这一注解即可,如:@Log(title = "职级", businessType = BusinessType.INSERT)@Log(title = "职级", businessType = BusinessType.UPDATE)一行代码轻松搞定,无需堆砌大量代码去实现,详细的使用场景请直接看框架源码,简单易读。

# 框架基类

为了简化模块的开发,我们对项目中每一层级文件都封装了一个基类文件,在新建文件时只需要集成基类即可帮你完成一些基础的功能,避免了抒写大量的重复性代码,我们封装基类的初衷就是简化开发,提高开发效率,把开发人员从复杂重复率高的代码中解放出来,只需要将精心放在业务的处理上,下面我们详细的介绍每个基类的内容及其用途:

  • 控制器基类(BaseController.java):控制器基类目前封装的比较简单,抽离了模块中共性的一些方法,如列表页显示、编辑页显示、删除方法等等,内容如下:
package com.javaweb.common.common;

import com.javaweb.common.utils.JsonResult;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

/**
 * 基类控制器
 */
public class BaseController {

    /**
     * 构造函数
     */
    public BaseController() {

    }

    /**
     * 列表页
     *
     * @return
     */
    @GetMapping("/index")
    public String index(Model model) {
        return this.render();
    }

    /**
     * 获取记录详情
     *
     * @param id    记录ID
     * @param model 模型
     * @return
     */
    @GetMapping("/edit")
    public String edit(Integer id, Model model) {
        return this.render();
    }

    /**
     * 删除记录
     *
     * @param id 记录ID
     * @return
     */
    @ResponseBody
    @GetMapping("/delete")
    public JsonResult delete(Integer id) {
        return null;
    }


    /**
     * 渲染模板
     *
     * @return
     */
    public String render() {
        return this.render("");
    }

    /**
     * 渲染模板
     *
     * @param tpl 模板路径
     * @return
     */
    public String render(String tpl) {
        if (StringUtils.isEmpty(tpl)) {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            String url = request.getRequestURI();
            return url.substring(1);
        } else {
            return tpl;
        }
    }

}

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

备注:其中的render方法是模板路径参数的动态解析方法,会分析当前网络请求地址,东西解析出当前模块对应的模板文件是在哪个目录,具体路径是什么。

  • 实体基类(BaseEntity.java) 实体基类对实体类中共性的字段信息做了抽离,避免每个实体类中都会出现重复性的字段,如:记录ID创建人(createUser)创建时间(createTime)更新人(updateUser)更新时间(updateTime)有效标识(mark)等等,内容如下:
package com.javaweb.common.common;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;

import java.io.Serializable;
import java.util.Date;

/**
 * 基类实体对象
 *
 * @author 牧羊人
 * @date 2019/11/28
 */
@Data
public class BaseEntity implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 主键ID
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;

    /**
     * 添加人
     */
    private Integer createUser;

    /**
     * 创建时间
     */
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone="GMT+8")
    private Date createTime;

    /**
     * 更新人
     */
    private Integer updateUser;

    /**
     * 更新时间
     */
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone="GMT+8")
    private Date updateTime;

    /**
     * 有效标识
     */
    private Integer mark;

}

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

备注:子类实体类只需要集成当前BaseEntity即可。

  • 列表查询基类(BaseQuery.java) 在数据列表中,必不可少的会对数据进行分页和条件筛选,其他筛选条件都是动态的,但是对于列表而言分页确实大家共性的东西,因此我们特地封装了查询基类,子类只需集成即可,内容如下:
package com.javaweb.common.common;

import lombok.Data;

/**
 * 查询对象基类
 */
@Data
public class BaseQuery {
    /**
     * 页码(默认1)
     */
    private Integer page = 1;

    /**
     * 每页数(默认:20)
     */
    private Integer limit = 20;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
  • 接口基类(IBaseService.java) 在MybatisPlus中框架已经提供了接口IService,根据框架设计要求,我们需要抽离出一些公共的API接口对外暴露调用,IService接口层提供了封装的API接口,但是对于我们框架来说肯定想做增强,同时又省的我们在每个业务模块中去写重复性的增删改查等一些重复性的方法,现在得在IService模块接口等中间写一个基类做桥接,因此IBaseService接口基类显得必不可少,直接上代码,内容如下:
package com.javaweb.common.common;

import com.baomidou.mybatisplus.extension.service.IService;
import com.javaweb.common.utils.JsonResult;

import java.io.Serializable;
import java.util.List;
import java.util.Map;


public interface IBaseService<T> extends IService<T> {

    /**
     * 根据查询条件获取数据列表
     *
     * @param query 查询条件
     * @return
     */
    JsonResult getList(BaseQuery query);

    /**
     * 根据ID获取记录信息
     *
     * @param id 记录ID
     * @return
     */
    Map<String, Object> info(Integer id);

    /**
     * 根据ID获取记录信息
     *
     * @param id 记录ID
     * @return
     */

    Object getInfo(Serializable id);

    /**
     * 根据实体对象添加记录
     *
     * @param entity 实体对象
     * @return
     */
    JsonResult add(T entity);

    /**
     * 根据实体对象更新记录
     *
     * @param entity 实体对象
     * @return
     */
    JsonResult update(T entity);

    /**
     * 根据实体对象添加、编辑记录
     *
     * @param entity 实体对象
     * @return
     */
    JsonResult edit(T entity);

    /**
     * 删除记录
     *
     * @param entity 实体对象
     * @return
     */
    JsonResult delete(T entity);

    /**
     * 根据ID删除记录
     *
     * @param id 记录ID
     * @return
     */
    JsonResult deleteById(Integer id);

    /**
     * 根据ID删除记录
     *
     * @param ids 记录ID
     * @return
     */
    JsonResult deleteByIds(String ids);

    /**
     * 设置状态
     *
     * @param entity 实体对象
     * @return
     */
    JsonResult setStatus(T entity);

    /**
     * 导出Excel
     *
     * @return
     */
    List<T> exportExcel();

}

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
  • 接口实现基类(BaseServiceImpl.java) 上面我们详细的介绍了,同理我们也需要写一个匹配IBaseService接口的实现基类BaseServiceImpl,在接口实现基类中我们将接口层的方法一一的做了重写和实现,已达到我们所期待的业务和数据处理,内容如下:
package com.javaweb.shiro.common;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.javaweb.common.common.BaseEntity;
import com.javaweb.common.common.BaseQuery;
import com.javaweb.common.common.IBaseService;
import com.javaweb.common.utils.DateUtils;
import com.javaweb.common.utils.JsonResult;
import com.javaweb.shiro.utils.ShiroUtils;
import org.springframework.util.StringUtils;

import java.io.Serializable;
import java.util.List;
import java.util.Map;

public class BaseServiceImpl<M extends BaseMapper<T>, T extends BaseEntity> extends ServiceImpl<M, T> implements IBaseService<T> {

    /**
     * 根据查询条件获取数据列表
     *
     * @param query 查询条件
     * @return
     */
    @Override
    public JsonResult getList(BaseQuery query) {
        return null;
    }

    /**
     * 根据ID获取记录信息
     *
     * @param id 记录ID
     * @return
     */
    @Override
    public Map<String, Object> info(Integer id) {
        Object entity = this.getInfo(id);
        Map<String, Object> map = JSON.parseObject(JSON.toJSONString(entity), new TypeReference<Map<String, Object>>() {
        });
        return map;
    }

    /**
     * 根据ID获取记录信息
     *
     * @param id 记录ID
     * @return
     */
    @Override
    public Object getInfo(Serializable id) {
        T entity = this.getById(id);
        return entity;
    }

    /**
     * 传入实体对象添加记录
     *
     * @param entity 实体对象
     * @return
     */
    @Override
    public JsonResult add(T entity) {
        entity.setCreateUser(ShiroUtils.getAdminId());
        entity.setCreateTime(DateUtils.now());
        entity.setMark(1);
        boolean result = this.save(entity);
        if (!result) {
            return JsonResult.error();
        }
        return JsonResult.success();
    }

    /**
     * 传入实体对象更新记录
     *
     * @param entity 实体对象
     * @return
     */
    @Override
    public JsonResult update(T entity) {
        entity.setUpdateUser(ShiroUtils.getAdminId());
        entity.setUpdateTime(DateUtils.now());
        boolean result = this.updateById(entity);
        if (!result) {
            return JsonResult.error();
        }
        return JsonResult.success();
    }

    /**
     * 根据实体对象添加、编辑记录
     *
     * @param entity 实体对象
     * @return
     */
    @Override
    public JsonResult edit(T entity) {
        if (entity == null) {
            return JsonResult.error("实体对象不存在");
        }
        if (entity.getId() != null && entity.getId() > 0) {
            // 修改记录
            return this.update(entity);
        } else {
            // 新增记录
            return this.add(entity);
        }
    }

    /**
     * 删除记录
     *
     * @param entity 实体对象
     * @return
     */
    @Override
    public JsonResult delete(T entity) {
        entity.setUpdateUser(ShiroUtils.getAdminId());
        entity.setUpdateTime(DateUtils.now());
        entity.setMark(0);
        boolean result = this.updateById(entity);
        if (!result) {
            return JsonResult.error();
        }
        return JsonResult.success("删除成功");
    }

    /**
     * 根据ID删除记录
     *
     * @param id 记录ID
     * @return
     */
    @Override
    public JsonResult deleteById(Integer id) {
        if (StringUtils.isEmpty(id)) {
            return JsonResult.error("记录ID不能为空");
        }
        boolean result = this.removeById(id);
        if (!result) {
            return JsonResult.error();
        }
        return JsonResult.success("删除成功");
    }

    /**
     * 根据ID删除记录
     *
     * @param ids 记录ID
     * @return
     */
    @Override
    public JsonResult deleteByIds(String ids) {
        if (StringUtils.isEmpty(ids)) {
            return JsonResult.error("记录ID不能为空");
        }
        String[] item = ids.split(",");
        Integer totalNum = 0;
        for (String id : item) {
            boolean result = this.removeById(id);
            if (result) {
                totalNum++;
            }
        }
        return JsonResult.error(String.format("本次共删除【%s】条记录", totalNum));
    }

    /**
     * 设置状态
     *
     * @param entity 实体对象
     * @return
     */
    @Override
    public JsonResult setStatus(T entity) {
        return this.update(entity);
    }

    /**
     * 导出Excel
     *
     * @return
     */
    @Override
    public List<T> exportExcel() {
        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
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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192

# 配置文件

在系统研发中,参数配置是必不可少的,框架中根据现实需要提供了一些基础的配置文件,如下:

  • AliSmsConfig:阿里短信配置文件,参数说明如下:
  1. accessKeyId:KEY值;
  2. accessKeySecret:秘钥;
  3. regionId:区域ID;
  4. signName:短信签名;
  5. templateCode:短信模板ID;

备注:读取全局配置文件application-dev.yml中的内容;

  • DbConfig:数据库连接池配置,参数说明如下:
  1. driver:数据库连接驱动;
  2. url:数据库连接地址;
  3. username:数据库连接登录名;
  4. password:数据库连接登录密码;

备注:读取全局配置文件application-dev.yml中的内容;

  • DruidConfig:Druid连接池配置,请看源码相关内容;
  • I18nConfig:国际化配置,可根据实际需要设置多语言版本;
  • MybatisPlusConfig: MybatisPlus配置,如分页;
  • RedisConfig:Redis缓存配置;
  • SystemConfig:系统常规参数配置,此配置文件的作用是读取全局配置参数以便全局使用;
  • ThreadPoolConfig:线程池配置,前面所有的系统日志,基于AOP的注解就是使用线程池处理的,可以结合起来看;
  • UploadFileConfig:上传文件配置,读取文件上传配置,包括:上传目录访问路径上传服务器的映射文件夹等等;
  • WebMvcConfig:Web配置;

# 工具类

javaweb-common模块中引入了程序开发中常用的起始依赖,封装了基类文件及大量的工具累对外提供API接口,工具类如:阿里短信(AliSmsUtils)常用函数(CommonUtils)Excel导出(ExcelUtil)多媒体文件(FfmpegUtil)消息推送(JPushUtils)JWT实现类(JwtUtil)邮件发送(MailUtils)Redis缓存(RedisUtils)Servlet工具类(ServletUtils)文件上传(UploadUtils)压缩处理类(ZipUtils)等等一系列常用的工具类,有关工具类详细的使用方法,看下源码即可明白,封装的源码都非常的简单易懂,只要有点JAVA基础的人都可以完美的读懂。