你了解Spring BeanFactoryAware嘛

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/GoSaint/article/details/85241451

    其实再写这篇文章之前呢,我是看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 《约翰·克里斯朵夫》

猜你喜欢

转载自blog.csdn.net/GoSaint/article/details/85241451