栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

FactoryBean of Spring

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

FactoryBean of Spring

        FactoryBean是spring框架定义的bean工厂,即FactoryBean用于产生另外一个bean。FactoryBean可以用于解决初始化过程较为复杂,通过spring的依赖注入等无法解决的bean的初始化问题。

FactoryBean的定义
public interface FactoryBean {
	String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
	T getObject() throws Exception;
	Class getObjectType();
	default boolean isSingleton() {
		return true;
	}
}

  • 是否单例指示

            如果是单例,首次获取的bean会在FactoryBeanRegistrySupport中进行缓存。

            如果是多例,那么每次获取工厂bean的Object都会生成一个新的bean。

  • 返回目标类型

            该方法用于返回FactoryBean可以生成的目标bean的类型。这样可以避免为了获取bean的类型而进行提前init的问题。

  • 获取目标

          该方法用于实现构造目标bean的逻辑。如果目标bean的构造依赖于其他的bean,那么FactoryBean也可以完成对这些被依赖的bean的注入管理。

            FactoryBean的初始化在执行getObject方法之前,故当执行FactoryBean的getObject方法的时候,FactoryBean应该已经完成了实例化。

           如果在FactoryBean当中注入一个目标bean,那么在FactoryBean的create过程当中会发生循环依赖问题。后面单独说明。

    例子 
    public interface BlueFactoryBeanInterface extends FactoryBean {
    	void asyncLog(BlueBean blueBean);
    }
    @Builder
    @Setter
    @Getter
    @ToString
    public class BlueBean {
    	private String name;
    	private long index;
    }
    @Component
    public class BlueFactoryBean implements BlueFactoryBeanInterface {
    	private static long index = 0;
    	
    	@Autowired
    	private BlueFactoryBeanInterface blueFactoryBean;
    	
    	@Autowired
    	@Lazy
    	private BlueFactoryBeanInterface blueFactoryBeanProxy;
    	
    	//@Autowired
    	//private BlueBean blue;
    	
    	@Autowired
    	@Lazy
    	private BlueBean blueProxy;
    	
    	@Async
    	public void asyncLog(BlueBean blueBean) {
    		System.out.println("asynchrounos log: BlueFactoryBean say hello " + blueBean);
    	}
    	
    	@Override
    	public BlueBean getObject() throws Exception {
    		//build the BlueBean
    		BlueBean bean = BlueBean.builder()
                                    .name("blue")
                                    .index(index++)
                                    .build();
    		System.out.println("getObject " + bean);
    		
    		//print log info in asynchronous way when new object generated.
    		blueFactoryBeanProxy.asyncLog(bean);
    		return bean;
    	}
    
    	@Override
    	public Class getObjectType() {
    		return BlueBean.class;
    	}
    
    	@Override
    	public boolean isSingleton() {
    		return false;
    	}

            我们定义一个FactoryBean接口的实现BlueFactoryBean。这个类能够生产多例BlueBean,每次调用getBean(''blueFactoryBean'')都会返回一个新的BlueBean。同时,在每次产生一个新的bean的时候,都会异步打印一条日志。这个类的组织关系如下图所示。

    为什么保存FactoryBean而不是目标?

            在容器当中保存的是FactoryBean本身,而不是目标。

    blueFactoryBean=org.blue.bluelearning.springproject.factory_bean.BlueFactoryBean@3a8d467e

            FactoryBean有可能根据isSingleton的配置来判断每次是否返回一个新的Object。假设直接保存Object而不是FactoryBean,则无法通过FactoryBean来实现这个功能。单独保存Object也是没有必要的,因为在spring getBean的实现逻辑当中,首次获取到的target object会被缓存在FactoryBeanRegistrySupport。

     org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getObjectFromFactoryBean(FactoryBean, String, boolean)

            因此,如果为多例。则每次的注入点解析,getBean(''blueFactoryBean'')实际上都可以返回一个新的bean。

           总之,通过保存FactoryBean,再通过FactoryBean获取目标bean,这样可以实现更加灵活的bean的生成。

    FactoryBean的获取       
    SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
    @EnableAsync
    public class App {
    	public static void main(String[] args) throws Exception {
    		//基于cglib
    		ConfigurableApplicationContext configurableApplicationContext = SpringApplication.run(App.class, args);
    		BlueBean bean1 = configurableApplicationContext.getBean(BlueBean.class);
    		FactoryBean bean2 =(FactoryBean)configurableApplicationContext.getBean(BlueFactoryBeanInterface.class);
    		FactoryBean bean3 =(FactoryBean)configurableApplicationContext.getBean(BeanFactory.FACTORY_BEAN_PREFIX+"blueFactoryBean");
    		BlueBean bean4 =(BlueBean)configurableApplicationContext.getBean("blueFactoryBean");
    		
    		//FactoryBean bean = configurableApplicationContext.getBean(FactoryBean.class);
    	}
    }

    (一)按照名称获取

            在BeanFactory中保存的bean的类型实际上是一个FactoryBean,并且这个FactoryBean的名字就是@Component中指定的bean名称或者bean class name的首字母缩写,比如本例中的blueFactoryBean。

            如果执行getBean("blueFactoryBean")会得到Object

            如果执行getBean("&blueFactoryBean")会得到FactoryBean本身。

    (二)按照类型获取

            和按照名称获取不同,按照类型获取到的bean的类型就是指定的类型。

            如果执行getBean(BlueFactoryBeanInterface.class)会得到FactoryBean本身。

          如果执行getBean(BlueBean.class)会得到FactoryBean本身。当然,spring会首先获取到BlueFactoryBean,再通过BlueFactoryBean得到BlueBean。

            这里定义了BlueFactoryBeanInterface,是为了便于根据这个类型得到惟一的一个bean,如果根据FactoryBean去查找,会得到多个候选。

    和循环依赖 (一)getObjectForBeanInstance

    AbstractBeanFactory.doGetBean

    从缓存当中获取到目标

    -getSingleton(beanName)

    -getObjectForBeanInstance(sharedInstance, beanName, name)

    OR(新建bean)

    -getSingleton(beanName, ObjectFactory)

    --createBean()

    --addSingleton

    -getObjectForBeanInstance

            可以看到无论是从缓存当中获取到目标,还是新建一个bean(在新建bean的过程当中可能发生循环依赖问题),都是在getSingleton结束之后,才会执行getObjectForBeanInstance。

            从上面可以看到FactoryBean的实例化,和普通bean的实例化是完全相同。自然也可能出现循环依赖问题。在FactoryBean中注入的实例,可以用于完成目标对象的实例化。

            但是getObjectForBeanInstance一般不会导致循环依赖的产生。

    (二)getObject方法

            FactoryBean的getObject方法似乎和循环依赖问题关联不大。不过getObject方法的不合理的定义会产生意想不到的问题。

  • 注入点的解析

          如果要要注入一个BlueBean,那么getBean方法就会执行getObject方法来在BlueFactoryBean的基础上生成一个BlueBean。要在FactoryBean中注入的是一个BlueBean。

          Spring会认为这是循环依赖,因为FactoryBean的非带前缀名称就是BlueBean的名称。

  • getObject的嵌套

          如果在getObject的逻辑当中触发getObject会造成overstack问题。

    public class BlueFactoryBean implements BlueFactoryBeanInterface {
    	private static long index = 0;
    	@Autowired
    	private BlueBean blue;
    	
    	@Async
    	public void asyncLog(BlueBean blueBean) {
    		System.out.println("asynchrounos log: BlueFactoryBean say hello" + blueBean);
    	}
    	
    	@Override
    	public BlueBean getObject() throws Exception {
    		//build the BlueBean
    		BlueBean bean = BlueBean.builder().name("blue").index(index++).build();
    		System.out.println("getObject " + bean);
    		
    		
    		System.out.println("The injected blue's index is: " + blue.getIndex());
    		
    		//print log info in asynchronous way when new object generated.
    		blueFactoryBeanProxy.asyncLog(bean);
    		return bean;
    	}
    }
    (三)在BlueFactoryBean注入一个BlueBean

            如果在BlueFactoryBean当中注入一个BlueBean,则会产生一个循环依赖的报错问题。代码如下。当然循环依赖问题的万能注解@Lazy可以解决这个问题。不过需要具体分析一下报错的原因。

    @Component
    public class BlueFactoryBean implements BlueFactoryBeanInterface {
    	......
    	@Autowired
    	private BlueBean blue;
    	
    	@Async
    	public void asyncLog(BlueBean blueBean) {
    		System.out.println("asynchrounos log: BlueFactoryBean say hello " + blueBean);
    	}
        ......
    }

            在spring的启动阶段会遍历容器所有的单例bean,对他们进行提前的预初始化。BlueFactoryBean就是一个单例。这个单例的名字是blueFactoryBean,因此执行逻辑如下:

            getBean("blueFactoryBean")

            - expose early reference

            - poputeBean

            -- resolve blue

            --- 找到 BlueFactoryBean 可以生产 BlueBean

            --- 执行getBean("blueFactoryBean")

            --- 注入一个BlueBean。

            --- 注册依赖 blueFactoryBean(注入的bean的名字就是BlueBean) -> blueFactoryBean(当前bean的名字)

            -initializeBean

            --对BlueFactoryBean增强为 Proxy(BlueFactoryBean)

            -check

           -- 发现BlueFactoryBean 被增强,并且 他已经提前注册到了另外一个bean里面(就是自己),所以报错。

            我个人认为,这里的检查报错的原因并非BlueFactoryBean的提前注册,而是因为 BlueFactoryBean的getObject方法的提前执行。BlueFactoryBean没有完全完成初始化之前,就进行了目标的生产,并且后续BlueFactoryBean又进行了增强,所以肯定存在隐患。

    (四)再提名字

            由在BlueFactoryBean中注入一个BlueBean的分析过程可以知道,在成功注入一个BlueBean之后,会在BeanFactory当中注册依赖。

    blueFactoryBean(注入的bean的名字就是BlueBean) -> blueFactoryBean(当前bean的名字)

            我个人认为,这个依赖的注册是spring能够检测到循环依赖问题,并且报错的关键。但是问题是,为什么注册了一个BlueBean,但是这里使用的名字却是blueFactoryBean?

            虽然,容器里面注册的是 FactoryBean,甚至名字也是FactoryBean的名字,但是实际上是用于获取一个它的产品。 所以,FactoryBean的名字也理所当然的是它所生产的产品的名字。而他自己的名字需要增 加一个&前缀。而BlueBean在容器当中并没有名字,因为它没有在容器当中直接进行注册,它的名字就是工厂bean的名字,而工厂bean的名字是需要添加&前缀。

    (五)在BlueFactoryBean注入一个BlueFactoryBean

            因为名字FactoryBean自身对应的bean名称的特殊性,在BlueFactoryBean中注入自身并不会出现循环依赖的报错。

    @Component
    public class BlueFactoryBean implements BlueFactoryBeanInterface {
    	......
    	@Autowired
    	private BlueFactoryBean blueFactoryBean;
    	
    	@Async
    	public void asyncLog(BlueBean blueBean) {
    		System.out.println("asynchrounos log: BlueFactoryBean say hello " + blueBean);
    	}
        ......
    }

            虽然为发生报错,但是由于是在create BlueFactoryBean过程当中提前获取的为增强raw bean。所以注入的bean并非最终版本。也是有问题的。下面分析一下为什么没有发生报错问题。

            getBean("blueFactoryBean")

            - expose early reference

            - poputeBean

            -- resolve blueFactoryBean

            --- 执行getBean(BlueFactoryBean.class)

            --- 注入一个BlueFactoryBean。

            --- 注册依赖 &blueFactoryBean(注入的bean的名字就是BlueBean) -> blueFactoryBean(当前bean的名字)

            -initializeBean

            --对BlueFactoryBean增强为 Proxy(BlueFactoryBean)

            -check

            -- 发现BlueFactoryBean 被增强,但是没有发现他已经提前注册到了另外一个bean里面。

            原因是这里注册依赖是

    &blueFactoryBean(注入的bean的名字就是BlueBean) -> blueFactoryBean(当前bean的名字)

            getBean在create过程当中总是认为自己的名字是blueFactoryBean,因为具体判断是返回自身还是产品是在create BlueFactoryBean之后。

    (六)@Lazy

            尽管直接注入BlueFactoryBean或者BlueBean都是有问题的,但是@Lazy这个万能注解可以很好的解决这个问题。

    和三级缓存

            三级缓存用于应对循环依赖问题,而FactoryBean的create过程和其他bean的create过程无异,必然也会和三级缓存相关。

    DefaultSingletonBeanRegistry.getSingleton(String beanName,ObjectFactory singletonFactory)

            -1. objectFactory.getObject():实际为createBean()方法。

            -2. DefaultSingletonBeanRegistry.addSingleton(String, Object)

    org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.addSingleton(String, Object)
    protected void addSingleton(String beanName, Object singletonObject) {
    	synchronized (this.singletonObjects) {
    		this.singletonObjects.put(beanName, singletonObject);
    		this.singletonFactories.remove(beanName);
    		this.earlySingletonObjects.remove(beanName);
    		this.registeredSingletons.add(beanName);
    	}
    }

            可以看到getSingleton执行完之后,单例已经放到了singletonObjects当中,三级缓存的作用域已经结束。所以,getObjectForBeanInstance获取目标target object的过程和三级缓存没有直接关系。但是FactoryBean的create过程还是和三级缓存直接相关的。

  • 转载请注明:文章转载自 www.mshxw.com
    本文地址:https://www.mshxw.com/it/715709.html
    我们一直用心在做
    关于我们 文章归档 网站地图 联系我们

    版权所有 (c)2021-2022 MSHXW.COM

    ICP备案号:晋ICP备2021003244-6号