Source Spring Series - FactoryBean expand the application (with source code parsing)

Foreword

When learning in Spring Core IOC container, you will certainly come into contact with the most basic BeanFactory this Spring's IOC container. This should be the first source Spring we learn when exposed to the class. Spring FactoryBean also present in this class, are very similar on both spelling and frequency are very high. In some Spring face questions I will ask you both what is the difference.

Let me talk conclusion here:

  • BeanFactory: IoC container Spring in all the Spring Bean Factory
  • FactoryBean: a Bean, in a simple Bean, to produce a modified object or object generation plant Bean, its implementation and design similar to the model of the factory mode and the modified mode

In the process of learning source Spring source code and other open source projects and found some FactoryBean framework often used to make integrated in the Spring category, this article describes the specific FactoryBean is simple and practical and specific application development.

What is FactoryBean

There are two types of Spring Bean, one is common Bean, another factory Bean i.e. FactoryBean.

Generally, Spring reflection to specify a class by using the bean class to instantiate bean. In some cases, examples of the bean process is complex, if, it is necessary in the conventional manner <bean>to provide a large amount of configuration information, configuration flexibility is limited, when the coding method may be employed to obtain a simple program. Spring aims to provide a org.Springframework.bean.factory.FactoryBeanplant-based interfaces, a user can implement this interface customization logic instance of the bean . (See some examples of the latter will understand more deeply)

So, when a configuration <bean>process is very complex creation process involves many other bean and complex logic, use xml configuration is difficult, then you can consider using FactoryBean .

Interface definition

package org.springframework.beans.factory;

public interface FactoryBean<T> {
	T getObject() throws Exception;
	
	Class<?> getObjectType();
	
	boolean isSingleton();
}
复制代码

Used on some open source framework

MyBatis-Spring # SqlSessionFactoryBean

    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="***" />
        <property name="configLocation" value="***"/>
        <property name="mapperLocations" value="***"/>
    </bean>
复制代码
public class SqlSessionFactoryBean
    implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
	...
}
复制代码

Ali open source distributed service framework Dubbo # ReferenceBean

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
    xsi:schemaLocation="http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans-4.3.xsd        http://dubbo.apache.org/schema/dubbo        http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
 
    <!-- 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样 -->
    <dubbo:application name="consumer-of-helloworld-app"  />
 
    <!-- 使用multicast广播注册中心暴露发现服务地址 -->
    <dubbo:registry address="multicast://224.5.6.7:1234" />
 
    <!-- 生成远程服务代理,可以和本地bean一样使用demoService -->
    <dubbo:reference id="demoService" interface="org.apache.dubbo.demo.DemoService" />
</beans>
复制代码

<dubbo:referenceBean is a corresponding com.alibaba.dubbo.config.spring.ReferenceBeanclass

public class ReferenceBean<T> extends ReferenceConfig<T> implements FactoryBean, ApplicationContextAware, InitializingBean, DisposableBean {
	...
}
复制代码

Expand practice

Involve

  • ProduceLocation producing region
  • Material Material
  • ProductFactoryBean bean products factory
  • Product Product
  • Boostrap startup class
  • test test-config.xml configuration file

ProduceLocation

@Data
public class ProduceLocation {

    private String locationName;

    private double distanceKm;

    private double pricePerPerKm;
}
复制代码

Material

@Data
public class Material {

    private String name;

    private double pricePerGram;

    private double weight;
}
复制代码

Product

@Data
@Builder
public class Product {

    private Material material;

    private ProduceLocation location;

    private double price;
}
复制代码

ProductFactoryBean

@Setter
@Getter
public class ProductFactoryBean implements FactoryBean<Product> {

    private Material material;

    private ProduceLocation produceLocation;

    @Override
    public Product getObject() throws Exception {
        return Product.builder()
                .location(produceLocation)
                .material(material)
                .price(cal(material, produceLocation))
                .build();
    }

    private double cal(Material material, ProduceLocation produceLocation) {
        return material.getPricePerGram() * material.getWeight()
                + produceLocation.getDistanceKm() * produceLocation.getPricePerPerKm();
    }

    @Override
    public Class<?> getObjectType() {
        return Product.class;
    }

    @Override
    public boolean isSingleton() {
        return false;
    }
}
复制代码

test-config.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-4.0.xsd">

    <bean id="produceLocation" class="base.ioc.FactoryBeanDemoSet.ProduceLocation">
        <property name="locationName" value="杭州"/>
        <property name="pricePerPerKm" value="151.01"/>
        <property name="distanceKm" value="3.1"/>
    </bean>
    <bean id="material" class="base.ioc.FactoryBeanDemoSet.Material">
        <property name="name" value="巧克力豆"/>
        <property name="pricePerGram" value="100"/>
        <property name="weight" value="50"/>
    </bean>
    <bean id="product" class="base.ioc.FactoryBeanDemoSet.ProductFactoryBean">
        <property name="material" ref="material"/>
        <property name="produceLocation" ref="produceLocation"/>
    </bean>
</beans>

复制代码

Boostrap

/**
 * @author Richard_yyf
 * @version 1.0 2019/9/21
 */
public class Bootstrap {

    public static void main(String[] args) throws Exception {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("test-config.xml");
        Product product = (Product) context.getBean("product");
        System.out.println(product.toString());
    }
}
复制代码

Export

Product(material=Material(name=巧克力豆, pricePerGram=100.0, weight=50.0), location=ProduceLocation(locationName=杭州, distanceKm=3.1, pricePerPerKm=151.01), price=5468.131)
复制代码

The configuration described above can of course also use java config way to do.

The above is an example of a simple business, you can also use some open source tools API some packages by FactoryBean, such as the creation of httpClient did some packages, such as time-out, the connection pool size, http and other agents.

characteristic

Given a id=mybeanof FactoryBean, getBean("mybean")get this object instance is created FactoryBean, and getBean("&mybean")get really FactoryBean own object.

According to the demo, run the following code

/**
 * @author Richard_yyf
 * @version 1.0 2019/9/21
 */
public class Bootstrap {

    public static void main(String[] args) throws Exception {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("test-config.xml");
        // Product product = (Product) context.getBean("product");
        // System.out.println(product.toString());
        
        FactoryBean<Product> factoryBean = (ProductFactoryBean) context.getBean("&product");
        System.out.println(factoryBean.getObject().toString());
    }
}
复制代码

Output

Product(material=Material(name=巧克力豆, pricePerGram=100.0, weight=50.0), location=ProduceLocation(locationName=杭州, distanceKm=3.1, pricePerPerKm=151.01), price=5468.131)
复制代码

The corresponding source code

Directly locked to corresponding logical source,

	protected Object getObjectForBeanInstance(
			Object beanInstance, String name, String beanName, RootBeanDefinition mbd) {

		// Don't let calling code try to dereference the factory if the bean isn't a factory.
        // BeanFactoryUtils.isFactoryDereference(name)方法判断name是否以&前缀
		if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
			throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
		}
        
       // Now we have the bean instance, which may be a normal bean or a FactoryBean.
		// If it's a FactoryBean, we use it to create a bean instance, unless the
		// caller actually wants a reference to the factory.
		if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
			return beanInstance;
		}

		Object object = null;
		if (mbd == null) {
			object = getCachedObjectForFactoryBean(beanName);
		}
		if (object == null) {
			// Return bean instance from factory.
			FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
			// Caches object obtained from FactoryBean if it is a singleton.
			if (mbd == null && containsBeanDefinition(beanName)) {
				mbd = getMergedLocalBeanDefinition(beanName);
			}
			boolean synthetic = (mbd != null && mbd.isSynthetic());
			object = getObjectFromFactoryBean(factory, beanName, !synthetic);
		}
		return object;
    }
复制代码
  1. BeanFactoryUtils.isFactoryDereference(name)To determine whether the acquisition FactoryBeanof reference

    	// 不为空且以”&“开头
    	public static boolean isFactoryDereference(String name) {
    		return (name != null && name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
    	}
    	String FACTORY_BEAN_PREFIX = "&";
    复制代码
  2. If the incoming beanInstanceis not FactoryBean, but the caller is used to obtain FactoryBeana reference time, throws BeanIsNotAFactoryExceptionan exception

  3. If the caller is to get FactoryBeanreferences, and beanInstanceis FactoryBean(in front of judge), then directly backbeanInstance

  4. If the incoming beanInstanceordinary bean, direct returnbeanInstance

  5. By coming FactoryBeanto create a corresponding bean

Guess you like

Origin juejin.im/post/5d882369f265da03ec2ea191