其实再写这篇文章之前呢,我是看Spring IOC源码,可是老师啃不动。那么就从外围开始吧。在Spring生命周期的探索当中,其中网上主流的做法就是让我们的Bean实现一大波接口,到目前为止,这些接口的名称一个也没有记住,Spring的这个名称太长啦。其中有这么一个接口BeanFactoryAware。什么意思呢?
在我之前的文章中为大家介绍过,那就是Spring的Bean是无知觉的。类似于Http请求的无状态一般。Bean是要注入在Spring容器当中的,也就是BeanFactory以及ApplicationContext这两个容器。为了使得Bean存在意识感,那么就要实现一些类Aware接口。而Aware接口也恰好是意识、直觉的意思。
下面的代码是我演示实现了这个接口之后的一些操作:
首先我们看看BeanFactoryAware接口的架构:
public interface BeanFactoryAware extends Aware {
void setBeanFactory(BeanFactory beanFactory) throws BeansException;
}
这个Aware接口就是意识的意思。BeanFactoryAware就是让我们的Bean在容器中有存在感。知道老子在炼丹炉中一样。看BeanFactoryAware就一个方法,setBeanFactory,传的参数很简单,就是BeanFactory容器。
public class Car implements BeanFactoryAware{
private String brand;
private String color;
private int maxSpeed;
private BeanFactory beanFactory;
public String getBrand() {
return brand;
}
public void setBrand(final String brand) {
System.out.println("调用Car的setBrand属性");
this.brand = brand;
}
@Override
public void setBeanFactory(final BeanFactory beanFactory) throws BeansException {
System.out.println("调用BeanFactoryAware的setBeanFactory方法--->");
this.beanFactory=beanFactory;
}
@Override public String toString() {
return "Car{" +
"brand='" + brand + '\'' +
", color='" + color + '\'' +
", maxSpeed=" + maxSpeed +
'}';
}
}
上述的Bean很简单,就是三个字段,外加一个BeanFactory接口。实现了setBeanFactory方法。其中的brand设置了setter方法。下面是我的xml配置以及单元测试:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="car" class="com.gosaint.domain.Car"/>
</beans>
@Test
public void testLife(){
ApplicationContext applicationContext=
new ClassPathXmlApplicationContext("classpath:spring/spring-beans.xml");
Car car = (Car) applicationContext.getBean("car");
car.setBrand("奔驰");
System.out.println(car);
}
获取结果如下:
调用BeanFactoryAware的setBeanFactory方法--->
调用Car的setBrand属性
Car{brand='奔驰', color='null', maxSpeed=0}
可以清楚的看到,在调用brand的setter方法之前,是调用了BeanFactoryAware接口的setBeanFactory()方法的。其实上述的代码也很好理解,可能大家要问,什么时候beanFactory实例化的呢。其实在上述的代码中,在通过ClasspathXmlApplicationContext获取容器的时候,读取配置文件,定位到Bean,实例化对象之后就开始讲BeanFactory注入到Car这个Bean当中啦。
但是我这样做只是用来探究这个接口的实际含义,其实Spring是不推荐这样做的,就是不推荐我们的Bean去实现这个接口。一旦我们的bean实现了这个接口,那么Bean就和Spring框架产生了严重的耦合。
思考一个问题,就是我们在平时使用Spring Bean的时候是怎么使用的?是不是一般就是在xml中配置,然后直接使用@Autowired来使用。实现了该接口我们是不是可以直接从Spring容器中获取相关的对象?
@Service
public class BeanFactoryHelper implements BeanFactoryAware {
private BeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
public Object getBean(String beanName){
if(beanFactory == null){
throw new NullPointerException("BeanFactory is null!");
}
return beanFactory.getBean(beanName);
}
}
此时是不是可以直接调用getBean("XXX")去获取呢?我再来设想一个场景:假设我有一个Service的接口,恰好实现了这和接口,就如上述的代码所示;比如说我有2个业务处理对象,他们都继承了我的BeanFactoryHelper对象,但是具体处理的时候要哪家业务的对象呢?这个依赖于用户的选择。 你可以注入2个BeanFactoryHelper实例,然后用if --else来搞,不过那样太坨了。每增加一个业务你都需要改代码。繁琐的操作如下:
public void testLife2(){
ApplicationContext applicationContext=
new ClassPathXmlApplicationContext("classpath:spring/spring-beans.xml");
BeanFactoryHelper helper =
(BeanFactoryHelper) applicationContext.getBean("beanFactoryHelper");
helper=new Demo001();
handler(helper);
}
public void handler(Object obj){
if(obj instanceof Demo001){
//TODO 处理001的业务
}else if(obj instanceof Demo002){
//TODO 处理002的业务
}
}
以后若是变动,还要添加代码。如果实现BeanFactoryAware,那么一切都好说,因为Spring实例化之后,我们只需要根据bean的名称获取对象即可。不会去实例化每一个对象。下面的代码是我之前项目中的一个片段:
1public class ControlServiceImpl implements IControlService, BeanFactoryAware {
2 private BeanFactory factory;
3
4 @Override
5 public void setBeanFactory(BeanFactory factory) {
6 this.factory = factory;
7 }
8 @Override
9 public OutputObject execute(InputObject inputObject) {
10 System.out.println("--------------ControlServiceImpl.execute()方法---------");
11
12 OutputObject outputObject = new OutputObject(ControlConstants.RETURN_CODE.IS_OK);
13 try {
14 outputObject.setReturnCode(ControlConstants.RETURN_CODE.IS_OK);
15 if (inputObject != null) {
16 Object object = factory.getBean(inputObject.getService());
17 Method mth = object.getClass().getMethod(inputObject.getMethod(), InputObject.class, OutputObject.class);
18 mth.invoke(object, inputObject, outputObject);
19 } else {
20 throw new Exception("InputObject can't be null !!!");
21 }
22 } catch (Exception e) {
23 // 异常处理
24
25 } finally {
26
27 }
28 return outputObject;
29 }
30}
作为后台service的统一入口:我实现了这个BeanFactoryAware接口。然后通过factory.getBean(Object)去动态的获取每一个Service。然后反射动态调用service中的方法,这样的实现是不是很好!其实我们要根据自身的业务场景去灵活的解决我们的问题。虽然说这样代码耦合度太高,但是现在那个项目离开Spring还能单独存活呢?当然是有的,可是目前主流开发都是离不开Spring的。
总结:1 实现BeanFacoryAware接口,是在Bean实例化后开始调用,在Setter方法之前调用。2 实现了BeanFacoryAware接口,可以使得bean获取到容器的内部信息,从而进行某些定制化的操作。3 实现了实现了BeanFacoryAware接口,可以使得Bean在容器中有意识,这样的目的是2中所说的。
一个人生气蓬勃的时候决不问为什么生活,只是为生活而生活——为了生活是桩美妙的事而生活。 from 《约翰·克里斯朵夫》