spring源码-FactoryBean入门
参考网址:
https://mp.weixin.qq.com/s/Q_dvZEM1FjRnLt1avy8BFw
方法介绍
共有3个方法需要实现:
(1)getObject
():从工厂中获取bean
(2)getObjectType
():获取Bean工厂创建的对象的类型,该方法返回的类型是在ioc容器中getbean所匹配的类型。
(3)isSingleton
():Bean工厂创建的对象是否是单例模式。
FactoryBean.java
package org.springframework.beans.factory;
import org.springframework.lang.Nullable;
public interface FactoryBean<T> {
String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
@Nullable
T getObject() throws Exception;
@Nullable
Class<?> getObjectType();
default boolean isSingleton() {
return true;
}
}
FactoryBean作用
一般情况下,Spring通过反射机制利用的class属性指定实现类实例化Bean。至于为什么会有FactoryBean?原因有两个:
(1)在某些情况下,实例化Bean过程比较复杂,如果按照传统的方式,则需要提供大量的配置信息。配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。Spring为此提供了一个org.springframework.bean.factory.FactoryBean的工厂类接口,用户可以通过实现该接口定制实例化Bean的逻辑。
FactoryBean接口对于Spring框架来说占用重要的地位,Spring自身就提供了70多个FactoryBean的实现。它们隐藏了实例化一些复杂Bean的细节,给上层应用带来了便利。
(2)由于第三方库不能直接注册到spring容器,于是可以实现org.springframework.bean.factory.FactoryBean接口,然后给出自己对象的实例化代码即可。
FactoryBean的应用
Mybatis中的SqlSessionFactoryBean。阿里开源的分布式服务框架 Dubbo中的Consumer 也使用到了FactoryBean
示意图
FactoryBean的基本使用
新建springboot项目
引入相关依赖
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.7.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>springboot-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
创建一个普通的实体类
User.java
package com.example;
public class User {
private Integer id;
private String userName;
public User() {
System.out.println("com.example.User 的 构造方法");
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
}
构建一个FactoryBean
对于 FactoryBean 的使用很简单,只需要实现接口 UserFactoryBean:
UserFacotryBean.java
package com.example;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.stereotype.Component;
@Component("user")
public class UserFactoryBean implements FactoryBean<User> {
@Override
public User getObject() throws Exception {
User user = new User();
user.setId(1);
user.setUserName("自定义 userName");
return user;
}
@Override
public Class<?> getObjectType() {
return User.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
测试例子
在启动类上测试
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.ApplicationContext;
@SpringBootApplication
public class SpringbootDemoApplication {
public static void main(String[] args) {
ApplicationContext ac = SpringApplication.run(SpringbootDemoApplication.class, args);
User user1 = ac.getBean(User.class);
User user2 = ac.getBean(User.class);
System.out.println(user1);
System.out.println(user2);
System.out.println("use是否是单例 : "+ (user1==user2));
UserFactoryBean userFactoryBean = ac.getBean("&user", UserFactoryBean.class);
System.out.println(userFactoryBean);
}
}
控制台打印
com.example.User 的 构造方法
com.example.User@4d7e7435
com.example.User@4d7e7435
use是否是单例 : true
com.example.UserFactoryBean@4a1e3ac1
说明:
(1)如果不加&返回的是实例,加了返回的是工厂bean本身。
(2)如果不执行获取实例的方法,也就是不执行ctx.getBean(Computer.class)获取实例方法,是不会构建Computer实例的,也就不会执行构造方法的。获取工厂Bean的话,也是不会实例的创建。
来看下通过beanName获取FactoryBean创建的对象:
(1)移除name变量的&前缀,用一个beanName变量接收(这次没有&前缀,name和beanName是一样的)然后从容器的缓存中取(FactoryBean本身),取到后走getObjectForBeanInstance,该方法需要将从容器中拿到的FactoryBean本身、原始参数name和beanName都要传过去。
(2)判断name是不是以&为前缀,不是的话,创建一个Object对象,用来接收FactoryBean创建出来的Bean对象。
(3)从工厂的factoryBeanObjectCached(FactoryBean创建Bean的缓存)缓存中取,有的话直接返回(第一次获取肯定没有),没有通过FactoryBean#getObject方法进行创建流程。
anInstance,该方法需要将从容器中拿到的FactoryBean本身、原始参数name和beanName都要传过去。
(2)判断name是不是以&为前缀,不是的话,创建一个Object对象,用来接收FactoryBean创建出来的Bean对象。
(3)从工厂的factoryBeanObjectCached(FactoryBean创建Bean的缓存)缓存中取,有的话直接返回(第一次获取肯定没有),没有通过FactoryBean#getObject方法进行创建流程。
(4)判断该FactoryBean#isSingleton是单例还是原型对象。然后通过FactoryBean#getObject创建一个Bean出来,如果是单例对象,放入factoryBeanObjectCached缓存中,如果是原型,不存缓存中。最后直接返回。