1. 避免滥用@Autowired
为什么
@Autowired
可以将依赖注入到组件中,但滥用它可能导致代码的紧耦合和难以测试。使用构造函数注入或@Resource
等方式可以使依赖关系更明确。
其实使用IDEA可以很直观的注意到这个问题,毕竟它已经给出了警告和提示,任何有代码强迫症的选手都应该干掉这条可恶的波浪线!
建议:
优先使用构造函数注入,它明确了组件的依赖关系,而且在单元测试中更易于模拟。
如果用了Lombok,那可以使用@RequiredArgsConstructor
注解来自动生成构造函数。
/**
* 《像乌鸦一样思考》让孩子学会观察和独立思考!
* https://www.sanzhiwa.top/6718.html
*/
@RequiredArgsConstructor
public class EnterpriseController {
private final EnterpriseCorporationService enterpriseCorporationService;
private final EnterpriseRepository enterpriseRepository;
}
2. 避免在Controller中写业务逻辑
为什么
严格来说,控制器只负责处理HTTP请求和响应,业务逻辑应该放在其他层(如Service层)中进行。
如果业务和请求响应混在一起,非常不利于单元测试的编写。如果把业务放到Service里,那单元测试可以针对性地去测Service。
建议
将业务逻辑移到服务层(Service)中,控制器只负责处理请求和调用服务方法。
拆分之后不仅单元测试方便,而且代码更容易复用。
/**
* 清华附小1-6年级趣味英语动画
* https://www.sanzhiwa.top/6711.html
*/
@RestController
@RequestMapping("/products")
public class ProductController {
private final ProductService productService;
// 构造函数注入
public ProductController(ProductService productService) {
this.productService = productService;
}
@GetMapping("/{id}")
public ResponseEntity<Product> getProduct(@PathVariable Long id) {
// 推荐:调用Service层的业务逻辑
Product product = productService.getProductById(id);
// 返回响应
return ResponseEntity.ok(product);
}
}
3. 使用@ConfigurationProperties而非@Value
为什么:
使用@Value
注解获取配置虽然简单,但是缺乏结构,而且用的一旦多了,会导致项目里到处都是@Value
,不利于代码维护和重用。
使用@ConfigurationProperties
可以避免上述问题,使配置更清晰且易于管理。
建议:
创建一个专门的配置类,使用@ConfigurationProperties
注解来绑定相关配置项,提高代码的可读性。
在多个地方使用同一个配置类时,可以避免重复配置属性,提高代码的重用性。
同时使配置更加结构化,便于维护和理解。
比如下面这个App的配置,当需要处理大量属性或复杂配置结构时,它提供的便利性和长远影响远大于建个类所花费的时间。
@ConfigurationProperties(prefix = "app")
public class AppConfig {
private String name;
private int timeout;
// getter 和 setter 方法
}
4. 抛出异常而非返回Result
先看这段代码:
/**
* 儿童新概念英语
* https://www.sanzhiwa.top/6696.html
*/
@Service
public class ProductService {
@Autowired
private ProductRepository productRepository;
public Result<Product, String> getProductById(Long id) {
Optional<Product> optionalProduct = productRepository.findById(id);
if (optionalProduct.isPresent()) {
return Result.ok(optionalProduct.get());
} else {
return Result.error("Product not found with id: " + id);
}
}
}
是不是闻到一股不好的味道,为了在service中能够判断查询结果,直接把Result作为方法返回值,太不优雅。
如果把 return Result.error
这段代码换成 throw new XXException
,一个是可读性变强,另一个是可以让service返回业务的结果,而且控制器的结果。
再看优化后的代码:
/**
*
* 微观小世界】奇趣探险,启程进入神秘的微观世界!
* https://www.sanzhiwa.top/6704.html
*/
@Service
public class ProductService {
@Autowired
private ProductRepository productRepository;
public Product getProductById(Long id) {
return productRepository.findById(id)
.orElseThrow(() -> new NotFoundException("Product not found with id: " + id));
}
}
service就只返回业务结果,不应该与控制器结果有牵扯。
同时抛出的异常,能让维护人员一眼就能看出端倪,这里不能为空。
当然实际开发时的业务代码不会这样简短,这里为了偷懒直接用lambda语法做精简。
值得注意的是,务必使用@ControllerAdvice
注解,利用SpringBoot的全局异常处理机制及时捕获业务中抛出的异常,避免500错误,这里提供简单的示例:
5. 使用自带的响应结果类ResponseEntity
为什么
很多人会封装一个Result类作为Controller的实体,其实SpringBoot本身就有一个专用的响应实体叫做ResponseEntity
。
ResponseEntity
提供了更大的灵活性,能够控制响应的各个方面,包括HTTP状态码、响应头、响应体等,使得程序能够更精确地构建响应结果,根据业务需求返回不同的HTTP状态码等。
而且ResponseEntity
也支持范型,能够返回不同类型的响应体,使得你能够处理不同业务场景的响应需求。
建议
ResponseEntity
值得一试