@NoArgsConstructor、@AllArgsConstructor、@RequiredArgsConstructor(搭配@FieldDefaults、@NonFinal)的区别以及在springboot常用地方
注解解释、区别:
@NoArgsConstructor:生成无参的构造方法。
@AllArgsConstructor:生成该类下全部属性的构造方法。(主讲)
@RequiredArgsConstructor:生成该类下被final修饰或者带有@non-null的构造方法。(主讲)
代码解析区别:
1. @AllArgsConstructor
@AllArgsConstructor
public class Student{
private String name;
// 被final修饰
private final String age;
@NonNull
private String sex;
}
根据反编译查看代码(反编译工具jad的使用可点击此处查看):
public class Student
{
public Student(String name, String age, String sex)
{
if(sex == null)
{
throw new NullPointerException("sex is marked non-null but is null");
} else
{
this.name = name;
this.age = age;
this.sex = sex;
return;
}
}
private String name;
private final String age;
private String sex;
}
结果:默认只要是该类下的字段,无论什么修饰,都会被参与构造。
2. @RequiredArgsConstructor
@RequiredArgsConstructor
public class Student
{
private String name;
// 被final修饰
private final String age;
@NonNull
private String sex;
}
根据反编译查看代码:
public class Student
{
public Student(String age, String sex)
{
if(sex == null)
{
throw new NullPointerException("sex is marked non-null but is null");
} else
{
this.age = age;
this.sex = sex;
return;
}
}
private String name;
private final String age;
private String sex;
}
结果:只构造了有final或者@no-null修饰的字段。
场景:
在springboot中,对于一个bean类,注入其他bean的时候,常见的是使用@Autowired,实际上也可以使用构造函数注入,这个时候就可以使用@AllArgsConstructor或者@RequiredArgsConstructor来代替。
场景使用—代替@Autowired注入bean对象:
1. @AllArgsConstructor
@Component
public class BeanTest1
{
}
@Component
public class BeanTest2
{
}
@Component
public class BeanTest3
{
}
//Controller层
@RestController
@ToString
@AllArgsConstructor
public class BeanTestController
{
// 注入三个bean对象,完全没有使用Autowired注解
private BeanTest1 beanTest1;
@NonNull
private BeanTest2 beanTest2;
private final BeanTest3 beanTest3;
}
//测试类
@Component
public class ConstructorRunner implements ApplicationRunner
{
@Autowired
BeanTestController beanTestController;
@Override
public void run(ApplicationArguments args) throws Exception
{
System.out.println(beanTestController);
}
}
结果:完完全全可以注入bean对象。
2. @RequiredArgsConstructor
//Controller层,换成@RequiredArgsConstructor
@RestController
@ToString
@RequiredArgsConstructor
public class BeanTestController
{
// 注入三个bean对象,完全没有使用Autowired注解
private BeanTest1 beanTest1;
@NonNull
private BeanTest2 beanTest2;
private final BeanTest3 beanTest3;
}
结果:由图可以看出,没有被修饰final或者@no-null的属性无法被注入,因此,建议使用@RequiredArgsConstructor的时候,对需要的字段加上final修饰。
注:强调对需要的字段,为什么要强调,请看下面的例子:
3. @RequiredArgsConstructor 与 @AllArgsConstructor 在注入bean上的区别
根据上面两个例子,我们可以看出无论是那种方法都可以注入bean属性对象,只是@RequiredArgsConstructor 是针对有条件的,没有什么区别。
但如果是下面的需求呢:
在该类下,部分字段还需要使用@Value来注入值呢?
使用@AllArgsConstructor
@RestController
@ToString
@AllArgsConstructor
public class BeanTestController
{
// 注入三个bean对象,完全没有使用Autowired注解
private BeanTest1 beanTest1;
@NonNull
private BeanTest2 beanTest2;
private final BeanTest3 beanTest3;
@Value("${constructor.name:hello}")
private String name;
}
出现以下报错:
使用@RequiredArgsConstructor
//Controller层,换成@RequiredArgsConstructor
@RestController
@ToString
@RequiredArgsConstructor
public class BeanTestController
{
// 注入三个bean对象,完全没有使用Autowired注解
private BeanTest1 beanTest1;
@NonNull
private BeanTest2 beanTest2;
private final BeanTest3 beanTest3;
@Value("${constructor.name:hello}")
private String name;
}
结论:
一个bean如果使用构造函数进行bean属性注入,那么当然构造函数不能加上name。
因为加上,在创建ConstructorDemo该bean的时候,需要找类型为String,名字是name的bean对象,当然是不存在,必然会报错。
因此,当然不能使用@AllArgsConstructor了,只能使用@RequiredArgsConstructor
总结:
上面只是举例了代替@Autowired的例子,实际上在json转化为对象,以及在spring中从配置文件读取配置使用@ConfigurationProperties以及@ConstructorBinding的时候,都可以使用构造函数赋值,都可以用到上面的两个@AllArgsConstructor、@RequiredArgsConstructor。
具体:只要记得,那些字段需要赋值,就把它列进构造方法的参数里面即可。
拓展:
@RequiredArgsConstructor搭配@FieldDefaults、@NonFinal。
注解解释:
@FieldDefaults:可以为被注解的类或枚举中的每个字段添加访问修饰符(public, private, 或 protected)。它还可以为注释的类或枚举中的每个字段添加final。
参数介绍:
添加访问修饰符:level=AccessLevel.PRIVATE(其余可填写PUBLIC,MODULE,PROTECTED,PACKAGE,PRIVATE,NONE),默认是NONE
添加final:makeFinal=true(默认是false关闭)。
注意:任何必须保持非final的字段可以用@NonFinal(也在lombok.experimental包中)来注释。
@NonFinal:作用于类、变量,表示变量不加 final。
@RequiredArgsConstructor和@FieldDefaults搭配使用
@RestController
@ToString
@RequiredArgsConstructor
@FieldDefaults(makeFinal = true)
public class BeanTestController
{
private BeanTest1 beanTest1;
private BeanTest2 beanTest2;
private BeanTest3 beanTest3;
}
反编译后代码,可以看出都加了final:
public class BeanTestController
{
public String toString()
{
return (new StringBuilder()).append("BeanTestController(beanTest1=").append(beanTest1).append(", beanTest2=").append(beanTest2).append(", beanTest3=").append(beanTest3).append(")").toString();
}
public BeanTestController(BeanTest1 beanTest1, BeanTest2 beanTest2, BeanTest3 beanTest3)
{
this.beanTest1 = beanTest1;
this.beanTest2 = beanTest2;
this.beanTest3 = beanTest3;
}
private final BeanTest1 beanTest1;
private final BeanTest2 beanTest2;
private final BeanTest3 beanTest3;
}
结果:注入成功
通过上面可以看出,@FieldDefaults(makeFinal = true)会为每个字段添加final,然后@RequiredArgsConstructor注入,但是如果在该类下,部分字段还需要使用@Value来注入值呢?
代码:
@RestController
@ToString
@RequiredArgsConstructor
@FieldDefaults(makeFinal = true)
public class BeanTestController
{
private BeanTest1 beanTest1;
private BeanTest2 beanTest2;
private BeanTest3 beanTest3;
@Value("${constructor.name:hello}")
private String name;
}
反编译后代码:
public class BeanTestController
{
public String toString()
{
return (new StringBuilder()).append("BeanTestController(beanTest1=").append(beanTest1).append(", beanTest2=").append(beanTest2).append(", beanTest3=").append(beanTest3).append(", name=").append(name).append(")").toString();
}
public BeanTestController(BeanTest1 beanTest1, BeanTest2 beanTest2, BeanTest3 beanTest3, String name)
{
this.beanTest1 = beanTest1;
this.beanTest2 = beanTest2;
this.beanTest3 = beanTest3;
this.name = name;
}
private final BeanTest1 beanTest1;
private final BeanTest2 beanTest2;
private final BeanTest3 beanTest3;
private final String name;
}
结果:注入失败,出现报错,
结论:
一个bean如果使用构造函数进行bean属性注入,那么当然构造函数不能加上name。
因为加上,在创建ConstructorDemo该bean的时候,需要找类型为String,名字是name的bean对象,当然是不存在,必然会报错。
因此,要用@NonFinal,不让使用@value注入的值被创建ConstructorDemo该bean时使用
@RequiredArgsConstructor和@FieldDefaults、@NonFinal搭配使用
代码:
@RestController
@ToString
@RequiredArgsConstructor
@FieldDefaults(makeFinal = true)
public class BeanTestController
{
private BeanTest1 beanTest1;
private BeanTest2 beanTest2;
private BeanTest3 beanTest3;
@NonFinal
@Value("${constructor.name:hello}")
private String name;
}
反编译后代码:
public class BeanTestController
{
public String toString()
{
return (new StringBuilder()).append("BeanTestController(beanTest1=").append(beanTest1).append(", beanTest2=").append(beanTest2).append(", beanTest3=").append(beanTest3).append(", name=").append(name).append(")").toString();
}
public BeanTestController(BeanTest1 beanTest1, BeanTest2 beanTest2, BeanTest3 beanTest3)
{
this.beanTest1 = beanTest1;
this.beanTest2 = beanTest2;
this.beanTest3 = beanTest3;
}
private final BeanTest1 beanTest1;
private final BeanTest2 beanTest2;
private final BeanTest3 beanTest3;
private String name;
}
结果:注入成功
标签:RequiredArgsConstructor,String,Spring,private,public,AllArgsConstructor,final,ap From: https://www.cnblogs.com/zhihongShee/p/18215174引用:
https://www.ab62.cn/article/11041.html
https://blog.csdn.net/qq_31635851/article/details/122123278