插手"容器的启动"——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
相关文章
相关标签/搜索