2、Mini-spring Bean实例化策略+Bean填充属性

163 阅读3分钟

Bean实例化策略

上一篇文章中,Bean在AbstractAutowireCapableFactory.doCreateBean方法中使用beanClass.newInstance()方法来实例化,这样只适用于Bena有无参构造函数的情况。

对应bean的实例化,我们可以抽象出一个实例化策略的接口,分别有两个实现类

InstantiationStrategy:实例化Bean的策略接口,两个实现类,在AbstractAutowireCapableBeanFactory中使用,通过实例化不同的实现类对象,实现不同策略实例化bean

/**
 * Bean的实例化策略 接口
 *
 */
public interface InstantiationStrategy {

   Object instantiate(BeanDefinition beanDefinition) throws BeansException;
}

SimpleInstantiationStrategy:使用bean的无参构造函数实例化对象

public class SimpleInstantiationStrategy implements InstantiationStrategy {

   /**
    * 简单的bean实例化策略,根据bean的无参构造函数实例化对象
    *
    * @param beanDefinition
    * @return
    * @throws BeansException
    */
   @Override
   public Object instantiate(BeanDefinition beanDefinition) throws BeansException {
      Class beanClass = beanDefinition.getBeanClass();
      try {
         Constructor constructor = beanClass.getDeclaredConstructor();
         return constructor.newInstance();
      } catch (Exception e) {
         throw new BeansException("Failed to instantiate [" + beanClass.getName() + "]", e);
      }
   }
}

CglibSubclassingInstantiationStrategy:使用CGLIB动态生成子类

public class CglibSubclassingInstantiationStrategy implements InstantiationStrategy {

   /**
    * 使用CGLIB动态生成子类
    *
    * @param beanDefinition
    * @return
    * @throws BeansException
    */
   @Override
   public Object instantiate(BeanDefinition beanDefinition) throws BeansException {
      Enhancer enhancer = new Enhancer();
      enhancer.setSuperclass(beanDefinition.getBeanClass());
      enhancer.setCallback((MethodInterceptor) (obj, method, argsTemp, proxy) -> proxy.invokeSuper(obj,argsTemp));
      return enhancer.create();
   }
}

使用:

Bean填充属性

到现在,我们beanDefinition中只有Class信息,现在我们再往其中填充属性。

1、BeanDefinition中新增属性

private PropertyValues propertyValues;

public class PropertyValues {

   private final List<PropertyValue> propertyValueList = new ArrayList<>();

   public void addPropertyValue(PropertyValue pv) {
      propertyValueList.add(pv);
   }

   public PropertyValue[] getPropertyValues() {
      return this.propertyValueList.toArray(new PropertyValue[0]);
   }

   public PropertyValue getPropertyValue(String propertyName) {
      for (int i = 0; i < this.propertyValueList.size(); i++) {
         PropertyValue pv = this.propertyValueList.get(i);
         if (pv.getName().equals(propertyName)) {
            return pv;
         }
      }
      return null;
   }
}

/**
 * bean属性信息
 *
 */
public class PropertyValue {

   private final String name;

   private final Object value;

   public PropertyValue(String name, Object value) {
      this.name = name;
      this.value = value;
   }

   public String getName() {
      return name;
   }

   public Object getValue() {
      return value;
   }
}

2、构造对应数据进行测试,BeanDefinition中存入Person的name属性和age属性

@Test
public void testPopulateBeanWithPropertyValues() throws Exception {
   DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
   PropertyValues propertyValues = new PropertyValues();
   propertyValues.addPropertyValue(new PropertyValue("name", "derek"));
   propertyValues.addPropertyValue(new PropertyValue("age", 18));
   BeanDefinition beanDefinition = new BeanDefinition(Person.class, propertyValues);
   beanFactory.registerBeanDefinition("person", beanDefinition);

   Person person = (Person) beanFactory.getBean("person");
   System.out.println(person);
   assertThat(person.getName()).isEqualTo("derek");
   assertThat(person.getAge()).isEqualTo(18);
}

3、getBean方法最终调用doCreateBean的时候,applyPropertyValues方法填充属性

protected Object doCreateBean(String beanName, BeanDefinition beanDefinition) {
   Object bean = null;
   try {
      bean = createBeanInstance(beanDefinition);
      //为bean填充属性
      applyPropertyValues(beanName, bean, beanDefinition);
   } catch (Exception e) {
      throw new BeansException("Instantiation of bean failed", e);
   }

   addSingleton(beanName, bean);
   return bean;
}

4、通过反射的方式填充属性

/**
 * 为bean填充属性
 *
 * @param bean
 * @param beanDefinition
 */
protected void applyPropertyValues(String beanName, Object bean, BeanDefinition beanDefinition) {
   try {
      for (PropertyValue propertyValue : beanDefinition.getPropertyValues().getPropertyValues()) {
         String name = propertyValue.getName();
         Object value = propertyValue.getValue();

         //通过反射设置属性
         BeanUtil.setFieldValue(bean, name, value);
      }
   } catch (Exception ex) {
      throw new BeansException("Error setting property values for bean: " + beanName, ex);
   }
}

Bean注入Bean

上面通过反射的方式,将Bean的属性设置进去。下面实现一个Bean引用另一个Bean如何填充属性。(暂时不支持循环依赖)

首先先从测试开始。

1、新增Car对象,然后让Person对象引入Car

public class Car {
   private String brand;
   
}

public class Person {

   private String name;

   private int age;

   private Car car;
}

2、测试

  • 首先先注册Car实例。并存储其相关属性
  • Person实例依赖Car实例,将car实例引入person,并且value为BeanReference引用类型。注册Person实例
  • 调用getBean()
/**
 * 为bean注入bean
 *
 * @throws Exception
 */
@Test
public void testPopulateBeanWithBean() throws Exception {
   DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

   //注册Car实例
   PropertyValues propertyValuesForCar = new PropertyValues();
   propertyValuesForCar.addPropertyValue(new PropertyValue("brand", "porsche"));
   BeanDefinition carBeanDefinition = new BeanDefinition(Car.class, propertyValuesForCar);
   beanFactory.registerBeanDefinition("car", carBeanDefinition);

   //注册Person实例
   PropertyValues propertyValuesForPerson = new PropertyValues();
   propertyValuesForPerson.addPropertyValue(new PropertyValue("name", "derek"));
   propertyValuesForPerson.addPropertyValue(new PropertyValue("age", 18));
   //Person实例依赖Car实例
   propertyValuesForPerson.addPropertyValue(new PropertyValue("car", new BeanReference("car")));
   BeanDefinition beanDefinition = new BeanDefinition(Person.class, propertyValuesForPerson);
   beanFactory.registerBeanDefinition("person", beanDefinition);

   Person person = (Person) beanFactory.getBean("person");
   System.out.println(person);
   assertThat(person.getName()).isEqualTo("derek");
   assertThat(person.getAge()).isEqualTo(18);
   Car car = person.getCar();
   assertThat(car).isNotNull();
   assertThat(car.getBrand()).isEqualTo("porsche");
}
//一个Bean对另一个Bean的引用
public class BeanReference {

   private final String beanName;


   public BeanReference(String beanName) {
      this.beanName = beanName;
   }

   public String getBeanName() {
      return beanName;
   }
}

3、填充属性。beanA依赖beanB

在beanA填充属性的时候判断propertyValue的类型是否是BeanReference引用类型,如果是引用类型的话,先去实例化beanB,也就是用这个引用类型去调用getBean()方法,类似于递归的调用,总之遇到引用类型,就先去实例化引用类型的属性。然后再设置。

protected void applyPropertyValues(String beanName, Object bean, BeanDefinition beanDefinition) {
   try {
      for (PropertyValue propertyValue : beanDefinition.getPropertyValues().getPropertyValues()) {
         String name = propertyValue.getName();
         Object value = propertyValue.getValue();
         if (value instanceof BeanReference) {
            // beanA依赖beanB,先实例化beanB
            BeanReference beanReference = (BeanReference) value;
            value = getBean(beanReference.getBeanName());
         }

         //通过反射设置属性
         BeanUtil.setFieldValue(bean, name, value);
      }
   } catch (Exception ex) {
      throw new BeansException("Error setting property values for bean: " + beanName, ex);
   }
}