你好呀,我是沉默王二,一个和黄家驹一样身高,刘德华一样颜值的程序员(不信围观朋友圈呗)。从 2 位偶像的年纪上,你就可以断定我的码龄至少在 10 年以上,但实话实说,我一直坚信自己只有 18 岁,因为好学使我年轻。本篇文章就打算通过我和三妹对话的形式来聊一聊“Spring 的 Aware、异步编程、计划任务”。
教妹学 Spring,没见过这么放肆的标题吧?“语不惊人死不休”,没错,本篇文章的标题就是这么酷炫,不然你怎么会点进来?
我有一个漂亮如花的妹妹(见上图,怎么又变了?还不能一天做个梦),她叫什么呢?我想聪明的读者能猜得出:沉默王三,没错,年方三六。父母正考虑让她向我学习,做一名正儿八经的 Java 程序员。我一开始是反对的,因为程序员这行业容易掉头发,女生可不适合掉头发。但家命难为啊,与其反对,不如做点更积极的事情,比如说写点有趣的文章教教她。
“二哥,听说今天要学习 Spring 的 Aware、异步编程、计划任务,真的是翘首以盼啊。”
“哎呀,三妹,瞧你那迫不及待的大眼神,就好像昨晚上月亮一样圆,一样大。”
01、Spring Aware
“二哥,据说 Aware 的目的是让 Bean 获取 Spring 容器的服务,你能给我具体说说吗?”
“没问题啊。”
Bean 一般不需要了解容器的状态和直接使用容器,但是在某些情况下,需要在 Bean 中直接对容器进行操作,这时候,就可以通过特定的 Aware 接口来完成。常见的 Spring Aware 接口有下面这些:
Aware 子接口 描述
BeanNameAware 获取容器中 Bean 的名称
BeanFactoryAware Bean 被容器创建以后,会有一个相应的 BeanFactory,可以直接通过它来访问容器
ApplicationContextAware Bean 被初始化后,会被注入到 ApplicationContext,可以直接通过它来访问容器
MessageSourceAware 获取 Message Source 的相关文本信息
ResourceLoaderAware 获取资源加载器,以获取外部资源文件
1)BeanNameAware
新建一个 MyBeanName 类,内容如下:
public class MyBeanName implements BeanNameAware { @Override public void setBeanName(String beanName) { System.out.println(beanName); } }
MyBeanName 实现了 BeanNameAware 接口,并重写了 setBeanName() 方法。beanName 参数表示 Bean 在 Spring 容器中注册的 name。
新建一个 Config 类,内容如下:
@Configuration public class Config { @Bean(name = "myCustomBeanName") public MyBeanName getMyBeanName() { return new MyBeanName(); } }
@Bean 注解用在 getMyBeanName() 方法上,表明当前方法返回一个 Bean 对象(MyBeanName),并通过 name 属性指定 Bean 的名字为“myCustomBeanName”。
新建 BeanNameMain 类,代码如下:
public class BeanNameMain { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BeanNameConfig.class); MyBeanName myBeanName = context.getBean(MyBeanName.class); context.close(); } }
程序输出的结果如下所示:
myCustomBeanName
1
如果把 @Bean() 注解中的 (name = "myCustomBeanName)" 去掉的话,程序输出的内容将会是 BeanNameConfig 类的 getMyBeanName() 的方法名“getMyBeanName”。
2)BeanFactoryAware
新建一个 MyBeanFactory 类,内容如下:
public class MyBeanFactory implements BeanFactoryAware { private BeanFactory beanFactory; @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.beanFactory = beanFactory; } public void getMyBeanName() { MyBeanName myBeanName = beanFactory.getBean(MyBeanName.class); System.out.println(beanFactory.isSingleton("myCustomBeanName")); System.out.println(beanFactory.isSingleton("getMyBeanFactory")); } }
借助 setBeanFactory() 方法,可以将容器中的 BeanFactory 赋值给 MyBeanFactory 类的成员变量 beanFactory,这样就可以在 getMyBeanName() 方法中使用 BeanFactory 了。
通过 getBean() 方法可以获取 Bean 的实例;通过 isSingleton() 方法判断 Bean 是否为一个单例。
在 Config 类中追加 MyBeanFactory 的 Bean:
@Configuration public class Config { @Bean(name = "myCustomBeanName") public MyBeanName getMyBeanName() { return new MyBeanName(); } @Bean public MyBeanFactory getMyBeanFactory() { return new MyBeanFactory(); } }
新建 BeanFactoryMain 类,代码如下:
public class BeanFactoryMain { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class); MyBeanFactory myBeanFactory = context.getBean(MyBeanFactory.class); myBeanFactory.getMyBeanName(); context.close(); } }
初始化 MyBeanFactory 后就可以调用 getMyBeanName() 方法了,程序输出的结果如下所示:
myCustomBeanName
true
true
结果符合我们的预期:MyBeanName 的名字为“myCustomBeanName”,MyBeanName 和 MyBeanFactory 的 scope 都是 singleton。
3)其他几个 Aware 接口就不再举例说明了。通常情况下,不要实现 Aware 接口,因为它会使 Bean 和 Spring 框架耦合。