preface
While learning about the IOC container in Spring Core, you will definitely come into contact with the BeanFactory, the most basic IOC container in Spring. This should be the first class you will encounter when learning Spring source code. Spring also has the FactoryBean class, which is spelled very similar and used very often. Some Spring questions will also ask you what the difference is between the two.
Here’s the conclusion:
- BeanFactory: The IoC container in Spring, the Factory of all Spring beans
- FactoryBean: a Bean, not a simple Bean, a FactoryBean that can generate objects or modify object generation. Its implementation is similar to that of the factory and decorator patterns in the design pattern
In the process of learning the source code of Spring and other open source projects, I found that FactoryBean is a class that some frameworks often use when integrating Spring. This paper specifically describes the simplicity, utility and specific application expansion of FactoryBean.
What is FactoryBean
There are two types of beans in Spring, ordinary beans and factory beans, or factoryBeans.
In general, Spring instantiates a bean through reflection using the bean’s class attribute to specify the implementation class. In some cases, the process of instantiating beans is complex, and the traditional way of providing a lot of configuration information in
is limited by the flexibility of configuration, so coding may yield a simple solution. Spring provides a org. Springframework. Beans. Factory. FactoryBean factory class interface, users can customize by implementing the interface logic instantiation of the bean. (See some examples below for more insight)
So, when the process of configuring a
is very complex, and the creation process involves many other beans and complex logic, it is difficult to configure with XML, so consider using FactoryBeans.
The interface definition
package org.springframework.beans.factory; public interface FactoryBean<T> { T getObject() throws Exception; Class<? > getObjectType(); boolean isSingleton(); }Copy the code
Use on some open source frameworks
MyBatis-Spring # SqlSessionFactoryBean
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="* * *" />
<property name="configLocation" value="* * *"/>
<property name="mapperLocations" value="* * *"/>
</bean>
Copy the code
public class SqlSessionFactoryBean
implements FactoryBean<SqlSessionFactory>, InitializingBean.ApplicationListener<ApplicationEvent> {... }Copy the code
Alibaba’s 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">
<! -- Consumer application name, used to calculate dependencies, not matching conditions, not the same as provider -->
<dubbo:application name="consumer-of-helloworld-app" />
<! Expose discovery service addresses with multicast broadcast registries -->
<dubbo:registry address=Multicast: / / 224.5.6.7: "1234" />
<! Create a remote service proxy that can use demoService as a local bean -->
<dubbo:reference id="demoService" interface="org.apache.dubbo.demo.DemoService" />
</beans>
Copy the code
< dubbo: reference corresponding beans are com. Alibaba. The dubbo. Config. Spring. ReferenceBean class
public class ReferenceBean<T> extends ReferenceConfig<T> implements FactoryBean.ApplicationContextAware.InitializingBean.DisposableBean {... }Copy the code
Development practices
involving
- ProduceLocation Production location
- Material Material
- ProductFactoryBean ProductFactoryBean
- The Product Product
- Boostrap start class
- Test-config. XML tests the configuration file
ProduceLocation
@Data
public class ProduceLocation {
private String locationName;
private double distanceKm;
private double pricePerPerKm;
}
Copy the code
Material
@Data
public class Material {
private String name;
private double pricePerGram;
private double weight;
}
Copy the code
Product
@Data
@Builder
public class Product {
private Material material;
private ProduceLocation location;
private double price;
}
Copy the code
ProductFactoryBean
@Setter
@Getter
public class ProductFactoryBean implements FactoryBean<Product> {
private Material material;
private ProduceLocation produceLocation;
@Override
public Product getObject(a) 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
publicClass<? > getObjectType() {return Product.class;
}
@Override
public boolean isSingleton(a) {
return false; }}Copy the code
test-config.xml
<? The 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. The ioc. FactoryBeanDemoSet. ProduceLocation" > < property name = "locationName" value = "hangzhou" / > < property Name ="pricePerPerKm" value="151.01"/> <property name="distanceKm" value="3.1"/> </bean> < id="material" Class = "base. The ioc. FactoryBeanDemoSet. Material" > < property name = "name" value = "chocolate" / > < 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>Copy the code
Boostrap
/ * * *@author Richard_yyf
* @version1.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()); }}Copy the code
The output
Product(Material = material (name= chocolate bean, pricePerGram=100.0, weight=50.0), location=ProduceLocation(locationName= hangzhou, DistanceKm = 3.1, pricePerPerKm = 151.01), the price = 5468.131)Copy the code
The above configuration can also be done in Java Config mode.
As an example of a simple business, You can also use FactoryBean to encapsulate the API usage of open source tools, such as httpClient creation processes such as timeouts, connection pool sizes, HTTP proxies, and so on.
features
Given a FactoryBean with id= MyBean, getBean(” myBean “) gets the object instance created by the FactoryBean, and getBean(“& MyBean “) gets the actual object of the FactoryBean itself.
Based on the demo above, run the following code
/ * * *@author Richard_yyf
* @version1.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()); }}Copy the code
Output
Product(Material = material (name= chocolate bean, pricePerGram=100.0, weight=50.0), location=ProduceLocation(locationName= hangzhou, DistanceKm = 3.1, pricePerPerKm = 151.01), the price = 5468.131)Copy the code
Corresponding source
First directly lock the corresponding logical source code,
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) method to judge whether the name prefixed by &
if(BeanFactoryUtils.isFactoryDereference(name) && ! (beanInstanceinstanceof 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(! (beanInstanceinstanceof 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);
}
booleansynthetic = (mbd ! =null&& mbd.isSynthetic()); object = getObjectFromFactoryBean(factory, beanName, ! synthetic); }return object;
}
Copy the code
-
BeanFactoryUtils. IsFactoryDereference (name) determine whether in obtaining FactoryBean references
// Is not empty and begins with "&" public static boolean isFactoryDereference(String name) { return(name ! =null && name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)); } String FACTORY_BEAN_PREFIX = "&"; Copy the code
-
BeanInstance not FactoryBean if came in, but the caller is used to obtain FactoryBean references, throw BeanIsNotAFactoryException anomalies
-
If the caller is getting a reference to a FactoryBean, and beanInstance is a FactoryBean, return beanInstance directly
-
If the incoming beanInstance is a plain bean, return beanInstance directly
-
Create a corresponding bean from the incoming FactoryBean