Spring boot项目实战:
开发环境:系统:windows 10、工具:Eclips、版本:JDK1.8、数据库:MySQL。
涉及技术 :Springboot、MySQL、jQuery,json,Ajax。
因为运用到了MySQL所以在start.spring.io创建项目时,增加MySQL和MyBatis依赖,并生成工程文件。
导入工程文件后,先在配置文件application.properties中,添加数据源的配置,以便连接MySQL数据库。
#spring datasource
spring.datasource.url=jdbc:mysql://localhost:3306/tedu_store?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=123456
1.注册用户功能
注册用户时,需要先查询数据库中已注册的用户名,并与用户输入的用户名进行重复判断,若重名弹框提示用户“该用户名已被占用,请重新输入用户名”,至于用户名和密码的格式要求,两次密码之间的值等判断无需在服务器上进行,直接通过前端解决即可。
1.1持久层:设计用户表t_user
CREATE TABLE t_user (
id INT AUTO_INCREMENT COMMENT 'id',
username VARCHAR(20) UNIQUE NOT NULL COMMENT '用户名',
password CHAR(32) NOT NULL COMMENT '密码',
salt CHAR(36) NOT NULL COMMENT '盐值',
gender INT COMMENT '性别,0-女;1-男,2-保密';
phone VARCHAR(20) COMMENT '手机号码',
email VARCHAR(50) COMMENT '电子邮箱',
avatar VARCHAR(100) COMMENT '头像',
is_delete INT DEFAULT 0 COMMENT '是否删除,0-未删除,1-已删除',
created_user VARCHAR(20) COMMENT '创建者',
created_time DATETIME COMMENT '创建时间',
modified_user VARCHAR(20) COMMENT '最后修改者',
modified_time DATETIME COMMENT '最后修改时间',
PRIMARY KEY(id)
) DEFAULT CHARSET=UTF8;
1.2实体类:根据表字段设计用户实体类User.java
考虑到有四项日志数据,是之后每个实体类都拥有的属性,故,设计一个共同的实体父类Base.java。具体代码不赘述。但要记得每个实体类都要实现serializable接口,并且生成序列号。
为了实现DI,每个属性的set方法必不可少,get方法和toString方法为了之后的测试,也可以选择生成和重写。
为了便于维护,所有实体类统一放到主目录下的entity包下。
1.3.持久层接口:UserMapper.java
这是一个接口文件,设计了注册时所需要对数据库进行操作的抽象方法,为了实现自动装配,可以在类前添加注解@Mapper,或者在启动程序前添加自动扫描注解:@MapperScan(“cn.haohao.store.mapper”)
/**
* 插入用户
* @param user 用户信息
* @return 生效的行数,1为成功插入,其他插入失败
*/
@Insert("INSERT INTO t_user (username, password, salt, gender, phone, email,avatar, is_delete, created_user, created_time, modified_user, modified_time ) "
+ "VALUES ( #{username}, #{password},#{salt}, #{gender},#{phone}, #{email}, #{avatar}, #{isDelete}, #{createdUser}, #{createdTime},#{modifiedUser}, #{modifiedTime})")
@Options(useGeneratedKeys=true,keyProperty="id")
Integer add(User user);
/**
* 根据用户名查询用户信息
* @param username
* @return
*/
@Select("SELECT id, username, password,salt, avatar, is_delete isDelete FROM t_user WHERE username=#{username}")
User findByUsername(String username);
因为设计到的sql语句与参数并不复杂,就直接在方法上通过注解来实现对持久层的映射。若后续有复杂的sql语句或复杂的参数,可以考虑通过对应的***Mapper.xml文件来实现映射。接口文件***Mapper.java通过自动装配所以无需另外设置。而用到对应的***Mapper.xml文件时,需要在Springboot配置文件上添加,自动扫描的地址:
#mybatis
mybatis.mapper-locations=classpath:mapper/**/*.xml;
1.4业务层设计:UserService.java
业务层设计的常规步骤:
- 是否抛出新异常,若是,需要创建新的异常,业务层发生的异常统一继承父类ServiceException。所有异常都是RunntionException的子孙类实现序列化并重写父类的方法。
抛出的异常有DuplicationKeyException用户名重复异常,InsertException插入数据异常,还要创建父类异常ServiceException,其继承了RunTimeException。统一实现序列化和重写分类方法。 - 创建业务层接口,并添加抽象方法,抽象方法声明时若有异常需要在方法后用throws抛出。
/**
* 用户注册
* @param user 用户的注册信息
* @return 成功注册的用户数据
* @throws InsertException
* @throws DuplicateKeyException
*/
User reg(User user) throws InsertException,DuplicateKeyException;
- 创建业务层接口的实现类,在类上添加@Service注解。通常业务层接口实现类会有来自对应***Mapper.java的私有方法,主要难点为设计实现业务逻辑的公有方法。
思路:注册时,先执行用户名重复性检查,检查通过后,要对密码进行加密处理。通常的做法是进行MD5摘要算法,首先生成随机的UUID来充当盐值(String salt = UUID.randomUUID().toString()),接着对原密码和盐值进行某种处理。加密过程,要记得把盐值封装到对象内,以便后面登陆时的密码检验。
/**
* 对密码进行摘要算法
* @param password 原密码
* @param salt 盐值
* @return 摘要密码
*/
private String getMd5Password(String password,String salt){
password = salt + password + salt;
for (int i = 0; i < 10; i++) {
password = DigestUtils.md5DigestAsHex(password.getBytes());
}
return password;
}
1.5控制器层的设计:UserController
控制器发给客户端的相应类型需要自己设计,这里用到了泛型和Void空类型。若忘了,请自行查询资料。
/**
4. 服务端发送给客户端的响应结果类型
5. 6. @param <E>服务端向客户端发送的数据类型
*/
public class ResponseResult<E> implements Serializable{
private static final long serialVersionUID = -2455812910986497099L;
//响应状态码
private Integer state;
//响应消息
private String message;
//响应数据
private E data;
//默认无参构造
public ResponseResult(){
super();
}
//操作成功,无响应数据,的构造
public ResponseResult(Integer state){
super();
setState(state);
}
//操作成功,并且需要发送数据的构造方法
public ResponseResult(Integer state, E data){
this(state);
setData(data);
}
//操作失败,直接发送message的构造方法
public ResponseResult(Integer state,String message){
this(state);
setMessage(message);
}
//操作失败发送异常的错误信息的构造方法
public ResponseResult(Integer state,Exception e){
this(state,e.getMessage());
}
//下面是get,set和toString方法,就不写了
控制器层需要捕获处理之前的service抛出或自己抛出的异常所以,可以设计一个父类Controller:BaseController.java来统一处理异常。
处理异常需要在方法前加注解@ExceptionHandler(“需要处理的异常类型.class”)如果需要处理多个异常,只需将双引号里用集合表示多个异常。
public class BaseController {
//操作成功的响应码
public static final Integer SUCCESS = 200;
@ExceptionHandler(ServiceException.class)
@ResponseBody
public ResponseResult<Void> handleException(Exception e){
//对可能发生的异常进行捕捉处理
Integer state = null;
if(e instanceof DuplicateKeyException){
state = 400;
}else if (e instanceof InsertException) {
state = 500;
}
return new ResponseResult<>(state,e);
}
}
控制器层的设计思路:
- 是否有新异常抛出,若有请在BaseController中处理,并设计好状态码等
- 设计处理请求的方法:
1.请求路径:/reg
2.请求方式:get/post;@GettMapping/@PostMapping/@RequestMapping ( "url" )
3.请求参数:User user
4.响应数据:Void:空类型,代表对数据不关心
5.是否拦截:暂未设计,详情见之后
//该注解表明以json格式发送响应
@RestController
public class UserController extends BaseController{
@Autowired
private IUserService service;
@RequestMapping("/reg")
public ResponseResult<Void> reg(User user){
service.reg(user);
return new ResponseResult<>(SUCCESS);
}
}