Spring BeanFactory 与 FactoryBean 解析

Spring BeanFactory 与 FactoryBean 解析

  • BeanFactory和FactoryBean都是接口。

  • 实现BeanFactory接口的类表明此类是一个工厂,配置,新建和管理Spring中Bean对象。在Spring中,BeanFactory是IOC容器的核心接口,它的职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。

  • 实现FactoryBean接口的类表明此类是一个工厂Bean(Spring中有两个Bean,普通Bean和工厂Bean),它也是用来管理Bean的,而它本身由spring管理。

    BeanFactory

    Spring为我们提供了许多易用的BeanFactory实现,XmlBeanFactory就是常用的一个,该实现将以XML方式描述组成应用的对象及对象间的依赖关系。XmlBeanFactory类将持有此XML配置元数据,并用它来构建一个完全可配置的系统或应用。但是在Spring3.1版本已过期,核心BeanFactory:DefaultListableBeanFactory

public class XmlBeanFactory extends DefaultListableBeanFactory {
      private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
      public XmlBeanFactory(Resource resource) throws BeansException {
        this(resource, null);
      }
      public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
        super(parentBeanFactory);
        this.reader.loadBeanDefinitions(resource);
      }
}

从上面可以看出XmlBeanFactory继承了DefaultListableBeanFactory,DefaultListableBeanFactory是Spring注册加载bean的默认实现,它是整个bean加载的核心部分,XmlBeanFactory与它的不同点就是XmlBeanFactory使用了自定义的XML读取器XmlBeanDefinitionReader,实现了自己的BeanDefinitionReader读取。
XmlBeanDefinitionReader主要通过以下三步来加载Spring配置文件中的bean:

  1. 通过继承自AbstractBeanDefinitionReader中的方法,使用ResourceLoader将资源文件(root.xml)路径转换为对应的Resource文件;

  2. 通过DocumentLoader对Resource文件进行转换,将Resource文件转换为Ducument文件;

  3. 通过DefaultBeanDefinitionDocumentReader类对Document进行解析,最后再对解析后的Element进行解析。

BeanFactory factory = new XmlBeanFactory(newClassPathResource("root.xml"));

这里写图片描述

this.reader.loadBeanDefinition(resource)就是资源加载真正的实现,时序图中XmlBeanDefinitionReader加载数据就是在这里完成的。

BeanFactory 定义方法

public interface BeanFactory {
    String FACTORY_BEAN_PREFIX = "&";//返回BeanFactory实例对象标识符
    Object getBean(String name) throws BeansException;
    <T> T getBean(String name, Class<T> requiredType) throws BeansException;
    <T> T getBean(Class<T> requiredType) throws BeansException;
    Object getBean(String name, Object... args) throws BeansException;
    <T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
    boolean containsBean(String name);
    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
    boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
    boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
    boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
    Class<?> getType(String name) throws NoSuchBeanDefinitionException;
    String[] getAliases(String name);
}

FactoryBean

 以Bean结尾,表示它是一个Bean,不同于普通Bean的是:它是实现了FactoryBean接口的Bean,根据该Bean的ID从BeanFactory中获取的实际上是FactoryBean的getObject()返回的对象,而不是FactoryBean本身,如果要获取FactoryBean对象,请在id前面加一个&符号来获取。
 
一个Bean实现FactoryBean接口必须实现以下三个接口

Object getObject() //返回由FactoryBean创建的Bean的实例
boolean isSingleton() //确定由FactoryBean创建的Bean的作用域是singleton还是prototype;
getObjectType() //返回FactoryBean创建的Bean的类型。

如果将一个实现了FactoryBean的类成功配置到了spring上下文中,那么通过该类对象的名称(比如appleFactoryBean)从spring的applicationContext或者beanFactory获取bean时,获取到的是appleFactoryBean创建的apple实例,而不是appleFactoryBean自己,如果想通过spring拿到appleFactoryBean,需要在名称前加 & 符号。

还有一点需要注意,FactoryBean管理的bean实际上也是由spring进行配置、实例化、管理,因此由FactoryBean管理的bean不能再次配置到spring配置文件中(xml、java类配置、注解均不可以),否则会报如下异常

Exception in thread "main" org.springframework.beans.factory.BeanIsNotAFactoryException: Bean named 'appleFactoryBean' is expected to be of type 'org.springframework.beans.factory.FactoryBean' but was actually of type 'java.lang.Object'
    at org.springframework.beans.factory.support.AbstractBeanFactory.getObjectForBeanInstance(AbstractBeanFactory.java:1612)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:317)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:742)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:866)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:542)
    at org.springframework.context.annotation.AnnotationConfigApplicationContext.<init>(AnnotationConfigApplicationContext.java:84)
    at com.joen.testspringcontainer.Start.main(Start.java:11)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)

简单的例子 使用FactoryBean

@Configuration
@ComponentScan
public class Configurations {
//配置类
}
//@Component  这里不可以加注解。会出现上述错误。 
public class AppleBean{
}   

@Component
public class AppleFactoryBean implements FactoryBean{
    public Object getObject() throws Exception {
        return new AppleBean();
    }
    public Class<?> getObjectType() {
        return AppleBean.class;
    }
    public boolean isSingleton() {
        return false;
    }
}
public class Start {
    public static void main(String[] args){
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Configurations.class);
                 out.println(applicationContext.getBean("appleFactoryBean"));//得到的是apple
                 out.println(applicationContext.getBean("&appleFactoryBean"));//得到的是apple工厂
    }
}

FactoryBean其实就是一个简单工厂,实现其方法覆写getObject方法可以直接简易的实现工厂模式.
BeanFactory是Spring的核心接口,说白了其实也是采用了工厂模式,根据传入的不同bean名字,之后调用容器(如DefaultListableBeanFactory)返回具体的bean实例。我们常用的ClassPathXmlApplicationContext以及FileSystemXmlApplicationContext等都实现了此接口。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值