FactoryBean简介:

1)Spring中Bean包含两种一种是普通Bean,另外一种是FactoryBean。它们都受IOC容器管理,但是也有不同之处。

2)普通Bean与FactoryBean的区别:

a)普通的Bean只需要定义好,然后配置文件中定义并配置其依赖关系,就可以通过IOC容器的getBean获取到;

b)FactoryBean是实现了FactoryBean<T>接口的Bean,通过FactoryBean类的getBean方法直接获取到的并不是该FactoryBean实例,而是FactoryBean中方法getObject返回的对象。

c)FactoryBean的实例可以通过其他方法获取到,在通过getBean方法获取实例时在参数name前面加上“&”符号即可。

FactoryBean的接口定义如下:

public interface FactoryBean<T> {
//获取FactoryBean初始化的Bean实例
T getObject() throws Exception;
//获取Bean实例的类型
Class<?> getObjectType();
//判断是否是单例模式
default boolean isSingleton() {
return true;
}
}

示例:

定义Category.java

package com.dx.spring.beans.factorybean;

public class Category {
private Integer id;
private String name; public Integer getId() {
return id;
} public void setId(Integer id) {
this.id = id;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public Category(Integer id, String name) {
super();
this.id = id;
this.name = name;
} @Override
public String toString() {
return "Category [id=" + id + ", name=" + name + "]";
} }

定义CategoryFactoryBean.java

package com.dx.spring.beans.factorybean;

import org.springframework.beans.factory.FactoryBean;

public class CategoryFactoryBean implements FactoryBean<Category> {

    private String name;

    public void setName(String name) {
this.name = name;
} @Override
public Category getObject() throws Exception {
System.out.println(this.name);
return new Category(1, "java spring");
} @Override
public Class<?> getObjectType() {
return Category.class;
} @Override
public boolean isSingleton() {
return FactoryBean.super.isSingleton();
} }

新建spring bean配置文件bean-factorybean.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="category" class="com.dx.spring.beans.factorybean.CategoryFactoryBean">
<property name="name" value="hello factorybean"></property>
</bean>
</beans>

添加测试类Client.java并测试:

package com.dx.spring.beans.factorybean;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; public class Client {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("bean-factorybean.xml");
Category category = (Category) ctx.getBean("category");
System.out.println(category); CategoryFactoryBean categoryFactoryBean = (CategoryFactoryBean) ctx.getBean("&category");
System.out.println(categoryFactoryBean);
}
}

打印信息为:

hello factorybean
Category [id=1, name=java spring]
com.dx.spring.beans.factorybean.CategoryFactoryBean@1e397ed7

实现原理:

声明:以下参考《Spring源码学习--FactoryBean实现原理

当程序执行Category category = (Category) ctx.getBean("category");时,在AbstractBeanFactory中会进行一系列的操作:

getBean获取bean

    @Override
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}

doGetBean中获取bean实例

protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {
.........//省略部分代码
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
.........//省略部分代码
return (T) bean;
}

getObjectForBeanInstance中会选择bean实例是普通的Bean还是FactoryBean,同时通过判断name中是否有&来选择判断是或者FactoryBean还是其getObject方法中获取的bean

protected Object getObjectForBeanInstance(
Object beanInstance, String name, String beanName, RootBeanDefinition mbd) {
.........//省略部分代码
//判断bean类型是否是FactoryBean,或者name是否是以&开头,如果是则直接返回
if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
return beanInstance;
}
//如果是则从getObjectFromFactoryBean中获取
if (object == null) {
// Return bean instance from factory.
FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
if (mbd == null && containsBeanDefinition(beanName)) {
mbd = getMergedLocalBeanDefinition(beanName);
}
boolean synthetic = (mbd != null && mbd.isSynthetic());
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;
}

getObjectFromFactoryBean接下来会执行FactoryBean的getObject方法获取bean了。

protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
.........//省略部分代码
Object object = doGetObjectFromFactoryBean(factory, beanName);
.........//省略部分代码
return object;
} private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName)
throws BeanCreationException {
Object object;
.........//省略部分代码
//调用Factory的getObject方法
object = factory.getObject();
.........//省略部分代码
return object;
}

Spring(十四):使用FactoryBean配置Bean的更多相关文章

  1. 跟着刚哥学习Spring框架--通过注解方式配置Bean(四)

    组件扫描:Spring能够从classpath下自动扫描,侦测和实例化具有特定注解的组件. 特定组件包括: 1.@Component:基本注解,识别一个受Spring管理的组件 2.@Resposit ...

  2. 跟着刚哥学习Spring框架--通过XML方式配置Bean(三)

    Spring配置Bean有两种形式(XML和注解) 今天我们学习通过XML方式配置Bean 1. Bean的配置方式 通过全类名(反射)的方式   √ id:标识容器中的bean.id唯一. √ cl ...

  3. Spring学习记录(十)---使用FactoryBean配置Bean

    之前学了,配置bean可以用普通全类名配置.用工厂方法配置,FactoryBean又是什么呢 有时候配置bean要用到,IOC其他Bean,这时,用FactoryBean配置最合适. FactoryB ...

  4. Spring(二)--FactoryBean、bean的后置处理器、数据库连接池、引用外部文件、使用注解配置bean等

    实验1:配置通过静态工厂方法创建的bean  [通过静态方法提供实例对象,工厂类本身不需要实例化!] 1.创建静态工厂类 public class StaticFactory { private st ...

  5. Spring工厂方法(factory-bean)配置bean

    在spring的世界中, 我们通常会利用bean config file 或者 annotation注解方式来配置bean. 在第一种利用bean config file(spring xml)方式中 ...

  6. Spring 通过FactoryBean配置Bean

    1.实现FactoryBean接口 import org.springframework.beans.factory.FactoryBean; public class CarFactoryBean ...

  7. spring FactoryBean配置Bean

    概要: 实例代码具体解释: 文件夹结构 Car.java package com.coslay.beans.factorybean; public class Car { private String ...

  8. 12.Spring通过FactoryBean配置Bean

    为啥要使用FactoryBean: 在配置Bean的时候,需要用到IOC容器中的其它Bean,这个时候使用FactoryBean配置最合适. public class Car { private St ...

  9. Spring4.0学习笔记(7) —— 通过FactoryBean配置Bean

    1.实现Spring 提供的FactoryBean接口 package com.spring.facoryBean; import org.springframework.beans.factory. ...

随机推荐

  1. 【原】Spring整合Redis(第三篇)—盘点SDR搭建中易出现的错误

    易错点01:Spring版本过低导致的错误[环境参数]Redis版本:redis-2.4.5-win32-win64Spring原来的版本:4.1.7.RELEASESpring修改后的版本:4.2. ...

  2. rails 数据迁移 -migration

    1.创建一个fruits 项目: rails new fruits -d mysql --skip-bundle 2.修改Gemfile: source 'https://gems.ruby-chin ...

  3. FireDAC 下的 Sqlite [4] - 创建数据库

    建立数据库的代码: {建立内存数据库的一般代码:} begin FDConnection1.DriverName := 'SQLite'; //同 FDConnection1.Params.Add(' ...

  4. div光标

    <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"% ...

  5. dp和px,那些不得不吐槽的故事——Android平台图片文字元素单位浅析 (转)

    一个优秀的手机软件,不仅要有精巧的功能,流畅的速度,让人赏心悦目的UI也往往是用户选择的重要理由.作为移动产品的PM,也需要了解一些在UI设计中的基本知识. 1. px和pt,一对好伙伴 在视觉设计中 ...

  6. CoreSight™ Technology

    ARM Cortex-M processor-based devices use the ARM CoreSight technology which introduces powerful new  ...

  7. AES Test vectors

    Table of content List of test vectors for AES/ECB encryption mode AES ECB 128-bit encryption mode AE ...

  8. sql server在执行批处理时出现错误。错误消息为: 目录名无效

    今天在客户服务器上的sql server上执行脚本,报错提示“在执行批处理时出现错误.错误消息为:目录名无效”,第一反应就是客户是不是在服务器装了360,因为之前有类似问题,360把数据库的文件给隔离 ...

  9. 根据twitter的snowflake算法生成唯一ID

    C#版本 /// <summary> /// 根据twitter的snowflake算法生成唯一ID /// snowflake算法 64 位 /// 0---0000000000 000 ...

  10. Java学习笔记八(反射)

    1.介绍 反射为Java程序在执行时提供了动态的能力.利用反射能够在执行时对程序进行动态的控制.本篇博客着重解说一下Java中的反射. 2.Class类的使用 在Java执行过程中,每一个类被载入后都 ...