SpringFramework-FactoryBean应用

69 阅读3分钟

前言

看spring源码过程时,看到了一个MethodInvokingFactoryBean类,文档中的描述如下:

    <property name="targetClass" value="java.lang.System">
    <property name="targetMethod" value="getProperties">
  </bean>
 
  <bean id="javaVersion" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
    <property name="targetObject" ref="sysProps">
    <property name="targetMethod" value="getProperty">
    <property name="arguments" value="java.version">
  </bean>

可以直接读取类的静态方法,并传入参数,包装成对应返回值的bean,注册到springIoc容器中。优势是不需要嵌入原有代码即可将之前的代码中的方法暴露出来,注册成为bean。按照示例开发了一个简单demo。代码运行后

1665505605123.png

可以获取到了系统变量属性对象,总共有54个对象。其实可以除了这种方法外我们也可以手动正向调用该静态方法的方式来暴露对应的bean。这个反射工厂类可以在我们通过配置来初始化某些bean时,外部定义配置好后,使用改bean工厂类动态初始化bean到系统中来。

拓展

我们平常使用FactoryBean场景最常用的,可以很快的想到mybatis中的接口类暴露。我们调用接口中定义的抽象方法既可以完成对应方法的调用,完成sql的操作。

动态代理应用

mybatis使用的JDK动态代理的方式,代理接口类。运行时构造对应的对象,通过InvocationHandler中的invoke方法来进行真实的方法调用,统一sql调用处理。

实现追踪

入口类我们从MapperScan注解开始,逐渐找到真实的调用代码块。

注册类

1665506280081.png 通过import引入我们的配置类,核心方法在registerBeanDefinitions方法中。

void registerBeanDefinitions(AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry) {

  ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);

  // this check is needed in Spring 3.1
  // 通过感知接口,注入资源加载器
  Optional.ofNullable(resourceLoader).ifPresent(scanner::setResourceLoader);

  // 添加注解过滤
  Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
  if (!Annotation.class.equals(annotationClass)) {
    scanner.setAnnotationClass(annotationClass);
  }

  // 添加接口过滤
  Class<?> markerInterface = annoAttrs.getClass("markerInterface");
  if (!Class.class.equals(markerInterface)) {
    scanner.setMarkerInterface(markerInterface);
  }
  
  // 添加bean名称生成器
  Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
  if (!BeanNameGenerator.class.equals(generatorClass)) {
    scanner.setBeanNameGenerator(BeanUtils.instantiateClass(generatorClass));
  }

  // 手动添加factoryBean,替换掉系统中的mybatis实现的FactoryBean
  Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
  if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
    scanner.setMapperFactoryBean(BeanUtils.instantiateClass(mapperFactoryBeanClass));
  }

  scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef"));
  scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef"));

  List<String> basePackages = new ArrayList<>();
  basePackages.addAll(
      Arrays.stream(annoAttrs.getStringArray("value"))
          .filter(StringUtils::hasText)
          .collect(Collectors.toList()));

  basePackages.addAll(
      Arrays.stream(annoAttrs.getStringArray("basePackages"))
          .filter(StringUtils::hasText)
          .collect(Collectors.toList()));

  basePackages.addAll(
      Arrays.stream(annoAttrs.getClassArray("basePackageClasses"))
          .map(ClassUtils::getPackageName)
          .collect(Collectors.toList()));

  scanner.registerFilters();
  // 核心会在下面这个方法中调用
  scanner.doScan(StringUtils.toStringArray(basePackages));
}

扫描类

进入到扫描方法中,核心方法是processBeanDefinitions方法。

1665506921656.png

bean定义修改

实际上面的核心方法对beanDefinition列表进行了修改。将接口相关的类替换为我们实际需要暴露的FactoryBean。在通过FactoryBean的核心getObject方法。暴露接口对应的动态代理实现类出来进行bean注册。 真正的核心是下面两行代码:

1665507368847.png

  • 将接口类传递到包装对象中
  • 设置beanClass为MapperFactoryBean

1665507454534.png

spring在调用该接口类时,通过byType找到对应的实现工厂类。调用getObject方法得到真正的实现类进行操作。

代理对象

通过追踪尚需的MapperFactoryBean很容易找到对应的代理对象的实现代码块。如下

public class MapperProxyFactory<T> {

  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<>();

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

  public Class<T> getMapperInterface() {
    return mapperInterface;
  }

  public Map<Method, MapperMethod> getMethodCache() {
    return methodCache;
  }

  @SuppressWarnings("unchecked")
  protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

  public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }

}

通过Proxy.newProxyInstance方法产生的代理对象。到这里整个接口类的实现就完整解析出来了。例如Feign等都是相同的方式来进行接口包装bean的过程。

FactoryBean加载

在想spring中为什么就可以直接使用FactoryBean进行bean的初始化,而不是直接调用类自带的构造方法进行类的构建。实际发现是在getbean的过程中有进行FactoryBean的判断,并进行初始化。通过getBean方法追踪后可以找到factoryBean的判断位置。

protected Object getObjectForBeanInstance(
      Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {

   // Don't let calling code try to dereference the factory if the bean isn't a factory.
   if (BeanFactoryUtils.isFactoryDereference(name)) {
      if (beanInstance instanceof NullBean) {
         return beanInstance;
      }
      if (!(beanInstance instanceof 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.
   // 普通bean直接返回
   if (!(beanInstance instanceof 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);
      }
      boolean synthetic = (mbd != null && mbd.isSynthetic());
      // 实际获得FactoryBean对象
      object = getObjectFromFactoryBean(factory, beanName, !synthetic);
   }
   return object;
}

核心创建过程

protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
   if (factory.isSingleton() && containsSingleton(beanName)) {
      synchronized (getSingletonMutex()) {
         Object object = this.factoryBeanObjectCache.get(beanName);
         if (object == null) {
            // 实际通过FactoryBean子类的getObject方法进行获取真实的bean对象
            object = doGetObjectFromFactoryBean(factory, beanName);
            // Only post-process and store if not put there already during getObject() call above
            // (e.g. because of circular reference processing triggered by custom getBean calls)
            Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
            if (alreadyThere != null) {
               object = alreadyThere;
            }
            else {
               if (shouldPostProcess) {
                  if (isSingletonCurrentlyInCreation(beanName)) {
                     // Temporarily return non-post-processed object, not storing it yet..
                     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;
   }
}

进而发现spring对FactoryBean是有一些特殊处理,来支持对象的特殊获取处理,将实际的对象通过map缓存起来。aware,DisposableBean,InitializingBean等接口都是可以直接相同使用,使用容器相关的特性。

其他FactoryBean扩展

下面会研究CacheProxyFactoryBean,TransactionProxyFactoryBean这两个常用的bean在项目中的使用。