这10个Spring错误你一定中过招!

帮助万千Java学习者持续成长

B 站搜索:楠哥教你学Java
获取更多优质视频教程
错误一:太过关注底层
错误二:内部结构 “泄露”
public class TopTalentEntity {private Integer id;private String name;public TopTalentEntity(String name) {this.name = name;}}
TopTalentEntity数据。返回TopTalentEntity实例可能很诱人,但更灵活的解决方案是创建一个新的类来表示 API 端点上的TopTalentEntity数据。public class TopTalentData {private String name;}
TopTalentEntity中添加一个 “password” 字段来存储数据库中用户密码的 Hash 值 —— 如果没有TopTalentData之类的连接器,忘记更改服务前端,将会意外地暴露一些不必要的秘密信息。错误三:缺乏关注点分离
TopTalentData。public class TopTalentController {private final TopTalentRepository topTalentRepository;public List<TopTalentData> getTopTalent() {return topTalentRepository.findAll().stream().map(this::entityToData).collect(Collectors.toList());}private TopTalentData entityToData(TopTalentEntity topTalentEntity) {return new TopTalentData(topTalentEntity.getName());}}
TopTalentEntity实例检索出来的TopTalentData的 List。TopTalentController实际上在此做了些事情;也就是说,它将请求映射到特定端点,从数据库检索数据,并将从TopTalentRepository接收的实体转换为另一种格式。一个“更干净” 的解决方案是将这些关注点分离到他们自己的类中。看起来可能是这个样子的:public class TopTalentController {private final TopTalentService topTalentService;public List<TopTalentData> getTopTalent() {return topTalentService.getTopTalent();}}public class TopTalentService {private final TopTalentRepository topTalentRepository;private final TopTalentEntityConverter topTalentEntityConverter;public List<TopTalentData> getTopTalent() {return topTalentRepository.findAll().stream().map(topTalentEntityConverter::toResponse).collect(Collectors.toList());}}public class TopTalentEntityConverter {public TopTalentData toResponse(TopTalentEntity topTalentEntity) {return new TopTalentData(topTalentEntity.getName());}}
错误四:缺乏异常处理或处理不当
public class ErrorResponse {private Integer errorCode;private String errorMessage;}
@ExceptionHandler注解来完成(注解案例可见于第六章)。避免全局状态
避免可变性
记录关键数据
复用现存实现
错误六:不使用基于注解的验证
@RequestMapping("/put")public void addTopTalent(@RequestBody TopTalentData topTalentData) {boolean nameNonExistentOrHasInvalidLength =Optional.ofNullable(topTalentData).map(TopTalentData::getName).map(name -> name.length() == 10).orElse(true);if (nameNonExistentOrInvalidLength) {// throw some exception}topTalentService.addTopTalent(topTalentData);}
public void addTopTalent( TopTalentData topTalentData) {topTalentService.addTopTalent(topTalentData);}public ErrorResponse handleInvalidTopTalentDataException(MethodArgumentNotValidException methodArgumentNotValidException) {// handle validation exception}// 此外,我们还必须指出我们想要在 TopTalentData 类中验证什么属性:public class TopTalentData {private String name;}
({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})(RetentionPolicy.RUNTIME)(validatedBy = { MyAnnotationValidator.class })public MyAnnotation {String message() default "String length does not match expected";Class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};int value();}public class MyAnnotationValidator implements ConstraintValidator<MyAnnotation, String> {private int expectedLength;public void initialize(MyAnnotation myAnnotation) {this.expectedLength = myAnnotation.value();}public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {return s == null || s.length() == this.expectedLength;}}
isValid方法中的s == null),如果这是属性的附加要求,则使用@NotNull注解。public class TopTalentData {private String name;}
错误七:(依旧)使用基于xml的配置
@SpringBootApplication复合注解中做了声明,如下所示:public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}}
@Component(TopTalentConverter,MyAnnotationValidator)@RestController(TopTalentController)@Repository(TopTalentRepository)@Service(TopTalentService) 类
@Configuration注解类,它们也会检查基于 Java 的配置。错误八:忽略 profile
APPLICATION.YAML 文件
# set default profile to 'dev'spring.profiles.active: dev# production database details# 公众号:程序员追风spring.datasource.url: 'jdbc:mysql://localhost:3306/toptal'spring.datasource.username: rootspring.datasource.password:8.2. APPLICATION-DEV.YAML 文件spring.datasource.url: 'jdbc:h2:mem:'spring.datasource.platform: h2
-Dspring.profiles.active=prod参数给 JVM 来手动覆盖配置文件。另外,还可将操作系统的环境变量设置为所需的默认 profile。错误九:无法接受依赖项注入
public class TopTalentController {private final TopTalentService topTalentService;public TopTalentController() {this.topTalentService = new TopTalentService();}}
public class TopTalentController {private final TopTalentService topTalentService;public TopTalentController(TopTalentService topTalentService) {this.topTalentService = topTalentService;}}
TopTalentService行为正确的前提下测试控制器。我们可以通过提供一个单独的配置类来插入一个模拟对象来代替实际的服务实现:@Configurationpublic class SampleUnitTestConfig {public TopTalentService topTalentService() {TopTalentService topTalentService = Mockito.mock(TopTalentService.class);Mockito.when(topTalentService.getTopTalent()).thenReturn(Stream.of("Mary", "Joel").map(TopTalentData::new).collect(Collectors.toList()));return topTalentService;}}
SampleUnitTestConfig作为它的配置类来注入模拟对象:@ContextConfiguration(classes = { SampleUnitTestConfig.class })错误十:缺乏测试,或测试不当
DispatcherServlet,并查看当收到一个实际的HttpServletRequest时会发生什么(使它成为一个 “集成” 测试,处理验证、序列化等)。@RunWith(SpringJUnit4Cla***unner.class)@ContextConfiguration(classes = {Application.class,SampleUnitTestConfig.class})public class RestAssuredTestDemonstration {@Autowiredprivate TopTalentController topTalentController;@Testpublic void shouldGetMaryAndJoel() throws Exception {// givenMockMvcRequestSpecification givenRestAssuredSpecification = RestAssuredMockMvc.given().standaloneSetup(topTalentController);// whenMockMvcResponse response = givenRestAssuredSpecification.when().get("/toptal/get");// thenresponse.then().statusCode(200);response.then().body("name", hasItems("Mary", "Joel"));}}
SampleUnitTestConfig类将TopTalentService的模拟实现连接到TopTalentController中,而所有的其他类都是通过扫描应用类所在包的下级包目录来推断出的标准配置。RestAssuredMockMvc只是用来设置一个轻量级环境,并向/toptal/get端点发送一个GET请求。1、搞定数据库索引,不怕面试官问了!
楠哥简介
资深 Java 工程师,微信号 southwindss
《Java零基础实战》一书作者
腾讯课程官方 Java 面试官,今日头条认证大V
GitChat认证作者,B站认证UP主(楠哥教你学Java)
致力于帮助万千 Java 学习者持续成长。

评论
