掃描下方二維碼或者微信搜索公衆號
菜鳥飛呀飛
,便可關注微信公衆號,閱讀更多Spring源碼分析文章html
首先須要說明的是,FactoryBean和BeanFactory雖然名字很像,可是這二者是徹底不一樣的兩個概念,用途上也是天差地別。BeanFactory是一個Bean工廠,在必定程度上咱們能夠簡單理解爲它就是咱們日常所說的Spring容器(注意這裏說的是簡單理解爲容器),它完成了Bean的建立、自動裝配等過程,存儲了建立完成的單例Bean。而FactoryBean經過名字看,咱們能夠猜出它是Bean,但它是一個特殊的Bean,究竟有什麼特殊之處呢?它的特殊之處在咱們平時開發過程當中又有什麼用處呢?java
FactoryBean的特殊之處在於它能夠向容器中註冊兩個Bean,一個是它自己,一個是FactoryBean.getObject()方法返回值所表明的Bean。先經過以下示例代碼來感覺下FactoryBean的用處吧。spring
package com.tiantang.study.component;
import com.tiantang.study.service.UserService;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.stereotype.Component;
@Component
public class CustomerFactoryBean implements FactoryBean<UserService> {
@Override
public UserService getObject() throws Exception {
return new UserService();
}
@Override
public Class<?> getObjectType() {
return UserService.class;
}
}
複製代碼
package com.tiantang.study.service;
public class UserService {
public UserService(){
System.out.println("userService construct");
}
}
複製代碼
com.tiantang.study.component
package com.tiantang.study.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("com.tiantang.study.component")
public class AppConfig {
}
複製代碼
public class MainApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
System.out.println("容器啓動完成");
UserService userService = applicationContext.getBean(UserService.class);
System.out.println(userService);
Object customerFactoryBean = applicationContext.getBean("customerFactoryBean");
System.out.println(customerFactoryBean);
}
}
複製代碼
com.tiantang.study.component
這個包下的類,按照咱們的常規理解,這個時候應該只會有CustomerFactoryBean這個類被放進Spring容器中了,UserService並無被掃描。而咱們在測試時卻能夠經過applicationContext.getBean(UserService.class)
從容器中獲取到Bean,爲何?CustomerFactoryBean
類的單例對象在容器中的beanName是customerFactoryBean。因此這個時候咱們調用方法getBean(beanName)經過beanName去獲取Bean,這個時候理論上應該返回的是CustomerFactoryBean類的單例對象。然而,咱們將結果打印出來,卻發現,這個對象的hashCode居然和userService對象的hashCode如出一轍,這說明這兩個對象是同一個對象,爲何會出現這種狀況呢?爲何不是CustomerFactoryBean類的實例對象呢?以上3個問題的答案能夠用一個答案解決,那就是FactoryBean是一個特殊的Bean。咱們自定義的CustomerFactoryBean實現了FactoryBean接口,因此當CustomerFactoryBean被掃描進Spring容器時,實際上它向容器中註冊了兩個bean,一個是CustomerFactoryBean類的單例對象;另一個就是getObject()方法返回的對象,在demo中,咱們重寫的getObject()方法中,咱們經過new UserService()返回了一個UserService的實例對象,因此咱們從容器中能獲取到UserService的實例對象。若是咱們想經過beanName去獲取CustomerFactoryBean的單例對象,須要在beanName前面添加一個
&
符號,以下代碼,這樣就能根據beanName獲取到原生對象了。sql
public class MainApplication {
public static void main(String[] args) {
CustomerFactoryBean rawBean = (CustomerFactoryBean) applicationContext.getBean("&customerFactoryBean");
System.out.println(rawBean);
}
}
複製代碼
經過上面的示例代碼,咱們知道了FactoryBean的做用,也知道該如何使用FactoryBean,那麼接下來咱們就經過源碼來看看FactoryBean的工做原理。緩存
public void preInstantiateSingletons() throws BeansException {
// 從容器中獲取到全部的beanName
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
// 在此處會根據beanName判斷bean是否是一個FactoryBean,實現了FactoryBean接口的bean,會返回true
// 此時當beanName爲customerFactoryBean時,會返回true,會進入到if語句中
if (isFactoryBean(beanName)) {
// 而後經過getBean()方法去獲取或者建立單例對象
// 注意:在此處爲beanName拼接了一個前綴:FACTORY_BEAN_PREFIX
// FACTORY_BEAN_PREFIX是一個常量字符串,即:&
// 因此在此時容器啓動階段,對於customerFactoryBean,應該是:getBean("&customerFactoryBean")
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
// 下面這一段邏輯,是判斷是否須要在容器啓動階段,就去實例化getObject()返回的對象,便是否調用FactoryBean的getObject()方法
if (bean instanceof FactoryBean) {
final FactoryBean<?> factory = (FactoryBean<?>) bean;
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
((SmartFactoryBean<?>) factory)::isEagerInit,
getAccessControlContext());
}
else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
getBean(beanName);
}
}
}
}
}
}
複製代碼
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
final String beanName = transformedBeanName(name);
Object bean;
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
if (mbd.isSingleton()) {
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
else if (mbd.isPrototype()) {
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
else {
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
}
return (T) bean;
}
複製代碼
protected Object getObjectForBeanInstance( Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
if (BeanFactoryUtils.isFactoryDereference(name)) {
if (beanInstance instanceof NullBean) {
return beanInstance;
}
if (!(beanInstance instanceof FactoryBean)) {
throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
}
}
// 若是bean不是factoryBean,那麼會直接返回Bean
// 或者bean是factoryBean但name是以&特殊符號開頭的,此時表示要獲取FactoryBean的原生對象。
// 例如:若是name = &customerFactoryBean,那麼此時會返回CustomerFactoryBean類型的bean
if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
return beanInstance;
}
// 若是是FactoryBean,那麼先從cache中獲取,若是緩存不存在,則會去調用FactoryBean的getObject()方法。
Object object = null;
if (mbd == null) {
// 從緩存中獲取。何時放入緩存的呢?在第一次調用getObject()方法時,會將返回值放入到緩存。
object = getCachedObjectForFactoryBean(beanName);
}
if (object == null) {
FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
if (mbd == null && containsBeanDefinition(beanName)) {
mbd = getMergedLocalBeanDefinition(beanName);
}
boolean synthetic = (mbd != null && mbd.isSynthetic());
// 在getObjectFromFactoryBean()方法中最終會調用到getObject()方法
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;
}
複製代碼
protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
// 若是BeanFactory的isSingleton()方法返回值是true,表示getObject()返回值對象是單例的
if (factory.isSingleton() && containsSingleton(beanName)) {
synchronized (getSingletonMutex()) {
// 再一次判斷緩存中是否存在。(雙重檢測機制,和平時寫線程安全的代碼相似)
Object object = this.factoryBeanObjectCache.get(beanName);
if (object == null) {
// 在doGetObjectFromFactoryBean()中才是真正調用getObject()方法
object = doGetObjectFromFactoryBean(factory, beanName);
Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
if (alreadyThere != null) {
object = alreadyThere;
}
else {
// 下面是進行後置處理,和普通的bean的後置處理沒有任何區別
if (shouldPostProcess) {
if (isSingletonCurrentlyInCreation(beanName)) {
return object;
}
beforeSingletonCreation(beanName);
try {
object = postProcessObjectFromFactoryBean(object, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName,
"Post-processing of FactoryBean's singleton object failed", ex);
}
finally {
afterSingletonCreation(beanName);
}
}
// 放入到緩存中
if (containsSingleton(beanName)) {
this.factoryBeanObjectCache.put(beanName, object);
}
}
}
return object;
}
}
// 非單例
else {
Object object = doGetObjectFromFactoryBean(factory, beanName);
if (shouldPostProcess) {
try {
object = postProcessObjectFromFactoryBean(object, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
}
}
return object;
}
}
複製代碼
private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName) throws BeanCreationException {
Object object;
if (System.getSecurityManager() != null) {
AccessControlContext acc = getAccessControlContext();
try {
object = AccessController.doPrivileged((PrivilegedExceptionAction<Object>) factory::getObject, acc);
}
catch (PrivilegedActionException pae) {
throw pae.getException();
}
}
else {
// 調用getObject()方法
object = factory.getObject();
}
return object;
}
複製代碼
如今知道了FactoryBean的原理,那麼在平時工做中,你見過哪些FactoryBean的使用場景。若是沒有留意過的話,筆者在這裏就拿Spring整合Mybatis的原理來舉例吧。安全
mybatis-spring
,而後是配置數據源。最後還須要一個配置,若是你是經過XML配置的話,還須要以下配置:<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
</bean>
複製代碼
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
return sqlSessionFactoryBean;
}
複製代碼
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
// ...省略其餘代碼
public SqlSessionFactory getObject() throws Exception {
if (this.sqlSessionFactory == null) {
afterPropertiesSet();
}
return this.sqlSessionFactory;
}
}
複製代碼
public void afterPropertiesSet() throws Exception {
// buildSqlSessionFactory()方法會根據mybatis的配置進行初始化。
this.sqlSessionFactory = buildSqlSessionFactory();
}
複製代碼
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
public @interface MapperScan {
}
複製代碼
getSqlSession().getMapper(this.mapperInterface)
返回了一個對象。這一行代碼最終會調用到MapperProxyFactory的newInstance()方法,爲每個Mapper建立一個代理對象。public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
@Override
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
@Override
public Class<T> getObjectType() {
return this.mapperInterface;
}
@Override
public boolean isSingleton() {
// 返回true是爲了讓Mapper接口是一個單例的
return true;
}
}
複製代碼
public class MapperProxyFactory<T> {
protected T newInstance(MapperProxy<T> mapperProxy) {
// JDK動態代理
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
複製代碼
在SpringBoot中整合MyBatis的原理是同樣的,雖然使用的是mybatis-spring-boot-starter這個依賴,但最終的整合原理和Spring是如出一轍的。SpringBoot的最主要的功能是自動配置,和其餘框架的整合原理與Spring相比幾乎沒變。微信
@Configuration
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})
@ConditionalOnBean({DataSource.class})
@EnableConfigurationProperties({MybatisProperties.class})
@AutoConfigureAfter({DataSourceAutoConfiguration.class})
public class MybatisAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
factory.setDataSource(dataSource);
// 省略部分代碼
return factory.getObject();
}
}
複製代碼
掃描下方二維碼便可關注微信公衆號
菜鳥飛呀飛
,一塊兒閱讀更多Spring源碼。mybatis