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是否为同一个?

@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 {
}
@Configuration
@ComponentScan({"com.wangzhen.ch9.controller","com.wangzhen.ch9.service","com.wangzhen.ch9.dao"})
public class Ch9MainConfig {
}
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其他加载方式(除了域加载)

@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接口:


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

参考

  • 1)享学课堂James老师笔记
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,386评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,142评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,704评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,702评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,716评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,573评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,314评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,230评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,680评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,873评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,991评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,706评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,329评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,910评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,038评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,158评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,941评论 2 355