Spring开发注解-实现方法、构造器位置的自动装配
Autowired源码解析:
可以看出@Autowired注解不仅可以标注在字段上,而且还可以标注在构造方法
CONSTRUCTOR
、实例方法
ElementType.METHOD
以及参数
ElementType.PARAMETER
上。
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
/**
* Declares whether the annotated dependency is required.
* <p>Defaults to {@code true}.
*/
boolean required() default true;
}
练习
新建一个Boss类,在Boss类中有一个Car类的引用,并且我们使用@Component注解将Dog类加载到IOC容器中,如下所示,注意,Car类上也要标注@Component注解,即它也要被加载到IOC容器中
package com.ralph.bean;
import org.springframework.stereotype.Component;
// 默认加在IOC容器中的组件,容器启动会调用无参构造器创建对象,然后再进行初始化、赋值等操作
@Component
public class Boss {
private Car car;
public Boss(Car car) {
this.car = car;
}
public Car getCar() {
return car;
}
public void setCar(Car car) {
this.car = car;
}
@Override
public String toString() {
return "Boss{" +
"car=" + car +
'}';
}
}
新建好以上Boss类之后,我们还需要在MainConfigOfAutowired配置类的@ComponentScan注解中进行配置,使其能够扫描com.ralph.bean包下的类,如下所示。
package com.ralph.config;
import com.ralph.dao.BookDao;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
@Configuration
@ComponentScan({"com.ralph.service","com.ralph.dao","com.ralph.controller","com.ralph.bean"})
public class MainConfigOfAutowired {
@Primary
@Bean("bookDao2")
public BookDao bookDao() {
BookDao bookDao = new BookDao();
bookDao.setLable("2");
return bookDao;
}
}
让set方法通过自动装配,我们可以将@Autowired注解标注在setter方法上
@Autowired
public void setCar(Car car) {
this.car = car;
}
当@Autowired注解标注在方法上面,spring容器在创建当前对象的时候,就会调用相应的方法为对象赋值,如果标注的方法存在参数时,那么方法使用的参数和自定义类型的值,需要从IOC容器中获取
测试:
@Test
public void testForAutoWiredMethod(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfAutowired.class);
Boss boss = applicationContext.getBean(Boss.class);
System.out.println(boss);
applicationContext.close();
}
OUTPUT:
Boss{car=com.ralph.bean.Car@4b5d6a01}
说明已经获取到了car的信息,也就是说可以将@Autowired注解标注在方法上。
为了验证最终的输出结果是否是从IOC容器中获取的,我们可以在Test类的方法中直接获取Car对象的信息
@Test
public void testForAutoWiredMethod(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfAutowired.class);
Boss boss = applicationContext.getBean(Boss.class);
System.out.println(boss);
Car bean = applicationContext.getBean(Car.class);
System.out.println(bean);
applicationContext.close();
}
OUTPUT: 可以看出两个内存地址是一样的
Boss{car=com.ralph.bean.Car@48e4374}
com.ralph.bean.Car@48e4374
这已然说明在Boss类中通过@Autowired注解获取到的Car对象和直接从IOC容器中获取到Car对象是同一个对象。
标注在构造方法上
在上面自动装配用在了普通方法上面 我们在boss类使用了@Component 注解,此时Spring会默认将该类加载到IOC容器中,ioc容器启动的时候会默认调用bean无参构造器创建对象,然后依次初始化、赋值
其实,我们为Boss类添加一个有参构造方法,然后去除setCar()方法上的@Autowired注解,将@Autowired注解标注在有参构造方法上,并在构造方法中打印一条信息
@Test
public void testForAutoWiredMethod(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfAutowired.class);
Boss boss = applicationContext.getBean(Boss.class);
System.out.println(boss);
Car bean = applicationContext.getBean(Car.class);
System.out.println(bean);
applicationContext.close();
}
说明IOC容器在启动的时候调用了Boss类的有参构造方法。并且还可以从输出的如下两行信息中看出,通过Boss类的toString()方法打印出的Car对象和直接从IOC容器中获取的Car对象是同一个对象。
OUTPUT:
This is Boss have params constructor
Boss{car=com.ralph.bean.Car@9353778}
com.ralph.bean.Car@9353778
-
使用@Autowired注解标注在构造方法上面的时候,构造方法中的参数对象也是从IOC容器中获取的
this.car = car;
-
使用@Autowired注解标注在构造方法上时,如果组件中只有一个有参构造方法,那么这个有参构造方法上的@Autowired注解可以省略,并且参数位置的组件还是可以自动从IOC容器中获取。
标注在参数上
我们也可以将@Autowired注解标注在参数上,例如,在Boss类中我们将构造方法上的@Autowired注解标注在构造方法的参数上,如下所示。
public Boss(@Autowired Car car) {
this.car = car;
System.out.println("Boss...有参构造器");
}
or
public void setCar(@Autowired Car car) {
this.car = car;
}
最终的效果与标注在字段、实例方法和构造方法上的效果都是一样的
于是,我们可以得出结论:
无论@Autowired注解是标注在字段上、实例方法上、构造方法上还是参数上,参数位置的组件都是从IOC容器中获取。
如果Spring的bean中只有一个有参构造方法,并且这个有参构造方法只有一个参数,这个参数还是IOC容器中的对象,当@Autowired注解标注在这个构造方法的参数上时,那么我们可以将其省略掉,如下所示。
public Boss(/*@Autowired*/ Car car) {
this.car = car;
System.out.println("Boss...有参构造器");
}
从输出的结果信息中可以看出,同样输出了下面的三行信息。
This is Boss have params constructor
Boss{car=com.ralph.bean.Car@7e0b85f9}
com.ralph.bean.Car@7e0b85f9
标注在方法位置
创建一个Color类
package com.ralph.bean;
public class Color {
public Car car;
public Car getCar() {
return car;
}
public void setCar(Car car) {
this.car = car;
}
@Override
public String toString() {
return "Color{" +
"car=" + car +
'}';
}
}
然后,我们在MainConfigOfAutowired配置类中实例化Color类,如下所示。
@Bean
public Color color() {
Color color = new Color();
return color;
}
在测试类中输出Color对象
@Test
public void testForAutoWired(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfAutowired.class);
Color bean = applicationContext.getBean(Color.class);
System.out.println(bean);
applicationContext.close();
}
OUTPUT:
Color{car=null}
说明此时的Color对象中的Car对象为空。此时,我们可以将Car对象作为一个参数传递到MainConfigOfAutowired配置类的color()方法中,并且将该Car对象设置到Color对象中,如下所示
@Bean
public Color color(@Autowired Car car){
Color color = new Color();
color.setCar(car);
return color;
}
再次运行测试类:
OUTPUT:
Color{car=com.ralph.bean.Car@31d7b7bf}
说明Car对象被成功创建并设置到Color对象中了。
至此,我们可以得出结论:
如果方法只有一个IOC容器中的对象作为参数,当@Autowired注解标注在这个方法的参数上时,我们可以将@Autowired注解省略掉。也就说@Bean注解标注的方法在创建对象的时候,方法参数的值是从IOC容器中获取的,此外,标注在这个方法的参数上的@Autowired注解可以省略。
其实,我们用到最多的还是把@Autowired注解标注在方法位置,即使用@Bean注解+方法参数这种形式,此时,该方法参数的值从IOC容器中获取,并且还可以默认不写@Autowired注解,因为效果都是一样的,都能实现自动装配!