@Service
@Slf4j
public class TestService1 {
public void test1() {
addLog('test1');
if (condition1) {
if (condition2) {
if (condition3) {
log.info('info:{}', info);
}
}
}
}
}
public void update(User user) {
if (null != user.getId()) {
User oldUser = userMapper.findUserById(user.getId());
if(null == oldUser) {
throw new RuntimeException('用户id不存在');
}
oldUser.setName(user.getName());
oldUser.setAge(user.getAge());
oldUser.setAddress(user.getAddress());
userMapper.updateUser(oldUser);
} else {
userMapper.insertUser(user);
}
}
int supplierCount = 1;
int purchaserCount = 2;
String userName = 'abc';
boolean hasSuccess = false;
见名知意
,不然就会出现这样的情况:String userName = '苏三';
String susan = '苏三';
驼峰风格
,即:第一个字母小写,后面的每个单词首字母大写。例如:int supplierCount = 1;
大写字母
下划线
分隔的参数名。例如:ctrl c
和 ctrl v
可能是程序员使用最多的快捷键了。@Service
@Slf4j
public class TestService1 {
public void test1() {
addLog('test1');
}
private void addLog(String info) {
if (log.isInfoEnabled()) {
log.info('info:{}', info);
}
}
}
@Service
@Slf4j
public class TestService3 {
public void test3() {
addLog('test3');
}
private void addLog(String info) {
if (log.isInfoEnabled()) {
log.info('info:{}', info);
}
}
}
@Slf4j
public class LogUtil {
private LogUtil() {
throw new RuntimeException('初始化失败');
}
public static void addLog(String info) {
if (log.isDebugEnabled()) {
log.debug('debug:{}', info);
}
}
}
代码即注释
。这也给那些不喜欢写代码注释的人,找了一个合理的理由。spring
的核心方法refresh
,也是加了很多注释的:public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn('Exception encountered during context initialization - '
'cancelling refresh attempt: ' ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
方法拆分
,即把一个大方法拆分成多个小方法。public void run() {
List<User> userList = userMapper.getAll();
List<User> updateList = filterUser(userList);
if(CollectionUtils.isEmpty(updateList)) {
return;
}
for(User user: updateList) {
clacExpireDay(user);
}
updateUser(updateList);
sendMq(updateList);
}
private List<User> filterUser(List<User> userList) {
//经过一系列的数据过滤
//此处省略了50行代码
List<User> updateList = //最终获取到user集合
return updateList;
}
private void clacExpireDay(User user) {
//经过一些复杂的过期时间计算
//此处省略30行代码
}
private void updateUser(List<User> updateList) {
//分页更新用户的过期时间
//此处省略20行代码
}
private void sendMq(List<User> updateList) {
//发mq消息通知用户
//此处省略30行代码
}
顺便说一句,Hotspot对字节码超过8000字节的大方法有JIT编译限制,超过了限制不会被编译。
5
个。public Result fun(String a,
String b,
String c) {
...
return result;
}
public void otherFun(Result result,
String d,
String e,
String f) {
...
}
public void client() {
Result result = fun('a','b','c');
otherFun(result, 'd', null, 'f');
}
lombok
的@Builder
注解,做成链式调用。例如:@NoArgsConstructor
@AllArgsConstructor
@Builder
@Data
public class Result {
private String a;
private String b;
private String c;
}
ThreadPoolExecutor
不也提供了7个参数的方法?public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
...
}
构造方法
,我们这里主要讨论的是普通方法
。for(int i=0; i<100;i ) {
for(int j=0; j<50;j ) {
for(int m=0; m<200;m ) {
for(int n=0; n<100;n ) {
for(int k=0; k<50; k ) {
...
}
}
}
}
}
代码层级太深
。private void doConditionB() {
if(b!=2) {
...
return;
}
doConditionC();
}
面向防御式编程
的一种,即先把不满足条件的代码先执行,然后才执行满足条件的代码。此外别忘了,把满足条件的代码抽取到一个新的方法中喔。map
。Map<Long, List<OrderDetail>> detailMap = detailList.stream().collect(Collectors.groupingBy(OrderDetail::getOrderId));
for(Order order:orderList) {
List<OrderDetail> detailList = detailMap.get(order.getId());
if(CollectionUtils.isNotEmpty) {
doSamething();
}
}
开闭原则:对扩展开放,对修改关闭。就是说增加新功能要尽量少改动已有代码。
单一职责原则:顾名思义,要求逻辑尽量单一,不要太复杂,便于复用。
策略模式
工厂模式
。public interface IPay {
void pay();
}
@Service
public class AliaPay implements IPay {
@PostConstruct
public void init() {
PayStrategyFactory.register('aliaPay', this);
}
@Override
public void pay() {
System.out.println('===发起支付宝支付===');
}
}
@Service
public class WeixinPay implements IPay {
@PostConstruct
public void init() {
PayStrategyFactory.register('weixinPay', this);
}
@Override
public void pay() {
System.out.println('===发起微信支付===');
}
}
@Service
public class JingDongPay implements IPay {
@PostConstruct
public void init() {
PayStrategyFactory.register('jingDongPay', this);
}
@Override
public void pay() {
System.out.println('===发起京东支付===');
}
}
public class PayStrategyFactory {
private static Map<String, IPay> PAY_REGISTERS = new HashMap<>();
public static void register(String code, IPay iPay) {
if (null != code && !''.equals(code)) {
PAY_REGISTERS.put(code, iPay);
}
}
public static IPay get(String code) {
return PAY_REGISTERS.get(code);
}
}
@Service
public class PayService3 {
public void toPay(String code) {
PayStrategyFactory.get(code).pay();
}
}
静态常量
。@Value('${com.susan.maxLimit:200}')
private int maxLimit = 200;
public void upload(List<Order> orderList) {
if(CollectionUtils.isEmpty(orderList)) {
throw new BusinessException('订单不能为空');
}
if(orderList.size() > maxLimit) {
throw new BusinessException('超过单次请求的数量限制');
}
}
我们在前期开发的时候,宁可多花一分钟思考一下,这个参数后面是否会被修改,是否可以定义成可配置的参数。也比后期修改代码,重新编译,重新打包,重新上线花的时间少得多。
@Transactional
注解声明事务。例如:@Transactional
注解声明一下,该方法通过AOP就自动拥有了事务的功能。@Transactional(rollbackFor = Throwable.class)
public void updateUser(User user) {
User oldUser = userMapper.getUserById(user.getId());
if(null != oldUser) {
userMapper.update(user);
} else {
userMapper.insert(user);
}
sendMq(user);
}
TransactionTemplate
的编程式事务优化代码。execute
方法中的代码块才真正需要事务,其余的方法,可以非事务执行,这样就能缩小事务的范围,避免大事务。TransactionTemplate
这种编程式事务,缩小事务范围,来解决大事务问题,只是其中一种手段。public void register(List<Corp> corpList) {
for(Corp corp: corpList) {
CorpInfo info = tianyanchaService.query(corp);
if(null == info) {
throw new RuntimeException('企业名称或统一社会信用代码不正确');
}
}
doRegister(corpList);
}
CompleteFuture
类,实现多个线程查天眼查接口,并且把查询结果统一汇总到一起。手动捕获异常
。例如:public void run() throws Exception {
try {
doSameThing();
} catch (Exception e) {
log.error(e.getMessage(), e);
throw e;
}
doOtherThing();
}
@PostMapping('/query')
public List<User> query(@RequestBody List<Long> ids) {
log.info('request params:{}', ids);
List<User> userList = userService.query(ids);
log.info('response:{}', userList);
return userList;
}
isDebugEnabled
判断一下,如果当前的日志级别是debug才打印日志。生产环境默认日志级别是info,在有些紧急情况下,把某个接口或者方法的日志级别改成debug,打印完我们需要的日志后,又调整回去。@PostMapping('/add')
public void add(@RequestBody User user) {
if(StringUtils.isEmpty(user.getName())) {
throw new RuntimeException('name不能为空');
}
if(null != user.getAge()) {
throw new RuntimeException('age不能为空');
}
if(StringUtils.isEmpty(user.getAddress())) {
throw new RuntimeException('address不能为空');
}
userService.add(user);
}
hibernate
的参数校验框架validate
之后,参数校验一下子变得简单多了。@Validated
注解,在接口方法上加上@Valid
注解。@Slf4j
@Validated
@RestController
@RequestMapping('/user')
public class UserController {
@Autowired
private UserService userService;
@PostMapping('/add')
public void add(@RequestBody @Valid User user) {
userService.add(user);
}
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Role {
@NotEmpty
private String roleName;
@NotEmpty
private String tag;
}
@NoArgsConstructor
@AllArgsConstructor
@Data
public class User {
private Long id;
@NotEmpty
private String name;
@NotNull
private Integer age;
@NotEmpty
private String address;
@NotNull
@Valid
private Role role;
}
@Valid
注解。温馨的提醒一声,使用validate框架校验参数一定要自测,因为很容易踩坑。
{
'code':0,
'msg':null,
'success':true,
'result':[]
}
温馨的提醒一下,业务服务不要捕获异常,直接把异常抛给网关服务,由它来统一全局捕获异常,这样就能统一异常的返回值结构。
gitlab
上,也有一些讲究。git
把代码提交了。例如:public void test() {
String userName='苏三';
String password=
}
好的习惯是:用git提交代码之前,一定要在本地运行一下,确保项目能正常启动才能提交。
@Deprecated
表示这个类或者方法没在使用了,例如:@Slf4j
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public void add(User user) {
System.out.println('add');
}
@Deprecated
public void update(User user) {
System.out.println('update');
}
@Deprecated
public void query(User user) {
System.out.println('query');
}
}
@Deprecated
注解的方法。这样一个看似简单的举手之劳,可以给自己,或者接手该代码的人,节省很多重复查代码的时间。建议我们把没用的代码优先删除掉,因为gitlab中是有历史记录的,可以找回。但如果有些为了兼容调用方老版本的代码,不能删除的情况,建议使用 @Deprecated
注解相关类或者接口。
@PostMapping('/queryUser')
public List<User> queryUser(@RequestBody List<Long> ids) {
return userService.query(ids);
}
对于已经在线上使用的接口,尽量不要修改接口名、参数名、修改参数类型、修改参数个数,还有请求方式,比如:get改成post等。宁可新加一个接口,也尽量不要影响线上功能。
{
'id':123,
'name':'苏三',
'age':18,
'address':'成都'
}
@PostMapping('/add')
public void add(@RequestBody @Valid User user){
System.out.println(user);
}
测试驱动开发
,他们会先把单元测试写好,再写具体的业务逻辑。我们新写的对外接口,测试同学不可能完全知道逻辑,只有开发自己最清楚。不像页面功能,可以在页面上操作。他们在测试接口时,很有可能覆盖不到位,很多bug测不出来。
ction>< font=""><>i><>我们写的代码大多数是可维护的代码,很有可能在未来的某一天需要被重构。试想一下,如果有些业务逻辑非常复杂,你敢轻易重构不?如果有单元测试就不一样了,每次重构完,跑一次单元测试,就知道新写的代码有没有问题。
ction>< font=""><>i>建议由于项目时间非常紧张,在开发时确实没有写单元测试,但在项目后期的空闲时间也建议补上。
联系客服