一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第18天,点击查看活动详情。
package org.springframework.beans.factory.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
boolean required() default true;
}
我们通过@Autowired注解的源码可以看出,在@Autowired注解上标注有如下的注解信息。
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
可以看出@Autowired注解不仅可以标注在字段上。也可以标注在构造方法上,实例方法上,参数上。
测试案例准备
接下来,我们在项目中新建一个Dog类,在Doc类中有一个Cat类的引用,并且我们使用@Component注解将Dog类加载到IOC容器中,如下所示。
package com.hanpang.model;
import org.springframework.stereotype.Component;
@Component
public class Dog {
private Cat cat;
public Cat getCat() {
return cat;
}
public void setCat(Cat cat) {
this.cat = cat;
}
@Override
public String toString() {
return "Dog{" + "cat=" + cat + '}';
}
}
配置好之后,我们还需要在AutowiredConfig类的@ComponentScan注解中进行配置,使其能够扫描com.hanpang.controller包下的类,如下所示。
@Configuration
@ComponentScan(value = {
"com.hanpang.dao",
"com.hanpang.service",
"com.hanpang.controller",
"com.hanpang.model"})
public class AutowiredConfig {
}
此时,我们可以直接在Dog类中的cat字段上添加@Autowired注解,使其自动装配。
标注在实例方法上
我们也可以将@Autowired注解标注在setter方法上,如下所示。
@Autowired
public void setCat(Cat cat) {
this.cat = cat;
}
当@Autowired注解标注在方法上时,Spring容器在创建对象的时候,就会调用相应的方法为对象赋值。如果标注的方法存在参数时,则方法使用的参数和自定义类型的值,需要从IOC容器中获取。
接下来,我们将AutowiredTest类的testAutowired01()方法中有关获取和打印PersonService信息的代码注释,新增获取和打印Dog信息的代码,如下所示。
@Test
public void testAutowired01(){
//创建IOC容器
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AutowiredConfig.class);
Dog dog = context.getBean(Dog.class);
System.out.println(dog.toString());
context.close();
}
运行AutowiredTest类的testAutowired01()方法进行测试,可以看到,结果信息中输出了如下一行信息。
Dog{cat=com.hanpang.model.Cat@6a400542}
说明已经获取到cat的信息,可以将@Autowired注解标注在方法上
为了验证最终的输出结果是否是从IOC容器中获取的,我们可以在AutowiredTest类的testAutowired01()方法中直接获取Cat的信息,如下所示。
@Test
public void testAutowired01(){
//创建IOC容器
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AutowiredConfig.class);
Dog dog = context.getBean(Dog.class);
System.out.println(dog.toString());
Cat cat = context.getBean(Cat.class);
System.out.println(cat);
context.close();
}
我们再次运行AutowiredTest类的testAutowired01()方法进行测试,可以在输出的结果信息看到如下两行代码。
Dog{cat=com.hanpang.model.Cat@6a400542}
com.hanpang.model.Cat@6a400542
可以看出在Dog类中通过@Autowired注解获取到的Cat对象和直接从IOC容器中获取到Cat对象是同一个对象。
标注在构造方法上(推荐)
在前面的案例中,我们在Dog类上使用了@Component注解,如下所示。
package com.hanpang.model;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Dog {
private Cat cat;
public Cat getCat() {
return cat;
}
@Autowired
public void setCat(Cat cat) {
this.cat = cat;
}
@Override
public String toString() {
return "Dog{" +
"cat=" + cat +
'}';
}
}
此时,Spring默认加载IOC容器中的组件,IOC容器启动的时候默认会调用bean的无参构造器创建对象,然后再进行初始化赋值等操作。
接下来,我们为Dog类添加一个有参构造方法,然后去除setCat()方法上的@Autowired注解,将@Autowired注解标注在有参构造方法上,并在构造方法中打印信息,如下所示。
package com.hanpang.model;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Dog {
private Cat cat;
@Autowired
public Dog(Cat cat){
this.cat = cat;
System.out.println("调用了Dog的有参构造方法");
}
public Cat getCat() {
return cat;
}
public void setCat(Cat cat) {
this.cat = cat;
}
@Override
public String toString() {
return "Dog{" +
"cat=" + cat +
'}';
}
}
接下来,我们运行AutowiredTest类的testAutowired01()方法进行测试,可以看到输出结果信息中存在如下一行信息。
调用了Dog的有参构造方法
说明IOC容器在启动的时候调用了Dog类的有参构造方法。并且可以从输出的如下两行信息可以看出:通过Dog类的toString()方法打印出的Cat对象和直接从IOC容器中获取的Cat对象是同一个对象。
Dog{cat=com.hanpang.model.Cat@6a400542}
com.hanpang.model.Cat@6a400542
这里,需要大家注意的是:使用@Autowired注解标注在构造方法上时,构造方法中的参数对象也都是从IOC容器中获取的。
标注在参数上
我们也可以将@Autowired注解标注在参数上,例如,在Dog类中我们将构造方法上的@Autowired注解标注在构造方法的参数上,如下所示。
public Dog(@Autowired Cat cat){
this.cat = cat;
System.out.println("调用了Dog的有参构造方法");
}
也可以将@Autowired注解标注在setter方法的参数上,如下所示。
public void setCat(@Autowired Cat cat) {
this.cat = cat;
}
这些效果与标注在字段、实例方法和构造方法上的效果都是一样的。
例如,我们将@Autowired注解标注在构造方法的参数上,运行AutowiredTest类的testAutowired01()方法进行测试,可以看到,输出结果中,同样包含如下三行信息。
调用了Dog的有参构造方法
Dog{cat=com.hanpang.model.Cat@6a400542}
com.hanpang.model.Cat@6a400542
结论:无论Autowired注解标注在字段上、实例方法上、构造方法上还是参数上,都是从IOC容器中获取参数组件的值。
如果Spring的bean只有一个有参构造方法,并且这个有参构造方法只有一个参数,并且这个参数是IOC容器中的对象,当@Autowired注解标注在这个构造方法的参数上时,我们可以将@Autowired注解省略,如下所示。
public Dog(Cat cat){
this.cat = cat;
System.out.println("调用了Dog的有参构造方法");
}
接下来,我们运行AutowiredTest类的testAutowired01()方法进行测试,从输出的结果信息中,可以看出,同样输出了下面的三行信息。
调用了Dog的有参构造方法
Dog{cat=com.hanpang.model.Cat@6a400542}
com.hanpang.model.Cat@6a400542
说明:如果Spring的bean只有一个有参构造方法,并且这个有参构造方法只有一个参数,并且这个参数是IOC容器中的对象,当@Autowired注解标注在这个构造方法的参数上时,我们可以将@Autowired注解省略。
标注在方法位置
@Autowired注解可以标注在某个方法的位置上。这里,为了更好的演示效果,我们新建一个Fish类,在Fish类中有一个Cat类型的成员变量,如下所示。
package com.hanpang.model;
public class Fish {
private Cat cat;
public void setCat(Cat cat) {
this.cat = cat;
}
@Override
public String toString() {
return "Fish{" + "cat=" + cat + '}';
}
}
接下来,我们在AutowiredConfig类中实例化Fish类,如下所示。
@Bean
public Fish fish(){
return new Fish();
}
接下来,我们在AutowiredTest类中创建testAutowired02()方法,如下所示。
@Test
public void testAutowired02(){
//创建IOC容器
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AutowiredConfig.class);
Fish fish = context.getBean(Fish.class);
System.out.println(fish);
context.close();
}
运行testAutowired02()方法,在输出的结果信息中存在如下一行信息。
Fish{cat=null}
说明此时的Fish类中的Cat对象为空。此时,我们可以将Cat对象作为一个参数传递到AutowiredConfig类的fish()方法中,并且将Cat对象设置到Fish中,如下所示。
@Bean
public Fish fish(Cat cat){
Fish fish = new Fish();
fish.setCat(cat);
return fish;
}
当然,我们也可以使用@Autowired注解来标注fish()方法中的cat参数,如下所示。
@Bean
public Fish fish(@Autowired Cat cat){
Fish fish = new Fish();
fish.setCat(cat);
return fish;
}
接下来,我们再次运行testAutowired02()方法,在输出的结果信息中存在如下一行信息。
Fish{cat=com.hanpang.model.Cat@21de60b4}
说明Cat对象被成功创建并设置到了Fish类中。
结论:如果方法只有一个IOC容器中的对象作为参数,当@Autowired注解标注在这个方法的参数上时,我们可以将@Autowired注解省略。
# 为什么Spring不推荐使用@Autowired注解?
在实际工作中,使用
IDEA开发时,很多码友都喜欢使用@Autowired注解进行依赖注入,这个时候 IDEA 就会报黄色警告,代码一片warning,代码洁癖的我不允许这么一个不明不白的警告在这里。@Autowired作为Spring的亲儿子,为啥在IDEA中提示了这么一个警告?
来自官方的推荐,至于原因,百度一下,一对打哪
不建议直接在字段上进行依赖注入。
Spring开发团队建议:在Java Bean中永远使用构造方法进行依赖注入。