插手"容器的啓動"——FactoryBean篇

插手"容器的啓動"——FactoryBean篇

源碼分析

spring剛啓動時 會進入doGetBean方法,若是bean是首次建立,則會進入建立Bean的步驟java

先經過Factory建立對象spring

image-20200603103306759

再調用getObjectForBeanInstance(sharedInstance, name, beanName, mbd)sql

image-20200603102840477

getObjectForBeanInstance有三個判斷 從上至下執行bash

  1. 若是name以&開頭,且bean不是一個FactoryBean,直接拋BeanIsNotAFactoryException異常
  2. 若是bean不是FactoryBean 或者 以name以&開頭 將剛剛ObjectFactory執行的createBean返回
  3. 最後一種是爲FactoryBean,且沒有以&開頭,則執行FactoryBean的自定義建立方法

image-20200603111238566

image-20200603111255798

FactoryBean--demo

一般狀況下,bean 無須本身實現工廠模式,Spring 容器擔任了工廠的 角色;但少數狀況下,容器中的 bean 自己就是工廠,做用是產生其餘 bean 實例。由工廠 bean 產生的其餘 bean 實例,再也不由 Spring 容器產生,所以與普通 bean 的配置不一樣,再也不須要提供 class 元素。mybatis

先定義一個Bean實現FactoryBean接口app

@Component
public class MyBean implements FactoryBean {
    private String message;
    public MyBean() {
        this.message = "經過構造方法初始化實例";
    }
    @Override
    public Object getObject() throws Exception {
        // 這裏並不必定要返回MyBean自身的實例,能夠是其餘任何對象的實例。
        //如return new Student()...
        return new MyBean("經過FactoryBean.getObject()建立實例");
    }
    @Override
    public Class<?> getObjectType() {
        return MyBean.class;
    }
    public String getMessage() {
        return message;
    }
}
複製代碼

MyBean實現了FactoryBean接口的兩個方法,getObject()是能夠返回任何對象的實例的,這裏測試就返回MyBean自身實例,且返回前給message字段賦值。同時在構造方法中也爲message賦值。而後測試代碼中先經過名稱獲取Bean實例,打印message的內容,再經過&+名稱獲取實例並打印message內容。ide

@RunWith(SpringRunner.class)
@SpringBootTest(classes = TestApplication.class)
public class FactoryBeanTest {
    @Autowired
    private ApplicationContext context;
    @Test
    public void test() {
        MyBean myBean1 = (MyBean) context.getBean("myBean");
        System.out.println("myBean1 = " + myBean1.getMessage());
        MyBean myBean2 = (MyBean) context.getBean("&myBean");
        System.out.println("myBean2 = " + myBean2.getMessage());
        System.out.println("myBean1.equals(myBean2) = " + myBean1.equals(myBean2));
    }
}
複製代碼
myBean1 = 經過FactoryBean.getObject()初始化實例
myBean2 = 經過構造方法初始化實例
myBean1.equals(myBean2) = false
複製代碼

FactoryBean在Spring中最爲典型的一個應用就是用來建立AOP的代理對象源碼分析

那麼mybatis 是怎麼插手的呢?

首先mybatis實現MapperScannerConfigurer,用於掃描mapper文件post

image-20200602165036118
《插手"容器的啓動"——BeanFactoryPostProcessor篇》知道,postProcessors的方法會在容器啓動某個過程當中被執行。

查看BeanDefinitionRegistryPostProcessor源碼,發現繼承於BeanFactoryPostProcessor測試

public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
	void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}
複製代碼

BeanFactoryPostProcessor是用於在bean被初始化爲BeanDefinition

  • 先執行 postProcessBeanDefinitionRegistry(registry)
  • 再執行 postProcessBeanFactory(beanFactory)
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
  if (this.processPropertyPlaceHolders) {
    processPropertyPlaceHolders();
  }

  ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
  scanner.setAddToConfig(this.addToConfig);
  scanner.setAnnotationClass(this.annotationClass);
  scanner.setMarkerInterface(this.markerInterface);
  scanner.setSqlSessionFactory(this.sqlSessionFactory);
  scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
  scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
  scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
  scanner.setResourceLoader(this.applicationContext);
  scanner.setBeanNameGenerator(this.nameGenerator);
  //用於配置掃描mapper的過濾器 我這裏的filter是null的 因此直接素有mapper會加載到beanDefinition中
  scanner.registerFilters();
  scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, 				                          ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
複製代碼

scan方法會走到doScan

public Set<BeanDefinitionHolder> doScan(String... basePackages) {
  //調用父類(Spring提供的ClassPathBeanDefinitionScanner進行掃描包,掃描包的類信息、註解到 BeanDefinition,最後在通過前面定義的filter,返回一個BeanDefinitionHolder),最後調用this.registerBeanDefinition(definitionHolder, this.registry)
  Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);

  if (beanDefinitions.isEmpty()) {
    logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
  } else {
    for (BeanDefinitionHolder holder : beanDefinitions) {
      GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition();

      if (logger.isDebugEnabled()) {
        logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() 
            + "' and '" + definition.getBeanClassName() + "' mapperInterface");
      }

      // mapperInterface設爲BeanName
      // MapperFactoryBean設爲BeanClass 後面會用到FactoryBean特性
      definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName());
      definition.setBeanClass(MapperFactoryBean.class);

      definition.getPropertyValues().add("addToConfig", this.addToConfig);

      boolean explicitFactoryUsed = false;
      if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
        definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
        explicitFactoryUsed = true;
      } else if (this.sqlSessionFactory != null) {
        definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
        explicitFactoryUsed = true;
      }

      if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
        if (explicitFactoryUsed) {
          logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
        }
        definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
        explicitFactoryUsed = true;
      } else if (this.sqlSessionTemplate != null) {
        if (explicitFactoryUsed) {
          logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
        }
        definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
        explicitFactoryUsed = true;
      }

      if (!explicitFactoryUsed) {
        if (logger.isDebugEnabled()) {
          logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
        }
        definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
      }
    }
  }

  return beanDefinitions;
}
複製代碼

這裏咱們重點關注設置beanClass

設置BeanDefinition對象的BeanClass爲MapperFactoryBean<?>。這意味着什麼呢?以UserMapper爲例,意味着當前的mapper接口在Spring容器中,beanName是userMapper,beanClass是MapperFactoryBean.class。那麼在IOC初始化的時候,實例化的對象就是MapperFactoryBean對象。

如今咱們全部的**MapperBeanClassMapperFactoryBean<?>,由上邊咱們知道,全部的FactoryBean在實例化時,會調用自身的getObject()方法,咱們查看下MapperFactoryBean<?>這個類

public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {

  private Class<T> mapperInterface;

  private boolean addToConfig = true;

  public void setMapperInterface(Class<T> mapperInterface) {
    this.mapperInterface = mapperInterface;
  }

  public void setAddToConfig(boolean addToConfig) {
    this.addToConfig = addToConfig;
  }
  public T getObject() throws Exception {
    return getSqlSession().getMapper(this.mapperInterface);
  }
  public Class<T> getObjectType() {
    return this.mapperInterface;
  }
  public boolean isSingleton() {
    return true;
  }
}
複製代碼

調用getObject會進入getMapper

image-20200603144812079

利用jdk代理反射獲取對象

image-20200603144830797

image-20200603144957539
相關文章
相關標籤/搜索