• 如何优雅的使用第三方插件写实体类


    如何优雅的使用第三方插件写实体类

    首先,这里解决一下大家的疑惑,什么是第三方插件?什么是实体类?

    其一:第三方插件是指:Lombok

    其二:实体类是指:大家开发中的 beanpojoentity

    下面一一介绍一下Lombok给编程带来的便捷之处... ...

    1.ORM实体类

    当一个java Bean类作为ORM实体类,或者xml、json的映射类时,需要这个类有这几个特征:

    • 拥有无参构造器;
    • 拥有toString()方法;
    • 拥有setter方法,用以反序列化;
    • 拥有getter方法,用以序列化。

    首先,举例一个最简单的

    package xyz.mrzhangxd.lombokProject;
    
    import lombok.Data;
    
    @Data
    public class UserBean{
      	//用户id
        private Integer userId;
      	//用户名
        private String userName;
      	//用户密码
        private String passWord;
    }
    

    温习一下:@Data注解相当于装配了@Getter @Setter @RequiredArgsConstructor @ToString @EqualsAndHashCode

    2.Builder

    构造器模式,在很多工具类中频繁的使用。

    package xyz.mrzhangxd.lombokProject;
    
    import lombok.Builder;
    
    @Builder
    public class UserBean{
      	//用户id
        private Integer userId;
      	//用户名
        private String userName;
      	//用户密码
        private String passWord;
    }
    

    那么问题来了,@Builder是什么玩意儿,在这里干了啥... ...

    • 它创建了一个 private 的全参构造器。也就意味着 无参构造器没有; 同时也意味着这个类不可以直接构造对象;
    • 它为每一个属性创建了一个同名的方法用于赋值,代替了 setter,而该方法的返回值为对象本身。

    来来来,咱们实践一下:

    package xyz.mrzhangxd.lombokDemo;
    import xyz.mrzhangxd.lombokProject.UserBean;
    
    @Test
    public class lombokDemoTest(){
      	UserBean ub = new UserBean()
      	.userId(1001)
      	.userName("MrZhangxd")
      	.passWord("123456")
      	.build();
      	System.out.println(u);
    }
    

    然而并没啥用,由于这个Bean并没有getter方法,里边的数据没办法直接使用。光说没用,继续执行你会发现输出是这个东西:xyz.mrzhangxd.lombokProject.UserBean@20322d26,连看都看不出是什么东东。因此,Builder提供了一个种可能性,实际使用还需要更多的东西。

    那么,我们为了测试方便需要添加 @ToString() 注解,就会输出 UserBean(userId=1001, userName=MrZhangxd,passWord=123456)

    那么有些小宝贝们不喜欢添加ToString注解,我把他转成json输出:

    package xyz.mrzhangxd.lombokDemo;
    import xyz.mrzhangxd.lombokProject.UserBean;
    
    @Test
    public class lombokDemoTest(){
      	UserBean ub = new UserBean()
      	.userId(1001)
      	.userName("MrZhangxd")
      	.passWord("123456")
      	.build();
      	ObjectMapper om = new ObjectMapper();
      	System.out.println(om.writeValueAsString(u));
    }
    

    很显然,我确实在骗你们,它会出现如下异常:

    ```com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class``` xyz.mrzhangxd.lombokProject.UserBean``` and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS)
    

    那么,是个错误,我们就要拿出我们排查bug的大招了,大招开发上线中... ...

    看到 no properties discovered 了吧,没错,工具类无法找到属性,因为没有 getter,那么我们加上 @Getter 就可以了。

    package xyz.mrzhangxd.lombokProject;
    
    import lombok.Builder;
    import lombok.Getter;
    
    @Builder
    @Getter
    public class UserBean{
      	//用户id
        private Integer userId;
      	//用户名
        private String userName;
      	//用户密码
        private String passWord;
    }
    

    序列化为json可以了,那么从一个json反序列化为对象呢?

    package xyz.mrzhangxd.lombokProject;
    
    import lombok.Builder;
    import lombok.Getter;
    import lombok.Setter;
    
    @Builder
    @Getter
    @Setter
    public class UserBean{
      	//用户id
        private Integer userId;
      	//用户名
        private String userName;
      	//用户密码
        private String passWord;
    }
    

    恭喜小宝贝们中大奖了,我们会遇到 com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of xyz.mrzhangxd.lombokProject.UserBean (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)

    前面已经交代了,只加 @Setter 是不够的,我们还需要一个无参构造器。那么,下面可以吗?

    package xyz.mrzhangxd.lombokProject;
    
    import lombok.Builder;
    import lombok.Data;
    
    
    @Builder
    @Data
    public class UserBean{
      	//用户id
        private Integer userId;
      	//用户名
        private String userName;
      	//用户密码
        private String passWord;
    }
    

    同样不行,因为虽然 Data 使用的时候可以直接使用无参构造器,但由于 Builder 引入了全参构造器,那么按照 java 原生的规则,无参构造器就没有了。那么就再加一个无参构造器

    package xyz.mrzhangxd.lombokProject;
    
    import lombok.Builder;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    @Builder
    @Data
    @NoArgsConstructor
    public class UserBean{
      	//用户id
        private Integer userId;
      	//用户名
        private String userName;
      	//用户密码
        private String passWord;
    }
    

    然而 Builder 又报错了,它找不到全参构造器,最后,绝活上线了 ... ...

    package xyz.mrzhangxd.lombokProject;
    
    import lombok.Builder;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    import lombok.AllArgsConstructor;
    
    @Builder
    @Data
    @NoArgsConstructor
    @AllArgsConstructor(access = AccessLevel.PRIVATE)
    public class UserBean{
      	//用户id
        private Integer userId;
      	//用户名
        private String userName;
      	//用户密码
        private String passWord;
    }
    
    • 注意全参构造器的访问级别,不要破坏 Builder 的规则。

    现在,我们更近一步,看带有List的实体类

    package xyz.mrzhangxd.lombokProject;
    
    import lombok.Builder;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    import lombok.AllArgsConstructor;
    
    @Builder
    @Data
    @NoArgsConstructor
    @AllArgsConstructor(access = AccessLevel.PRIVATE)
    public class UserBean{
      	//用户id
        private Integer userId;
      	//用户名
        private String userName;
      	//用户密码
        private String passWord;
      	//用户地址
      	private List<String> address;
    }
    

    这个 List 我们也需要在外面new 一个 ArrayList,然后 build 进去,使用起来并不舒服。lombok 提供了另一个注解配合使用,那就是 @Singular,如下:

    package xyz.mrzhangxd.lombokProject;
    
    import lombok.Builder;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    import lombok.AllArgsConstructor;
    import lombok.Singular;
    
    @Builder
    @Data
    @NoArgsConstructor
    @AllArgsConstructor(access = AccessLevel.PRIVATE)
    public class UserBean{
      	//用户id
        private Integer userId;
      	//用户名
        private String userName;
      	//用户密码
        private String passWord;
      	//用户地址
      	@Singular
      	private List<String> address;
    }
    

    下面测试一下... ...

    package xyz.mrzhangxd.lombokDemo;
    import xyz.mrzhangxd.lombokProject.UserBean;
    
    @Test
    public class lombokDemoTest(){
      	UserBean ub = new UserBean()
      	.userId(1001)
      	.userName("MrZhangxd")
      	.passWord("123456")
      	.address("beijing")
      	.address("shanghai")
      	.build();
    }
    

    写到这儿,小宝贝们是不是得给我一个赞呀,是不是感觉简单了好多哈,同时还提供了一个 clearXXX方法,清空集合。

    还有一个小坑,如果我们增加一个title属性,然后给它一个默认值:

    package xyz.mrzhangxd.lombokProject;
    import xyz.mrzhangxd.lombokProject.UserBean;
    
    import lombok.Builder;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    import lombok.AllArgsConstructor;
    
    @Builder
    @Data
    @NoArgsConstructor
    @AllArgsConstructor(access = AccessLevel.PRIVATE)
    public class UserBean{
      	//用户id
        private Integer userId;
      	//用户名
        private String userName;
      	//用户密码
        private String passWord;
      	private String title = "爱你么么哒...";
    }
    

    作为一个资深程序员,写到这儿最想干的一件事是什么??你没有错,那就是单元测试,来来来,走一波... ...

    package xyz.mrzhangxd.lombokDemo;
    import xyz.mrzhangxd.lombokProject.UserBean;
    
    @Test
    public class lombokDemoTest(){
      	UserBean ub = new UserBean()
      	.userId(1001)
      	.userName("MrZhangxd")
      	.passWord("123456")
      	.build();
      	System.out.println(ub.toString());
    }
    

    那么,到了激动人心的时候了,下面看看输出结果:

    UserBean(userId=1001,uaerName=MrZhangxd,passWord=123456,title=null)
    

    呀呀呀,不对啊?title怎么会为空呢??这要从Builder的原理来解释,他实际上是分别设置了一套属性列表的值,然后使用全参构造器创建对象。那么,默认值在Bean上,不在Builder上,那么Builder没赋值,它的值就是null,最后把所有属性都复制给UserBean,从而```nul``l覆盖了默认值。

    如何让Builder实体来有默认值呢?只需要给该字段增加 @Default 注解级可。

    package xyz.mrzhangxd.lombokProject;
    
    import lombok.AccessLevel;
    import lombok.Builder;
    import lombok.Builder.Default;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    import lombok.AllArgsConstructor;
    
    @Builder
    @Data
    @NoArgsConstructor
    @AllArgsConstructor(access = AccessLevel.PRIVATE)
    public class UserBean{
      	//用户id
        private Integer userId;
      	//用户名
        private String userName;
      	//用户密码
        private String passWord;
      	@Default
      	private String title = "爱你么么哒...";
    }
    

    心若在,梦就在,只不过是重新再来... ...

    来来来,不灰心,在来测试一下,看看输出结果:

    UserBean(userId=1001,uaerName=MrZhangxd,passWord=123456,title=爱你么么哒...)
    

    3.Wither

    wither方式构建对象,这在Objective-C 中比较多见。

    适用的场景是,使用几个必要的参数构建对象,其他参数,动态的拼装。举个例子,我们构建一个ApiClient,它的用户名和密码是必须的,他的ApiService的地址有一个默认值,然后我们可以自己定制这个地址。

    package xyz.mrzhangxd.lombokProject;
    
    import lombok.AllArgsConstructor;
    import lombok.experimental.Wither;
    
    @Wither
    @AllArgsConstructor
    public class ApiClient {
    	private String appId;
    	private String appKey;
    	private String endpoint="http://api.xxxx.com/xxx";
    }
    

    那么问题来了,怎么用啊

    package xyz.mrzhangxd.lombokDemo;
    import xyz.mrzhangxd.lombokProject.ApiClient;
    
    @Test
    public class lombokDemoTest(){
      	ApiClient aclient = new ApiClient(null,null,null);
      	System.out.println(aclient);
      
      	Object aclient1 = aclient.withAppId("20200107")
    		.withAppKey("MrZhangxd")
    		.withEndpoint("http://192.168.xxx.xxx/");
    	System.out.println(aclient1);
    }
    

    默认的使用 null 去初始化一个对象还是很奇怪的。和 Builder 一样,Wither 也是提供了可能性,实际使用还需要调整一下。

    我们可以设置一个必选参数的构造器,如下:

    package xyz.mrzhangxd.lombokProject;
    
    import lombok.AllArgsConstructor;
    import lombok.NonNull;
    import lombok.RequiredArgsConstructor;
    import lombok.experimental.Wither;
    
    @RequiredArgsConstructor
    @Wither
    @AllArgsConstructor
    public class ApiClient {
    	@NonNull
    	private String appId;
    	@NonNull
    	private String appKey;
    	private String endpoint="http://api.xxxx.com/xxx";
    }
    

    很有自信的告诉小宝贝们,这下可以了... ...

    package xyz.mrzhangxd.lombokDemo;
    import xyz.mrzhangxd.lombokProject.ApiClient;
    
    @Test
    public class lombokDemoTest(){
    	ApiClient aclient=new ApiClient("20200107", "MrZhangxd");
    	System.out.println(aclient);
    		
    	Object aclient1 = aclient.withEndpoint("http://192.168.xxx.xxx/");
    	System.out.println(aclient)1;
    }
    

    告诉博主,是不是优雅了好多好多,而且实际上使用时也使用链式语法:

    ApiClient client1=new ApiClient("20200107", "MrZhangxd").
    	withEndpoint("http://192.168.xxx.xxx/");
    

    另外还有一个小细节,前面的示例输出如下:

    xyz.mrzhangxd.lombokProject.ApiClient@786830e
    xyz.mrzhangxd.lombokProject.ApiClient@470e7030
    

    这个日志表明,with() 返回的对象并不是原来的对象,而是一个新对象,这很重要。

    4.Accessors

    访问器模式,是给一个普通的Bean增加一个便捷的访问器,包括读和写。

    它有两种工作模式,fluent和chain,举例说明:

    package xyz.mrzhangxd.lombokProject;
    
    
    import lombok.Accessors;
    import lombok.Data;
    
    
    @Data
    @Accessors(fluent = true)
    public class UserBean{
      	//用户id
        private Integer userId;
      	//用户名
        private String userName;
      	//用户密码
        private String passWord;
    }
    

    那么问题来了,怎么用啊

    package xyz.mrzhangxd.lombokDemo;
    import xyz.mrzhangxd.lombokProject.UserBean;
    
    @Test
    public class lombokDemoTest(){
      	UserBean ub = new UserBean()
      	.userId(1001)
      	.userName("MrZhangxd")
      	.passWord("123456");
      	ub.setUserName("MrZ")
      	System.out.println(ub.getUserName());
    }
    

    这和 Builder 类似,但更小巧,而且不影响属性的读写,只不过使用属性同名字符串代替了 gettersetter

    另一个模式是 chain:

    package xyz.mrzhangxd.lombokDemo;
    import xyz.mrzhangxd.lombokProject.UserBean;
    
    @Test
    public class lombokDemoTest(){
      	UserBean ub = new UserBean()
      	.setUserId(1001)
      	.setUserName("MrZhangxd")
      	.setPassWord("123456");
      
      	ub.setUserName("MrZ")
      	System.out.println(ub.getUserName());
    }
    

    可以看得出来,这 fluent 的区别就在于使用 gettersetter

  • 相关阅读:
    noip模拟70
    noip模拟测试62
    noip模拟66
    noip模拟67
    noip模拟64
    QATF自动化测试框架
    自动化领域谁主沉浮
    TestComplete9.2增强支持 Embarcadero RAD Studio XE3、Ext JS
    QTP对SAP的支持
    如何将makefile构建的工程导入C++test?
  • 原文地址:https://www.cnblogs.com/MrZhangxd/p/12159865.html
Copyright © 2020-2023  润新知