7.4.5 让Bean获取Spring容器

7.4.5 让Bean获取Spring容器

前面介绍的几个示例,都是程序先创建Spring容器,再调用Spring容器的getBean()方法来获取Spring容器中的Bean。在这种访问模式下,程序中总是持有Spring容器的引用。
在某些特殊的情况下,Bean需要实现某个功能(比如该Bean需要输出国际化消息,或者该Bean需要向Spring容器发布事件…),但该功能必须借助于Spring容器才能实现,此时就必须让该Bean获取它所在的Spring容器,然后借助于Spring容器来实现该功能。

如何获取Bean所在的Spring容器

为了让Bean获取它所在的Spring容器,可以让该Bean实现BeanFactoryAware接口。BeanFactoryAware接口里只有一个方法。

方法 描述
setBeanFactory(BeanFactory beanFactory) 该方法有一个参数beanFactory,该参数指向创建它的BeanFactory

大部分初学者看到这个setter方法会感到比较奇怪,因为以前定义一个setter方法之后,该setter方法通常都是由程序员来调用的, setter方法参数由程序员指定;即使使用Spring进行依赖注入时, setter方法参数值也是由程序员通过配置文件来指定的。但此处的这个setter方法比较奇怪,这个方法将由Spring调用, Spring调用该方法时会将Spring容器作为参数传入该方法。与该接口类似的还有BeanNameAwareResourceLoaderAware接口,这些接口里都会提供类似的setter方法,这些方法也由Spring负责调用。
BeanFactoryAware接口类似的有ApplicationContextAware接口,实现该接口的Bean需要实现setApplicationContext(ApplicationContext applicationContex)方法—该方法也不是由程序员负责调用的而是由Spring来调用的。当Spring容器调用该方法时,它会把自身作为参数传入该方法。

程序示例

下面示例假设Person类的sayHi()方法必须能输出国际化消息,由于国际化功能需要借助于Spring容器来实现,因此程序就需要让Person类实现Application ContextAware接口。下面是Person类的源代码。

项目结构

Person.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package org.crazyit.app.service;

import java.util.Locale;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class Person implements ApplicationContextAware
{
// 用成员变量保存它所在的ApplicationContext容器
private ApplicationContext ctx;

/*
* Spring容器会检测容器中所有Bean,如果发现某个Bean实现了ApplicationContextAware接口,
* Spring容器会在创建该Bean之后,自动调用该方法,调用该方法时, 会将容器本身作为参数传给该方法。
*/
public void setApplicationContext(ApplicationContext ctx)
throws BeansException
{
this.ctx = ctx;
}
public void sayHi(String name)
{
System.out.println(ctx.getMessage("hello",
new String[]{name}, Locale.getDefault(Locale.Category.FORMAT)));
}
}

上面的Person类实现了ApplicationContextAware接口,并实现了该接口提供的setApplicationContextAware()方法.
Spring容器会检测容器中所有的Bean,如果发现某个Bean实现了ApplicationContextAware接口,Spring容器会在创建该Bean之后,自动调用该BeansetApplicationContextAware()方法,调用该方法时,会将容器本身作为参数传给该方法——该方法的实现部分将Spring传入的参数(容器本身)赋给该Person对象的ctx实例变量,因此接下来即可通过该ctx实例变量来访问容器本身。
将该Bean部署在Spring容器中,部署该Bean与部署其他Bean没有任何区别。

beans.xml

XML配置文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="GBK"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 加载容器国际化所需要的语言资源文件 -->
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<list>
<value>message</value>
</list>
</property>
<!-- 设置国际化文件编码 -->
<property name="defaultEncoding" value="UTF-8" />
</bean>
<!-- Spring容器会检测容器中所有Bean,如果发现某个Bean实现了ApplicationContextAware接口,
Spring容器会在创建该Bean之后, 自动调用该Bean的setApplicationContext()方法,
调用该方法时, 会将容器本身作为参数传给该方法。 -->
<bean id="person" class="org.crazyit.app.service.Person" />
</beans>

主程序部分进行简单测试,程序先通过实例化的方法来获得ApplicationContext,然后再通过personBean来获得BeanFactory,并将二者进行比较。主程序如下。

SpringTest.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package lee;

import org.crazyit.app.service.Person;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringTest
{
public static void main(String[] args)throws Exception
{
@SuppressWarnings("resource")
ApplicationContext ctx = new
ClassPathXmlApplicationContext("beans.xml");
Person p = ctx.getBean("person" , Person.class);
p.sayHi("孙悟空");
}
}

Bean获取它所在容器的好处

上面程序执行Person对象的sayHi()方法时,该sayHi()方法就自动具有了国际化的功能,而这种国际化的功能实际上是由Spring容器提供的,这就是让Bean获取它所在容器的好处