前言:主要介绍最基础的springboot开发架构
目录
- 1. overall
- 2. 配置文件
- 2.1 YAML
- 2.2 properties
- 2.3 配置文件加载顺序
- 2.4 多配置文件
- 3. 代码包
- 3.1 infrastructure
- 3.1.1 persistence
- 3.2 application
- 3.2.1 dto
- 3.2.2 converter
- 3.2.3 service
- 3.3 api
- 3.3.1 vo
- 3.3.2 req
- 3.3.3 converter
- 3.3.4 controller
- **结语**
1. overall
核心部分主要是src包,main包存放的是后端代码,test是用于测试main包的代码。main包主要包含以下几个部分:
- java:存放后端代码
- resources:存放配置文件
- webapp:存放资源文件,如前端jsp、图片资源等
2. 配置文件
springboot使用固定名字的配置文件:application.properties或application.yml。
2.1 YAML
基本语法
- 使用缩进表示层级关系
- 缩进时不允许使用Tab键,只允许使用空格
- 缩进的空格数目不重要,只要相同层级的元素左侧对齐即可
- 大小写敏感
yml举例:
server:
port: 8080
2.2 properties
properties举例:
spring.application.name=apply
2.3 配置文件加载顺序
- 按目录。位置高的将覆盖位置低的。
- application.properties 的优先级高于 application.yml
- 互补读取:
- 如果优先级高的配置文件中没有某个配置项,则会到优先级低的配置文件中找该配置项,即具有互补功能。
- 文件名相同才会互补
2.4 多配置文件
有时候为了分开produce和test配置,会使用多个配置文件。
这种情况下可以在application.yml文件中指定使用的配置文件:
3. 代码包
代码包的路径为:
其中buildbaseframe为项目名。
后端代码主要包含以下几个部分:
- BuildBaseFrameApplication: 启动类
- utils:工具代码包
- infrastructure
- application
- api
3.1 infrastructure
对应数据访问层,主要包含:
- common包: 底层的一些公共配置
- 模块包: 如用户模块
本节主要对各个模块包进行说明。
3.1.1 persistence
数据持久化层,主要包括mysql和repository。
- mysql
主要是用来和数据库做绑定,映射对象到PO。
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.example.buildbaseframe.infrastructure.user.persistence.mysql.mapper.UserPoMapper">
<select id="findThis" resultType="com.example.buildbaseframe.infrastructure.user.persistence.repository.po.UserPo">
select *
from t_user
where id = #{id}
</select>
</mapper>
namespace是为了绑定mysql包下对应的映射文件,下写各种sql操作语句。
xxxPoMapper文件是为了做数据库对象和PO对象的映射,举例:
@Mapper
public interface UserPoMapper extends BaseMapper<UserPo> {
/**
* 测试xml方式绑定查询
*/
UserPo findThis(@Param("id") Long id);
}
- PoMapper文件只需要写一个接口继承自BaseMapper,类型名为同名的Po类(自定义)
- PoMapper文件的方法名和xml文件中的操作id需要一致
- @Param传入参数也需要和xml对应
- repository
主要操作PO对象。
PO包
将数据库表结构映射到JAVA,举例:
@Data
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
@TableName("t_user")
public class UserPo extends BaseDatabasePo {
private static final long serialVersionUID = 1L;
/**
* 用户昵称
*/
@TableField(value = "name")
private String nickname;
/**
* 用户头像url
*/
@TableField(value = "avatar_url")
private String avatarUrl;
/**
* 用户个人介绍
*/
@TableField(value = "description")
private String introduction;
/**
* 用户性别
*/
@TableField(value = "gender")
private Integer gender;
}
由于对PO对象有一些公共操作,通常先在common包中写一个公共类:
@Data
@EqualsAndHashCode(exclude = {"createTime", "updateTime"})
public class BaseDatabasePo implements Serializable {
/**
* 主键ID
*/
@TableId(value = "id", type = IdType.ASSIGN_ID)
protected Long id;
/**
* 创建时间
*/
@TableField
protected LocalDateTime createTime;
/**
* 更新时间
*/
@TableField
protected LocalDateTime updateTime;
}
repository接口
主要包含一些对数据对象的最基本的操作方法。举例:
public interface UserRepository {
public UserPo get(Long userId);
public int update(UserPo po, Long userId);
public Long insertOneUser(UserPo userPo);
}
repository实现类
主要是对接口类的实现。
@Repository
public class UserRepositoryImpl implements UserRepository {
@Autowired
private UserPoMapper mapper;
@Override
public UserPo get(Long userId) {
return mapper.selectById(userId);
}
@Override
public int update(UserPo po, Long userId) {
UpdateWrapper<UserPo> wrapper = new UpdateWrapper<>();
wrapper.eq("id", userId);
if (po.getNickname() != null)
wrapper.set("nickname", po.getNickname());
if (po.getAvatarUrl() != null)
wrapper.set("avatar_url", po.getAvatarUrl());
if (po.getIntroduction() != null)
wrapper.set("introduction", po.getIntroduction());
if (po.getGender() != null)
wrapper.set("gender", po.getGender());
return mapper.update(po, wrapper);
}
@Override
public Long insertOneUser(UserPo userPo){
mapper.insert(userPo);
return userPo.getId();
}
}
- 先注入PoMapper对象,用于调用各种对数据库底层的操作
3.2 application
对应应用层,主要结构如下:
- common包:公共配置,主要涉及到一些exception的处理
- 各模块包
主要讲解各模块包,结构如下,其中dto、converter、service是必须的:
3.2.1 dto
前端给后端传递的数据,举例:
@Data
public class UserInfoDto {
/**
* 用户id
*/
private Long id;
/**
* 用户昵称
*/
private String nickname;
/**
* 用户头像url
*/
private String avatarUrl;
/**
* 用户个人介绍
*/
private String introduction;
/**
* 用户性别
*/
private GenderEnum gender;
}
3.2.2 converter
只用写接口,具体方法会自动实现。用来实现Po和Dto对象之间的转换。
@Mapper(componentModel = "spring")
public interface UserAppConverter {
UserInfoDto toUserInfoDto(UserPo po);
UserPo toUserPo(UserInfoDto dto);
}
3.2.3 service
服务层的接口和实现,用来实现和数据访问层的交互。
接口
public interface UserService {
public UserInfoDto getUserInfo(Long userId);
public UserInfoDto updateUserInfo(UserInfoDto dto, Long userId);
public Long insertOneUser(UserInfoDto userInfoDto);
}
实现类
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private UserAppConverter userAppConverter;
/**
* 根据id获取用户信息
* @param userId
* @return
*/
@Override
public UserInfoDto getUserInfo(Long userId) {
UserPo po = userRepository.get(userId);
if (po == null) {
throw new NotFoundException("用户信息");
}
System.out.println(po);
return userAppConverter.toUserInfoDto(po);
}
/**
* 更新用户信息
* @param dto
* @param userId
* @return
* @throws NotFoundException
*/
@Override
public UserInfoDto updateUserInfo(UserInfoDto dto, Long userId){
UserPo po = userAppConverter.toUserPo(dto);
if (userRepository.get(userId) == null) {
throw new NotFoundException("用户信息");
}
int succ = userRepository.update(po, userId);
return userAppConverter.toUserInfoDto(userRepository.get(userId));
}
/**
* 添加新用户
* @param userInfoDto
* @return
*/
@Override
public Long insertOneUser(UserInfoDto userInfoDto){
UserPo userPo = userAppConverter.toUserPo(userInfoDto);
UserPo userPo1 = userRepository.findByName(userPo.getNickname());
if(userPo1!=null){
throw new BusinessException(ExceptionType.DUPLICATE_ERROR);
}
Long id = userRepository.insertOneUser(userPo);
return id;
}
}
3.3 api
控制器层,目录结构如下:
- common包:公共类,包括异常捕获、权限、page验证等功能
- 各模块包
主要讲解模块包,结构如下:
3.3.1 vo
数据视图对象,后端传给前端的数据。
@Data
public class UserInfoVo {
/**
* 用户id
*/
private String id;
/**
* 用户昵称
*/
private String nickname;
/**
* 用户头像url
*/
private String avatarUrl;
/**
* 用户个人介绍
*/
private String introduction;
/**
* 用户性别
*/
private String gender;
}
3.3.2 req
对vo对象做检查。
@Data
public class UserCreateReq {
/**
* 用户昵称
*/
@Length(min = 2, max = 64, message = "昵称长度应在2-64之间")
private String nickname;
/**
* 用户头像url
*/
@URL(message = "头像应当是有效的图片url")
@Length(max = 255, message = "头像URL长度不能超过255")
private String avatarUrl;
/**
* 用户个人介绍
*/
@Length(max = 255, message = "用户介绍长度不能超过255")
private String introduction;
/**
* 用户性别
*/
@EnumStringValidate(value = GenderEnum.class, message = "不是有效的性别类型")
private String gender;
}
3.3.3 converter
dto和vo对象的转换。
@Mapper(componentModel = "spring")
public interface UserApiConverter{
@Mapping(source = "introduction", target = "description")
UserInfoVo toUserInfoVo(UserInfoDto dto);
@Mapping(source = "description", target = "introduction")
UserInfoDto toUserInfoDto(UserCreateReq req);
}
3.3.4 controller
api的实现。
@Validated
@RestController // 既需要返回html数据又需要返回非html数据,类上使用@controller,方法上@requestcontroller或@responsebody
@Slf4j
@RequestMapping("/api/v1/user")
public class UserController {
@Autowired
private UserService service;
@Autowired
private UserApiConverter userApiConverter;
@Autowired
private GenderEnumConverter genderEnumConverter;
/**
* 用户获得自己的基本信息
*
* @param
* @return
* @methodName getOwnInfo
*/
// 可以不用返回CommonResullt,有包装器会拦截
@RequestMapping(value = "/info/own", method = RequestMethod.GET)
public UserInfoVo getOwnInfo() {
return userApiConverter.toUserInfoVo(service.getUserInfo(SecurityContextHolder.getUserId()));
}
/**
* 根据id获得用户信息
*
* @param
* @return
* @methodName getUserInfo
*/
@RequestMapping(value = "/info/{userId}", method = RequestMethod.GET)
public UserInfoVo getUserInfo(@PathVariable("userId") String userId) {
return userApiConverter.toUserInfoVo(service.getUserInfo(Long.valueOf(userId)));
}
}
结语
以上是关于springboot的一个基本框架的梳理,具体细节方面的实现需要根据实际需求修改。