Spring组件属性赋值、自动装配

Spring需要使用的组件配置pom.xml

1.@Value赋值

  • 使用基本字符串进行赋值
  • 使用SpringEL进行赋值
  • 使用@PropertySource读取配置文件进行赋值

Bird.java三种对属性进行赋值的方法

public class Bird {
    //使用@Value进行赋值:1,基本字符  2,springEL表达式, 3,可以读取 我们的配置文件
    @Value("James")
    private String name;

    @Value("#{20-2}")
    private Integer age;

    @Value("${bird.color}")
    private String color;

配置文件Ch8MainConfig.java

@Configuration
@PropertySource(value="classpath:/test.properties")
public class Ch8MainConfig {
    @Bean
    public Bird bird(){
        return new Bird();
    }
}

属性文件test.properties

bird.color=red

测试文件Ch8Test.java
test.properties里面的属性值是会加载到环境变量中的,所以从ConfigurableEnvironment能获取到该属性值。

public class Ch8Test {
    @Test
    public void test01() {
        AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(Ch8MainConfig.class);
        //从容器中获取所有bean
        String[] names = app.getBeanDefinitionNames();

        for (String name : names) {
            System.out.println(name);
        }
        Bird bird = (Bird) app.getBean("bird");

        System.out.println(bird);
        System.out.println("IOC容器创建完成........");

        ConfigurableEnvironment environment = app.getEnvironment();
        System.out.println("environment====" + environment.getProperty("bird.color"));
        app.close();
    }
}

2.自动装配

  • @Autowired属于spring的, 不能脱离spring
  • @Resource和@Inject都是JAVA规范
    @Inject是需要引入第三方包
  • 推荐大家使用@Autowired
    @Inject与@Autowired的区别如下:
  • @Inject和Autowired一样可以装配bean, 并支持@Primary功能, 可用于非spring框架.
  • @Inject缺点: 但不能支持@Autowired(required = false)的功能,需要引入第三方包javax.inject
    @Resource和Autowired的区别如下:
  • @Resource和Autowired一样可以装配bean
  • @Resource缺点:
    不能支持@Primary功能
    不能支持@Autowired(required = false)的功能

2.1 @Autowired行为

1)比较TestService拿到testDao与直接从容器中拿到的testDao是否为同一个?

  • 三个业务类
    TestController.java
    TestService.java
    TestDao.java
@Controller
public class TestController {
    @Autowired
    private TestService testService;
}
@Service
public class TestService {
    @Autowired
    private TestDao testDao;//如果使用Autowired, testDao2, 找到TestDao类型的

     public void println(){
         System.out.println(testDao);
     }
}
@Repository
public class TestDao {
}
  • 配置类
    Ch9MainConfig.java
@Configuration
@ComponentScan({"com.wangzhen.ch9.controller","com.wangzhen.ch9.service","com.wangzhen.ch9.dao"})
public class Ch9MainConfig {
}
  • 测试类
    Ch9Test.java
public class Ch9Test {
    @Test
    public void test01(){
        AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(Ch9MainConfig.class);
        System.out.println(app);

        TestService testService = app.getBean(TestService.class);
        testService.println();
        //直接从容器中获取TestDao, 和使用Autowired注解来取比较
        TestDao testDao = app.getBean(TestDao.class);
        System.out.println(testDao);

        app.close();
    }
}

测试结果:

com.wangzhen.ch9.dao.TestDao@7ef27d7f
com.wangzhen.ch9.dao.TestDao@7ef27d7f

结果表明:是同一个testDao。
@Autowired默认优先按类型去容器中找对应的组件,相当于app.getBean(TestDao.class)去容器获取id为testDao的bean, 并注入到TestService的bean中;
使用方式如下:

   TestService{
        @Autowired
        private TestDao testDao;//默认去容器中找id为”testDao”的bean
   }

2)@Autowired其他加载方式(除了域加载)

  • 方法加载
    Sun.java
@Component
public class Sun {
    private Moon moon;

    public Sun(Moon moon){
        this.moon = moon;
        System.out.println("..Constructor................");
    }
    public Moon getMoon() {
        return moon;
    }

    @Autowired
    public void setMoon(Moon moon) {
        this.moon = moon;
    }

    @Override
    public String toString() {
        return "Sun [moon=" + moon + "]";
    }
}

测试Ch9Test.java

        Moon moon = (Moon)app.getBean(Moon.class);
        System.out.println(moon);

        Sun sun = (Sun)app.getBean(Sun.class);
        System.out.println(sun.getMoon());

结果如下:

com.wangzhen.ch9.bean.Moon@4b41dd5c
com.wangzhen.ch9.bean.Moon@4b41dd5c
  • 方法内部的使用参数加载
public Sun(@Autowired Moon moon){
        this.moon = moon;
        System.out.println("..Constructor................");
    }

  • 构造方法加载
    @Autowired
    public Sun(Moon moon){
        this.moon = moon;
        System.out.println("..Constructor................");
    }

2.2 定义多个同类对象,加载哪个类对象

  • 业务类做点打印修改
@Service
public class TestService {
    @Autowired
    private TestDao testDao;//如果使用Autowired, testDao2, 找到TestDao类型的

    public void println(){
        System.out.println("Service...dao...."+testDao);
    }
}
@Repository
public class TestDao {
    private String flag = "1";
    public String getFlag() {
        return flag;
    }

    public void setFlag(String flag) {
        this.flag = flag;
    }

    @Override
    public String toString() {
        return "TestDao...... [flag=" + flag + "]";
    }
}
  • 在配置类中增加一个testDao的注入
@Configuration
@ComponentScan({"com.wangzhen.ch9.controller","com.wangzhen.ch9.service","com.wangzhen.ch9.dao"})
public class Ch9MainConfig {
    //spring进行自装配的时候默认首选的bean
    @Bean("testDao")
    public TestDao testDao(){
        TestDao testDao = new TestDao();
        testDao.setFlag("2");
        return testDao;
    }
}

结果是:

Service...dao....TestDao...... [flag=2]
TestDao...... [flag=2]
  • 如果将TestService中的testDao改为testDao2
@Service
public class TestService {
    @Autowired
    private TestDao testDao2;//如果使用Autowired, testDao2, 找到TestDao类型的

    public void println(){
        System.out.println("Service...dao...."+testDao2);
    }
}

结果依然不变,还是:

Service...dao....TestDao...... [flag=2]
TestDao...... [flag=2]

2.3 使用@Qualifier,加载哪个类对象

  • TestService声明@Qualifier("testDao")
@Service
public class TestService {
    @Qualifier("testDao")//指定名称来加载
    @Autowired
    private TestDao testDao2;//如果使用Autowired, testDao2, 找到TestDao类型的

    public void println(){
        System.out.println("Service...dao...."+testDao2);
    }
}
  • 配置文件将注入的Bean改名
@Configuration
@ComponentScan({"com.wangzhen.ch9.controller","com.wangzhen.ch9.service","com.wangzhen.ch9.dao","com.wangzhen.ch9.bean"})
public class Ch9MainConfig {
    //spring进行自装配的时候默认首选的bean
    //@Primary //只要在这里申请Primary, 代表所有要注入TestDao的bean,
    @Bean("testDao2")
    public TestDao testDao(){
        TestDao testDao = new TestDao();
        testDao.setFlag("2");
        return testDao;
    }
}

结果:

Service...dao....TestDao...... [flag=1]

org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.wangzhen.ch9.dao.TestDao' available: expected single matching bean but found 2: testDao,testDao2

如果为以下情况,则报错:
Qualifier为testDao2,而@Bean为testDao

@Service
public class TestService {
    @Qualifier("testDao2")//指定名称来加载
    @Autowired
    private TestDao testDao;//如果使用Autowired, testDao2, 找到TestDao类型的

    public void println(){
        System.out.println("Service...dao...."+testDao);
    }
}
@Configuration
@ComponentScan({"com.wangzhen.ch9.controller","com.wangzhen.ch9.service","com.wangzhen.ch9.dao","com.wangzhen.ch9.bean"})
public class Ch9MainConfig {
    //spring进行自装配的时候默认首选的bean
    @Primary //只要在这里申请Primary, 代表所有要注入TestDao的bean,
    @Bean("testDao")
    public TestDao testDao(){
        TestDao testDao = new TestDao();
        testDao.setFlag("2");
        return testDao;
    }
}

2.4 @Autowired的required属性

  • 注释掉@Repository和@Bean("testDao2"),即容器中没有任何一个testDao
@Service
public class TestService {
    @Qualifier("testDao")//指定名称来加载
    @Autowired(required=false)
    private TestDao testDao2;//如果使用Autowired, testDao2, 找到TestDao类型的

    public void println(){
        System.out.println("Service...dao...."+testDao2);
    }
}

结果:不会报错

Service...dao....null

2.5 @Primary

  • TestService的修改
@Service
public class TestService {
    //@Qualifier("testDao")//指定名称来加载
    @Autowired
    private TestDao testDao;//如果使用Autowired, testDao2, 找到TestDao类型的

    public void println(){
        System.out.println("Service...dao...."+testDao);
    }
}
  • 配置文件加入Primary
@Configuration
@ComponentScan({"com.wangzhen.ch9.controller","com.wangzhen.ch9.service","com.wangzhen.ch9.dao","com.wangzhen.ch9.bean"})
public class Ch9MainConfig {
    //spring进行自装配的时候默认首选的bean
    @Primary //只要在这里申请Primary, 代表所有要注入TestDao的bean,
    @Bean("testDao2")
    public TestDao testDao(){
        TestDao testDao = new TestDao();
        testDao.setFlag("2");
        return testDao;
    }
}

结果:

Service...dao....TestDao...... [flag=2]
TestDao...... [flag=2]

2.6 同时存在@Qualifier和@Primary加载顺序

  • TestService的修改
@Service
public class TestService {
    @Qualifier("testDao")//指定名称来加载
    @Autowired
    private TestDao testDao;//如果使用Autowired, testDao2, 找到TestDao类型的

    public void println(){
        System.out.println("Service...dao...."+testDao);
    }
}
  • 配置文件
@Configuration
@ComponentScan({"com.wangzhen.ch9.controller","com.wangzhen.ch9.service","com.wangzhen.ch9.dao","com.wangzhen.ch9.bean"})
public class Ch9MainConfig {
    //spring进行自装配的时候默认首选的bean
    @Primary //只要在这里申请Primary, 代表所有要注入TestDao的bean,
    @Bean("testDao2")
    public TestDao testDao(){
        TestDao testDao = new TestDao();
        testDao.setFlag("2");
        return testDao;
    }
}

结果:

Service...dao....TestDao...... [flag=1]
TestDao...... [flag=2]

此时不会报错,第二个getBean取得是Primary指定的,而Qualifier取得是另外一个指定名字的。两个同时存在。

2.7 @Resource

  • TestService的修改
@Service
public class TestService {
    //@Qualifier("testDao")//指定名称来加载
    //@Autowired
    @Resource
    private TestDao testDao;//如果使用Autowired, testDao2, 找到TestDao类型的

    public void println(){
        System.out.println("Service...dao...."+testDao);
    }
}

结果:

Service...dao....TestDao...... [flag=1]
TestDao...... [flag=2]

小结:@Resource和Autowired的区别如下:

  • @Resource和Autowired一样可以装配bean
  • @Resource缺点:
    不能支持@Primary功能
    不能支持@Autowired(required = false)的功能

2.8 @Inject

一模一样的用法!

@Inject与@Autowired的区别如下:

  • @Inject和Autowired一样可以装配bean, 并支持@Primary功能, 可用于非spring框架.
  • @Inject缺点: 但不能支持@Autowired(required = false)的功能,需要引入第三方包javax.inject

3.Aware注入Spring底层组件原理

参考Bean生命周期——4.2 ApplicationContextAwareProcessor实现分析
自定义组件想要使用Spring容器底层的组件(ApplicationContext, BeanFactory, ......)
思路: 自定义组件实现xxxAware, 在创建对象的时候, 会调用接口规定的方法注入到相关组件:Aware

查看有哪些接口继承了Aware接口:


Spring组件属性赋值、自动装配_第1张图片

实现ApplicationContextAware、BeanNameAware和EmbeddedValueResolverAware接口:
ApplicationContextAware接口: 获取IOC容器
BeanNameAware接口: 获取Bean信息
EmbeddedValueResolverAware接口: 解析器(表达式及相关脚本解析)

ApplicationContextAware接口获取IOC容器
AnnotationConfigApplicationContext加载配置文件获取的容器

参考Light.java

@Component
public class Light implements ApplicationContextAware, BeanNameAware, EmbeddedValueResolverAware {
    private ApplicationContext applicationContext;

    @Override
    public void setBeanName(String name) {
        System.out.println("当前bean的名字:"+name);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("传入的IOC容器: "+applicationContext);
        this.applicationContext = applicationContext;
    }

    @Override
    public void setEmbeddedValueResolver(StringValueResolver resolver) {
        String result = resolver.resolveStringValue("你好${os.name}, 计算#{3*8}");
        System.out.println("解析的字符串为---"+result);
    }
}

结果:

当前bean的名字:light
解析的字符串为---你好Windows 7, 计算24
传入的IOC容器: org.springframework.context.annotation.AnnotationConfigApplicationContext@7f77e91b: startup date [Mon Sep 10 16:11:54 CST 2018]; root of context hierarchy
Spring组件属性赋值、自动装配_第2张图片

总结:把Spring底层的组件可以注入到自定义的bean中,ApplicationContextAware是利用ApplicationContextAwareProcessor来处理的,其它XXXAware也类似, 都有相关的Processor来处理,其实就是后置处理器来处理。
XXXAware---->功能使用了XXXProcessor来处理的,这就是后置处理器的作用;
ApplicaitonContextAware--->ApplicationContextProcessor后置处理器来处理的

参考

  • 1)享学课堂James老师笔记

你可能感兴趣的:(Spring组件属性赋值、自动装配)