细思极恐-你真的会写Java吗?

导语

目前创新互联公司已为上1000+的企业提供了网站建设、域名、网页空间、网站托管、服务器租用、企业网站设计、竹山网站维护等服务,公司将坚持客户导向、应用为本的策略,正道将秉承"和谐、参与、激情"的文化,与客户和合作伙伴齐心协力一起成长,共同发展。

自2013年毕业后,今年已经是我工作的第4个年头了,总在做java相关的工作,终于有时间坐下来,写一篇关于java写法的一篇文章,来探讨一下如果你真的是一个java程序员,那你真的会写java吗?

笔者是一个务实的程序员,故本文绝非扯淡文章,文中内容都是干货,望读者看后,能有所收获。

文章核心

其实,本不想把标题写的那么恐怖,只是发现很多人干了几年java以后,都自认为是一个不错的java程序员了,可以拿着上万的工资都处宣扬自己了,写这篇文章的目的并不是嘲讽和我一样做java的同行们,只是希望读者看到此骗文章后,可以和我一样,心平气和的争取做一个优秀的程序员。

讲述方向

由于一直从事移动互联网相关工作,java开发中经常和移动端打交道或者做一些后端的工作,所以本篇文章更可能涉及和移动端的交互或者与后端的交互方式,笔者希望以自身的一些学习经验或者开发经验,可以带动认真阅读本篇文章的读者们,让大家对java有一个更好的态度去学习它,它不只是一个赚钱的工具而已。

笔者身边有很多与笔者年龄相仿或年龄更大的朋友或同事,经常有人问我:“你现在还在学习吗?我觉得没什么好学的,这些东西都差不多”,我总是回答只要有时间,我就要看一会书,这个时候,大家都会露出一副不屑的眼神或笑容。其实,非常能理解身边朋友或同事的看法,以目前状态来讲,大多都是工作至少5年的程序员了,对于公司大大小小的业务需要,以目前的知识储备来讲,都可以轻松应对,“没有什么好学的”其实这句话没有多大的问题,但是,如果你对编程还有一点点兴趣,只是不知道如何努力或改进,希望本篇文章可以帮到你。

技术点

本文不是一个吹嘘的文章,不会讲很多高深的架构,相反,会讲解很多基础的问题和写法问题,如果读者自认为基础问题和写法问题都是不是问题,那请忽略这篇文章,节省出时间去做一些有意义的事情。

开发工具

不知道有多少”老”程序员还在使用eclipse,这些程序员们要不就是因循守旧,要不就是根本就不知道其他好的开发工具的存在,eclipse吃内存卡顿的现象以及各种偶然莫名异常的出现,都告知我们是时候寻找新的开发工具了。

更换IDE

根本就不想多解释要换什么样的IDE,如果你想成为一个优秀的java程序员,请更换intellij idea. 使用idea的好处,请搜索谷歌。

别告诉我快捷键不好用

更换IDE不在我本文的重点内容中,所以不下想用太多的篇幅去写为什么更换IDE,请谷歌。

在这里,我只能告诉你,更换IDE只为了更好、更快的写好java代码。原因略。

别告诉我快捷键不好用,请尝试新事物。

bean

bean使我们使用最多的模型之一,我将以大篇幅去讲解bean,希望读者好好体会。

domain包名

根据很多java程序员的”经验”来看,一个数据库表则对应着一个domain对象,所以很多程序员在写代码时,包名则使用:com.xxx.domain ,这样写好像已经成为了行业的一种约束,数据库映射对象就应该是domain。但是你错了,domain是一个领域对象,往往我们再做传统java软件web开发中,这些domain都是贫血模型,是没有行为的,或是没有足够的领域模型的行为的,所以,以这个理论来讲,这些domain都应该是一个普通的entity对象,并非领域对象,所以请把包名改为:com.xxx.entity。

如果你还不理解我说的话,请看一下Vaughn Vernon出的一本叫做《IMPLEMENTING DOMAIN-DRIVEN DESIGN》(实现领域驱动设计)这本书,书中讲解了贫血模型与领域模型的区别,相信你会受益匪浅。

DTO

数据传输我们应该使用DTO对象作为传输对象,这是我们所约定的,因为很长时间我一直都在做移动端api设计的工作,有很多人告诉我,他们认为只有给手机端传输数据的时候(input or output),这些对象成为DTO对象。请注意!这种理解是错误的,只要是用于网络传输的对象,我们都认为他们可以当做是DTO对象,比如电商平台中,用户进行下单,下单后的数据,订单会发到OMS 或者 ERP系统,这些对接的返回值以及入参也叫DTO对象。

我们约定某对象如果是DTO对象,就将名称改为XXDTO,比如订单下发OMS:OMSOrderInputDTO。

DTO转化

正如我们所知,DTO为系统与外界交互的模型对象,那么肯定会有一个步骤是将DTO对象转化为BO对象或者是普通的entity对象,让service层去处理。

场景

比如添加会员操作,由于用于演示,我只考虑用户的一些简单数据,当后台管理员点击添加用户时,只需要传过来用户的姓名和年龄就可以了,后端接受到数据后,将添加创建时间和更新时间和默认密码三个字段,然后保存数据库。

 
 
 
  1. @RequestMapping("/v1/api/user")
  2. @RestController
  3. public class UserApi {
  4.     @Autowired
  5.     private UserService userService;
  6.     @PostMapping
  7.     public User addUser(UserInputDTO userInputDTO){
  8.         User user = new User();
  9.         user.setUsername(userInputDTO.getUsername());
  10.         user.setAge(userInputDTO.getAge());
  11.         return userService.addUser(user);
  12.     }
  13. }

我们只关注一下上述代码中的转化代码,其他内容请忽略:

 
 
 
  1. User user = new User();
  2. user.setUsername(userInputDTO.getUsername());
  3. user.setAge(userInputDTO.getAge());

请使用工具

上边的代码,从逻辑上讲,是没有问题的,只是这种写法让我很厌烦,例子中只有两个字段,如果有20个字段,我们要如何做呢? 一个一个进行set数据吗?当然,如果你这么做了,肯定不会有什么问题,但是,这肯定不是一个最优的做法。

网上有很多工具,支持浅拷贝或深拷贝的Utils. 举个例子,我们可以使用org.springframework.beans.BeanUtils#copyProperties对代码进行重构和优化:

 
 
 
  1. @PostMapping
  2. public User addUser(UserInputDTO userInputDTO){
  3.     User user = new User();
  4.     BeanUtils.copyProperties(userInputDTO,user);
  5.     return userService.addUser(user);
  6. }

BeanUtils.copyProperties是一个浅拷贝方法,复制属性时,我们只需要把DTO对象和要转化的对象两个的属性值设置为一样的名称,并且保证一样的类型就可以了。如果你在做DTO转化的时候一直使用set进行属性赋值,那么请尝试这种方式简化代码,让代码更加清晰!

转化的语义

上边的转化过程,读者看后肯定觉得优雅很多,但是我们再写java代码时,更多的需要考虑语义的操作,再看上边的代码:

 
 
 
  1. User user = new User();
  2. BeanUtils.copyProperties(userInputDTO,user);

虽然这段代码很好的简化和优化了代码,但是他的语义是有问题的,我们需要提现一个转化过程才好,所以代码改成如下:

 
 
 
  1. @PostMapping
  2.  public User addUser(UserInputDTO userInputDTO){
  3.          User user = convertFor(userInputDTO);
  4.          return userService.addUser(user);
  5.  }
  6.  private User convertFor(UserInputDTO userInputDTO){
  7.          User user = new User();
  8.          BeanUtils.copyProperties(userInputDTO,user);
  9.          return user;
  10.  }

这是一个更好的语义写法,虽然他麻烦了些,但是可读性大大增加了,在写代码时,我们应该尽量把语义层次差不多的放到一个方法中,比如:

 
 
 
  1. User user = convertFor(userInputDTO);
  2. return userService.addUser(user);

这两段代码都没有暴露实现,都是在讲如何在同一个方法中,做一组相同层次的语义操作,而不是暴露具体的实现。

如上所述,是一种重构方式,读者可以参考Martin Fowler的《Refactoring Imporving the Design of Existing Code》(重构 改善既有代码的设计) 这本书中的Extract Method重构方式。

抽象接口定义

当实际工作中,完成了几个api的DTO转化时,我们会发现,这样的操作有很多很多,那么应该定义好一个接口,让所有这样的操作都有规则的进行。

如果接口被定义以后,那么convertFor这个方法的语义将产生变化,他将是一个实现类。

看一下抽象后的接口:

 
 
 
  1. public interface DTOConvert {
  2.     T convert(S s);
  3. }

虽然这个接口很简单,但是这里告诉我们一个事情,要去使用泛型,如果你是一个优秀的java程序员,请为你想做的抽象接口,做好泛型吧。

我们再来看接口实现:

 
 
 
  1. public class UserInputDTOConvert implements DTOConvert {
  2. @Override
  3. public User convert(UserInputDTO userInputDTO) {
  4. User user = new User();
  5. BeanUtils.copyProperties(userInputDTO,user);
  6. return user;
  7. }
  8. }

我们这样重构后,我们发现现在的代码是如此的简洁,并且那么的规范:

 
 
 
  1. @RequestMapping("/v1/api/user")
  2. @RestController
  3. public class UserApi {
  4.     @Autowired
  5.     private UserService userService;
  6.     @PostMapping
  7.     public User addUser(UserInputDTO userInputDTO){
  8.         User user = new UserInputDTOConvert().convert(userInputDTO);
  9.         return userService.addUser(user);
  10.     }
  11. }

review code

如果你是一个优秀的java程序员,我相信你应该和我一样,已经数次重复review过自己的代码很多次了。

我们再看这个保存用户的例子,你将发现,api中返回值是有些问题的,问题就在于不应该直接返回User实体,因为如果这样的话,就暴露了太多实体相关的信息,这样的返回值是不安全的,所以我们更应该返回一个DTO对象,我们可称它为UserOutputDTO:

 
 
 
  1. @PostMapping
  2. public UserOutputDTO addUser(UserInputDTO userInputDTO){
  3.         User user = new UserInputDTOConvert().convert(userInputDTO);
  4.         User saveUserResult = userService.addUser(user);
  5.         UserOutputDTO result = new UserOutDTOConvert().convertToUser(saveUserResult);
  6.         return result;
  7. }

这样你的api才更健全。

不知道在看完这段代码之后,读者有是否发现还有其他问题的存在,作为一个优秀的java程序员,请看一下这段我们刚刚抽象完的代码:

User user = new UserInputDTOConvert().convert(userInputDTO);

你会发现,new这样一个DTO转化对象是没有必要的,而且每一个转化对象都是由在遇到DTO转化的时候才会出现,那我们应该考虑一下,是否可以将这个类和DTO进行聚合呢,看一下我的聚合结果:

 
 
 
  1. public String getUsername() {
  2.         return username;
  3.     }
  4.     public void setUsername(String username) {
  5.         this.username = username;
  6.     }
  7.     public int getAge() {
  8.         return age;
  9.     }
  10.     public void setAge(int age) {
  11.         this.age = age;
  12.     }
  13.     public User convertToUser(){
  14.         UserInputDTOConvert userInputDTOConvert = new UserInputDTOConvert();
  15.         User convert = userInputDTOConvert.convert(this);
  16.         return convert;
  17.     }
  18.     private static class UserInputDTOConvert implements DTOConvert {
  19.         @Override
  20.         public User convert(UserInputDTO userInputDTO) {
  21.             User user = new User();
  22.             BeanUtils.copyProperties(userInputDTO,user);
  23.             return user;
  24.         }
  25.     }
  26. }

然后api中的转化则由:

 
 
 
  1. User user = new UserInputDTOConvert().convert(userInputDTO);
  2. User saveUserResult = userService.addUser(user);

变成了:

 
 
 
  1. User user = userInputDTO.convertToUser();
  2. User saveUserResult = userService.addUser(user);

我们再DTO对象中添加了转化的行为,我相信这样的操作可以让代码的可读性变得更强,并且是符合语义的。

再查工具类

再来看DTO内部转化的代码,它实现了我们自己定义的DTOConvert接口,但是这样真的就没有问题,不需要再思考了吗?

我觉得并不是,对于Convert这种转化语义来讲,很多工具类中都有这样的定义,这中Convert并不是业务级别上的接口定义,它只是用于普通bean之间转化属性值的普通意义上的接口定义,所以我们应该更多的去读其他含有Convert转化语义的代码。

我仔细阅读了一下GUAVA的源码,发现了com.google.common.base.Convert这样的定义:

 
 
 
  1. public abstract class Converter implements Function {
  2.     protected abstract B doForward(A a);
  3.     protected abstract A doBackward(B b);
  4.     //其他略
  5. }

从源码可以了解到,GUAVA中的Convert可以完成正向转化和逆向转化,继续修改我们DTO中转化的这段代码:

 
 
 
  1. private static class UserInputDTOConvert implements DTOConvert {
  2.         @Override
  3.         public User convert(UserInputDTO userInputDTO) {
  4.                 User user = new User();
  5.                 BeanUtils.copyProperties(userInputDTO,user);
  6.                 return user;
  7.         }
  8. }

修改后:

 
 
 
  1. private static class UserInputDTOConvert extends Converter {
  2.          @Override
  3.          protected User doForward(UserInputDTO userInputDTO) {
  4.                  User user = new User();
  5.                  BeanUtils.copyProperties(userInputDTO,user);
  6.                  return user;
  7.          }
  8.          @Override
  9.          protected UserInputDTO doBackward(User user) {
  10.                  UserInputDTO userInputDTO = new UserInputDTO();
  11.                  BeanUtils.copyProperties(user,userInputDTO);
  12.                  return userInputDTO;
  13.          }
  14.  }

看了这部分代码以后,你可能会问,那逆向转化会有什么用呢?其实我们有很多小的业务需求中,入参和出参是一样的,那么我们变可以轻松的进行转化,我将上边所提到的UserInputDTO和UserOutputDTO都转成UserDTO展示给大家:

DTO:

 
 
 
  1. public class UserDTO {
  2.     private String username;
  3.     private int age;
  4.     public String getUsername() {
  5.             return username;
  6.     }
  7.     public void setUsername(String username) {
  8.             this.username = username;
  9.     }
  10.     public int getAge() {
  11.             return age;
  12.     }
  13.     public void setAge(int age) {
  14.             this.age = age;
  15.     }
  16.     public User convertToUser(){
  17.             UserDTOConvert userDTOConvert = new UserDTOConvert();
  18.             User convert = userDTOConvert.convert(this);
  19.             return convert;
  20.     }
  21.     public UserDTO convertFor(User user){
  22.             UserDTOConvert userDTOConvert = new UserDTOConvert();
  23.             UserDTO convert = userDTOConvert.reverse().convert(user);
  24.             return convert;
  25.     }
  26.     private static class UserDTOConvert extends Converter {
  27.             @Override
  28.             protected User doForward(UserDTO userDTO) {
  29.                     User user = new User();
  30.                     BeanUtils.copyProperties(userDTO,user);
  31.                     return user;
  32.             }
  33.             @Override
  34.             protected UserDTO doBackward(User user) {
  35.                     UserDTO userDTO = new UserDTO();
  36.                     BeanUtils.copyProperties(user,userDTO);
  37.                     return userDTO;
  38.             }
  39.     }
  40. }

api:

 
 
 
  1. @PostMapping
  2.  public UserDTO addUser(UserDTO userDTO){
  3.          User user =  userDTO.convertToUser();
  4.          User saveResultUser = userService.addUser(user);
  5.          UserDTO result = userDTO.convertFor(saveResultUser);
  6.          return result;
  7.  }

当然,上述只是表明了转化方向的正向或逆向,很多业务需求的出参和入参的DTO对象是不同的,那么你需要更明显的告诉程序:逆向是无法调用的:

 
 
 
  1. private static class UserDTOConvert extends Converter {
  2.          @Override
  3.          protected User doForward(UserDTO userDTO) {
  4.                  User user = new User();
  5.                  BeanUtils.copyProperties(userDTO,user);
  6.                  return user;
  7.          }
  8.          @Override
  9.          protected UserDTO doBackward(User user) {
  10.                  throw new AssertionError("不支持逆向转化方法!");
  11.          }
  12.  }

看一下doBackward方法,直接抛出了一个断言异常,而不是业务异常,这段代码告诉代码的调用者,这个方法不是准你调用的,如果你调用,我就”断言”你调用错误了。

关于异常处理的更详细介绍,可以参考我之前的文章:如何优雅的设计java异常 ,应该可以帮你更好的理解异常。

bean的验证

如果你认为我上边写的那个添加用户api写的已经非常完美了,那只能说明你还不是一个优秀的程序员。我们应该保证任何数据的入参到方法体内都是合法的。

为什么要验证

很多人会告诉我,如果这些api是提供给前端进行调用的,前端都会进行验证啊,你为什还要验证?

其实答案是这样的,我从不相信任何调用我api或者方法的人,比如前端验证失败了,或者某些人通过一些特殊的渠道(比如Charles进行抓包),直接将数据传入到我的api,那我仍然进行正常的业务逻辑处理,那么就有可能产生脏数据!

“对于脏数据的产生一定是致命”,这句话希望大家牢记在心,再小的脏数据也有可能让你找几个通宵!

jsr 303验证

hibernate提供的jsr 303实现,我觉得目前仍然是很优秀的,具体如何使用,我不想讲,因为谷歌上你可以搜索出很多答案!

再以上班的api实例进行说明,我们现在对DTO数据进行检查:

 
 
 
  1. public class UserDTO {
  2.     @NotNull
  3.     private String username;
  4.     @NotNull
  5.     private int age;
  6.         //其他代码略
  7. }

api验证:

 
 
 
  1. @PostMapping
  2.     public UserDTO addUser(@Valid UserDTO userDTO){
  3.             User user =  userDTO.convertToUser();
  4.             User saveResultUser = userService.addUser(user);
  5.             UserDTO result = userDTO.convertFor(saveResultUser);
  6.             return result;
  7.     }

我们需要将验证结果传给前端,这种异常应该转化为一个api异常(带有错误码的异常)。

 
 
 
  1. @PostMapping
  2. public UserDTO addUser(@Valid UserDTO userDTO, BindingResult bindingResult){
  3.      checkDTOParams(bindingResult);
  4.      User user =  userDTO.convertToUser();
  5.      User saveResultUser = userService.addUser(user);
  6.      UserDTO result = userDTO.convertFor(saveResultUser);
  7.      return result;
  8. }
  9. private void checkDTOParams(BindingResult bindingResult){
  10.      if(bindingResult.hasErrors()){
  11.              //throw new 带验证码的验证错误异常
  12.      }
  13. }

BindingResult是Spring MVC验证DTO后的一个结果集,可以参考spring 官方文档

检查参数后,可以抛出一个“带验证码的验证错误异常”,具体异常设计可以参考如何优雅的设计java异常

拥抱lombok

上边的DTO代码,已经让我看的很累了,我相信读者也是一样,看到那么多的Getter和Setter方法,太烦躁了,那时候有什么方法可以简化这些呢。

请拥抱lombok,它会帮助我们解决一些让我们很烦躁的问题

去掉Setter和Getter

其实这个标题,我不太想说,因为网上太多,但是因为很多人告诉我,他们根本就不知道lombok的存在,所以为了让读者更好的学习,我愿意写这样一个例子:

 
 
 
  1. @Setter
  2. @Getter
  3. public class UserDTO {
  4.     @NotNull
  5.     private String username;
  6.     @NotNull
  7.     private int age;
  8.     public User convertToUser(){
  9.         UserDTOConvert userDTOConvert = new UserDTOConvert();
  10.         User convert = userDTOConvert.convert(this);
  11.         return convert;
  12.     }
  13.     public UserDTO convertFor(User user){
  14.         UserDTOConvert userDTOConvert = new UserDTOConvert();
  15.         UserDTO convert = userDTOConvert.reverse().convert(user);
  16.         return convert;
  17.     }
  18.     private static class UserDTOConvert extends Converter {
  19.         @Override
  20.         protected User doForward(UserDTO userDTO) {
  21.             User user = new User();
  22.             BeanUtils.copyProperties(userDTO,user);
  23.             return user;
  24.         }
  25.         @Override
  26.         protected UserDTO doBackward(User user) {
  27.             throw new AssertionError("不支持逆向转化方法!");
  28.         }
  29.     }
  30. }

看到了吧,烦人的Getter和Setter方法已经去掉了。

但是上边的例子根本不足以体现lombok的强大。我希望写一些网上很难查到,或者很少人进行说明的lombok的使用以及在使用时程序语义上的说明。

比如:@Data,@AllArgsConstructor,@NoArgsConstructor..这些我就不进行一一说明了,请大家自行查询资料.

bean中的链式风格

什么是链式风格?我来举个例子,看下面这个Student的bean:

 
 
 
  1. public class Student {
  2.     private String name;
  3.     private int age;
  4.     public String getName() {
  5.         return name;
  6.     }
  7.     public Student setName(String name) {
  8.         this.name = name;
  9.         return this;
  10.     }
  11.     public int getAge() {
  12.         return age;
  13.     }
  14.     public Student setAge(int age) {
  15.         return this;
  16.     }
  17. }

仔细看一下set方法,这样的设置便是chain的style,调用的时候,可以这样使用:

 
 
 
  1. Student student = new Student()
  2.         .setAge(24)
  3.         .setName("zs");

相信合理使用这样的链式代码,会更多的程序带来很好的可读性,那看一下如果使用lombok进行改善呢,请使用 @Accessors(chain = true),看如下代码:

 
 
 
  1. @Accessors(chain = true)
  2. @Setter
  3. @Getter
  4. public class Student {
  5.     private String name;
  6.     private int age;
  7. }

这样就完成了一个对于bean来讲很友好的链式操作。

静态构造方法

静态构造方法的语义和简化程度真的高于直接去new一个对象。比如new一个List对象,过去的使用是这样的:

 
 
 
  1. List list = new ArrayList<>();

看一下guava中的创建方式:

 
 
 
  1. List list = Lists.newArrayList();

Lists命名是一种约定(俗话说:约定优于配置),它是指Lists是List这个类的一个工具类,那么使用List的工具类去产生List,这样的语义是不是要比直接new一个子类来的更直接一些呢,答案是肯定的,再比如如果有一个工具类叫做Maps,那你是否想到了创建Map的方法呢:

 
 
 
  1. HashMap objectObjectHashMap = Maps.newHashMap();

好了,如果你理解了我说的语义,那么,你已经向成为java程序员更近了一步了。

再回过头来看刚刚的Student,很多时候,我们去写Student这个bean的时候,他会有一些必输字段,比如Student中的name字段,一般处理的方式是将name字段包装成一个构造方法,只有传入name这样的构造方法,才能创建一个Student对象。

接上上边的静态构造方法和必传参数的构造方法,使用lombok将更改成如下写法(@RequiredArgsConstructor 和 @NonNull):

 
 
 
  1. @Accessors(chain = true)
  2. @Setter
  3. @Getter
  4. @RequiredArgsConstructor(staticName = "ofName")
  5. public class Student {
  6.     @NonNull private String name;
  7.     private int age;
  8. }

测试代码:

 
 
 
  1. Student student = Student.ofName("zs");

这样构建出的bean语义是否要比直接new一个含参的构造方法(包含 name的构造方法)要好很多。

当然,看过很多源码以后,我想相信将静态构造方法ofName换成of会先的更加简洁:

 
 
 
  1. @Accessors(chain = true)
  2. @Setter
  3. @Getter
  4. @RequiredArgsConstructor(staticName = "of")
  5. public class Student {
  6.         @NonNull private String name;
  7.         private int age;
  8. }

测试代码:

 
 
 
  1. Student student = Student.of("zs");

当然他仍然是支持链式调用的:

 
 
 
  1. Student student = Student.of("zs").setAge(24);

这样来写代码,真的很简洁,并且可读性很强。

使用builder

Builder模式我不想再多解释了,读者可以看一下《Head First》(设计模式) 的建造者模式。

今天其实要说的是一种变种的builder模式,那就是构建bean的builder模式,其实主要的思想是带着大家一起看一下lombok给我们带来了什么。

看一下Student这个类的原始builder状态:

 
 
 
  1. public class Student {
  2.     private String name;
  3.     private int age;
  4.     public String getName() {
  5.             return name;
  6.     }
  7.     public void setName(String name) {
  8.             this.name = name;
  9.     }
  10.     public int getAge() {
  11.             return age;
  12.     }
  13.     public void setAge(int age) {
  14.             this.age = age;
  15.     }
  16.     public static Builder builder(){
  17.             return new Builder();
  18.     }
  19.     public static class Builder{
  20.             private String name;
  21.             private int age;
  22.             public Builder name(String name){
  23.                     this.name = name;
  24.                     return this;
  25.             }
  26.             public Builder age(int age){
  27.                     this.age = age;
  28.                     return this;
  29.             }
  30.             public Student build(){
  31.                     Student student = new Student();
  32.                     student.setAge(age);
  33.                     student.setName(name);
  34.                     return student;
  35.             }
  36.     }
  37. }

调用方式:

 
 
 
  1. Student student = Student.builder().name("zs").age(24).build();

这样的builder代码,让我是在恶心难受,于是我打算用lombok重构这段代码:

 
 
 
  1. @Builder
  2. public class Student {
  3.     private String name;
  4.     private int age;
  5. }

调用方式:

 
 
 
  1. Student student = Student.builder().name("zs").age(24).build();

代理模式

正如我们所知的,在程序中调用rest接口是一个常见的行为动作,如果你和我一样使用过spring 的RestTemplate,我相信你会我和一样,对他抛出的非http状态码异常深恶痛绝。

所以我们考虑将RestTemplate最为底层包装器进行包装器模式的设计:

 
 
 
  1. public abstract class FilterRestTemplate implements RestOperations {
  2.         protected volatile RestTemplate restTemplate;
  3.         protected FilterRestTemplate(RestTemplate restTemplate){
  4.                 this.restTemplate = restTemplate;
  5.         }
  6.         //实现RestOperations所有的接口
  7. }

然后再由扩展类对FilterRestTemplate进行包装扩展:

 
 
 
  1. public class ExtractRestTemplate extends FilterRestTemplate {
  2.     private RestTemplate restTemplate;
  3.     public ExtractRestTemplate(RestTemplate restTemplate) {
  4.             super(restTemplate);
  5.             this.restTemplate = restTemplate;
  6.     }
  7.     public  RestResponseDTO postForEntityWithNoException(String url, Object request, Class responseType, Object... uriVariables)
  8.                     throws RestClientException {
  9.             RestResponseDTO restResponseDTO = new RestResponseDTO();
  10.             ResponseEntity tResponseEntity;
  11.             try {
  12.  

    当前标题:细思极恐-你真的会写Java吗?
    网页网址:http://www.hantingmc.com/qtweb/news4/508704.html

    网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

    广告

    声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联