目录
关于Service
关于Controller
关于各组件的处理流程
补充:
Service保证数据完整性是怎么体现的?
以下代表复制属性,把来源adminAddNewParam复制到目标 admin里面
关于Service
Service的核心价值在于:组织业务流程,处理业务逻辑,以保证数据的完整性、有效性、安全性。
在编写代码时,强烈建议先定义Service的接口,然后,自定义编写其实现类。
关于“新增相册”,先自定义POJO类,用于封装相关参数!在项目的根包下创建pojo.param.AlbumAddNewParam
类:
@Data
public class AlbumAddNewParam implements Serializable {
private String name;
private String description;
private Integer sort;
}
然后,在项目的根包下创建service.IAlbumService
,并在接口中添加“新增相册”的抽象方法:
public interface IAlbumService {
void addNew(AlbumAddNewParam albumAddNewParam);
}
然后,在项目的根包下创建service.impl.AlbumServiceImpl
类,实现以上接口:
public class AlbumServiceImpl implements IAlbumService {}
关于Service方法的声明:
-
返回值类型:仅以操作成功为前提来设计返回值
-
操作失败全部通过抛出异常来表示
-
-
方法名称:自定义
-
参数列表:如果参数数量较多,且具有相关性,可以封装,如果参数数量较少,或不具备相关性,则逐一声明
-
抛出异常:抛出所有遇到的异常,如果只会出现
RuntimeException
,并不需要使用throws
关键字显式的声明
在具体实现之前,为了明确的表示出错的原因是因为所设计的规则,应该先自定义异常类型,并且,在规则验证不通过时,抛出自定义异常类型,后续,在调用此方法时,根据是否抛出了自定义异常类型来判断是否符合所设计的规则。
在项目的根包下创建ex.ServiceException
类,继承自RuntimeException
类,并添加基于父级异常的、带String message
参数的构造方法:
public class ServiceException extends RuntimeException {
public ServiceException(String message) {
super(message);
}
}
关于自定义异常需要继承自RuntimeException
,原因主要有2点:
-
如果继承的不是
RuntimeException
,抛出异常的方法必须显式的使用throws
声明抛出,并且,Service方法的调用者(Controller)也必须在代码中明确的try...catch
或throws
,而开发实践中,会使用Spring MVC框架的全局异常处理机制来统一处理异常,则Service、Controller等组件都必须将异常抛出,这是固定的做法,所以,没有必要继承自非RuntimeException
并反复声明抛出异常 -
基于Spring JDBC的事务管理将根据
RuntimeException
进行回滚
然后,在AlbumServiceImpl
类上添加@Service
注解,在类中自动装配AlbumMapper
类型的属性,并实现接口中定义的抽象方法,在实现过程中,如果判断违背了所设计的规则,应该抛出自定义的ServiceException
类型的异常对象:
@Service
public class AlbumServiceImpl implements IAlbumService {
@Autowired
private AlbumMapper albumMapper;
@Override
public void addNew(AlbumAddNewParam albumAddNewParam) {
// 检查相册名称是否被占用,如果被占用,则抛出异常
QueryWrapper<Album> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("name", albumAddNewParam.getName()); // name='参数中的相册名称'
int countByName = albumMapper.selectCount(queryWrapper);
if (countByName > 0) {
String message = "添加相册失败,相册名称已经被占用!";
// System.out.println(message);
throw new ServiceException(message);
}
// 将相册数据写入到数据库中
Album album = new Album();
BeanUtils.copyProperties(albumAddNewParam, album);
album.setGmtCreate(LocalDateTime.now());
album.setGmtModified(LocalDateTime.now());
albumMapper.insert(album);
}
}
完成后,应该在src/test/java
下的根包下创建service.AlbumServiceTests
测试类,测试以上方法:
@SpringBootTest
public class AlbumServiceTests {
@Autowired
IAlbumService service;
@Test
void addNew() {
AlbumAddNewParam albumAddNewParam = new AlbumAddNewParam();
albumAddNewParam.setName("测试数据-00003");
albumAddNewParam.setDescription("测试数据简介-00003");
albumAddNewParam.setSort(99);
try {
service.addNew(albumAddNewParam);
System.out.println("添加成功!");
} catch (ServiceException e) {
System.out.println(e.getMessage());
} catch (Throwable e) {
System.out.println("添加失败!出现了某种异常!");
e.printStackTrace();
}
}
}
关于Controller
通过控制器接收并处理请求
在通过控制器处理请求之前,需要添加对应的依赖项:spring-boot-starter-web
。
提示:所有Spring提供的以spring-boot-starter
作为名称前缀的依赖项(例如spring-boot-starter-web
),都包含了Spring Boot的基础依赖项(spring-boot-starter
)。
则将项目中原本依赖的spring-boot-starter
改为spring-boot-starter-web
。
在项目的根包下创建cn.tedu.csmall.product.controller.AlbumController
,在类中添加方法处理“添加相册”的请求:
@RestController
@RequestMapping("/album")
public class AlbumController {
@Autowired
private IAlbumService albumService;
// http://localhost:8080/album/add-new?name=TestName001&description=TestDescription001&sort=99
@RequestMapping("/add-new")
public String addNew(AlbumAddNewParam albumAddNewParam) {
try {
albumService.addNew(albumAddNewParam);
return "添加成功!";
} catch (ServiceException e) {
return e.getMessage();
} catch (Throwable e) {
return "添加失败!出现了某种异常!";
}
}
}
完成后,启动项目,在浏览器的地址栏中使用 http://localhost:8080/album/add-new?name=TestName001&description=TestDescription001&sort=99 即可测试访问。
关于各组件的处理流程
补充:
Service保证数据完整性是怎么体现的?
用户提交的数据不够表中的字段时,就需要我们在Service层进行补全,普通用户操作界面提交的数据就是不完整的,因为有一部分数据我们不放心他去提交,比如时间。但是他不提提交,数据库里面又有,我们就去补全。
以下代表复制属性,把来源adminAddNewParam复制到目标 admin里面
BeanUtils.copyProperties(adminAddNewParam, admin);