spring(四):set注入(上)

PropertyValue

接下来我们实现spring的set注入,目标是通过xml配置属性set到具体的实体内。了解了目的,我们先来写它的测试用例:

    @Test
    public void testGetBeanDefinition(){
        DefaultBeanFactory factory = new DefaultBeanFactory();
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
        Resource resource = new ClassPathResource("petstore-v2.xml");
        reader.loadBeanDefinitions(resource);

        BeanDefinition bd = factory.getBeanDefinition("petStore");

        List pvs = bd.getPropertyValues();
        Assert.assertTrue(pvs.size() == 2 );
        {
            PropertyValue pv = this.getPropertyValue("accountDao",pvs);
            Assert.assertNotNull(pv);
            Assert.assertTrue(pv.getValue() instanceof RuntimeBeanReference);
        }

        {
            PropertyValue pv = this.getPropertyValue("itemDao" , pvs);
            Assert.assertNotNull(pv);
            Assert.assertTrue(pv.getValue() instanceof RuntimeBeanReference);
        }
        
    }

    private PropertyValue getPropertyValue(String name , List pvs){
        for (PropertyValue pv : pvs){
            if (pv.getName().equals(name))
                return pv;
        }
        return null;
    }

为了让这个测试用例通过,我们给出一个要解析的xml,和其他实体类。此处只列出xml:

    
        
        
    

给bean设置一个id,指定对应的class,这个一功能上面几篇已经实现了。下面我们就要把具体的属性(prooperty)赋予相应的值。我们引进一个类(PropertyValue),这里存放bean下面的property,每个property对应一个PropertyValue,而bean下面可能会有多个property,我们又要把xml解析成BeanDefintion,所以我们在BeanDefintion中增加一个方法:

    /**
     * 获取property的全部属性
     * @return
     */
    List getPropertyValues();

消除编译错误,在GenericBeanDefinition中实现此方法,实现方式也很简单,指定把解析出来的propertyValues返回,具体查看github上相关代码。
接下来我们来实现解析property,解析的操作是在我们的XmlBeanDefintionReader的loadBeanDefintions中进行的,我们需要在此方法中增加这一部分解析,同时对代码进行重构,提取出来两个方法,新的XmlBeanDefintionReader的代码如下:

public class XmlBeanDefinitionReader {

    public static final String ID_ATTRIBUTE = "id";
    public static final String CLASS_ATTRIBUTE = "class";

    public static final String SCOPE_ATTRIBUTE = "scope";

    public static final String PROPERTY_ELEMENT = "property";

    public static final String REF_ATTRIBUTE = "ref";
    public static final String VALUE_ATTRIBUTE = "value";
    public static final String NAME_ATTRIBUTE = "name";

    protected final Log logger = LogFactory.getLog(getClass());

    BeanDefinitionRegistry registry;//bean的注册实体

    public XmlBeanDefinitionReader(BeanDefinitionRegistry registry){
        this.registry = registry;
    }

    public void loadBeanDefinitions(Resource resource ){
        InputStream is = null;
        try {
            is = resource.getInputStream();
/*            //获取默认类加载器
            ClassLoader cl = ClassUtils.getDefaultClassLoader();
            //读取文件
            is = cl.getResourceAsStream(configFile);*/
            SAXReader reader = new SAXReader();
            Document doc = null;
            doc = reader.read(is);
            // 获取
            Element root = doc.getRootElement();
            Iterator iterator = root.elementIterator();
            //遍历所有,并把信息注册到registry中
            while (iterator.hasNext()){
                Element element = (Element)iterator.next();
                String id = element.attributeValue(ID_ATTRIBUTE);
                String beanClassName = element.attributeValue(CLASS_ATTRIBUTE);
                BeanDefinition bd = new GenericBeanDefinition(id,beanClassName);
                if (null != element.attribute( SCOPE_ATTRIBUTE )){
                    bd.setScope( element.attributeValue( SCOPE_ATTRIBUTE ) );
                }
                parsePropertyElement(element,bd);
                this.registry.registerBeanDefinition(id,bd);
            }
        } catch (DocumentException | IOException e) {
            throw new BeanDefinitionStoreException("IOException parsing XML document",e);
        } finally {
            if (is != null){
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 解析property
     * @param beanElem
     * @param bd
     */
    public void parsePropertyElement(Element beanElem , BeanDefinition bd){
        Iterator iter = beanElem.elementIterator(PROPERTY_ELEMENT);
        //为了让property无顺序性,这里采取了遍历
        while (iter.hasNext()){
            Element propElem = (Element) iter.next();
            String propertyName = propElem.attributeValue(NAME_ATTRIBUTE);
            if (!StringUtils.hasLength(propertyName)) {
                logger.fatal("Tag 'property' must have a 'name' attribute" );
                return;
            }
            Object val = parsePropertyValue(propElem,bd,propertyName);
            PropertyValue pv = new PropertyValue(propertyName,val);
            //给beanDefintion中的propertyValues添加新的pv
            bd.getPropertyValues().add(pv);
        }
    }


    /**
     * 根据ref、value返回对应的实体或值。
     * @param ele
     * @param bd
     * @param propertyName
     * @return
     */
    public Object parsePropertyValue(Element ele , BeanDefinition bd , String propertyName){
        String elementName = (propertyName != null ) ?
                " element for property ' " + propertyName + "'" :
                " element ";
        boolean hasRefAttribute = (ele.attribute(REF_ATTRIBUTE) != null);
        boolean hasValueAttribute = (ele.attribute(VALUE_ATTRIBUTE) != null );

        if (hasRefAttribute){
            //如果是ref,则返回RuntimeBeanReference类型
            String refName = ele.attributeValue(REF_ATTRIBUTE);
            if (!StringUtils.hasText(refName)){
                logger.error(elementName + " contains empty 'ref' attribute ");
            }
            RuntimeBeanReference ref = new RuntimeBeanReference(refName);
            return ref;
        }else if (hasValueAttribute){
            //如果是value则返回TypedStringValue类型
            TypedStringValue valueHolder = new TypedStringValue(ele.attributeValue(VALUE_ATTRIBUTE));
            return valueHolder;
        }else {
            throw new RuntimeException(elementName + " must specify a ref or value");
        }
    }

}

在parsePropertyValue这个方法中解析的是property的具体值,平时我们配置spring知道,property是既支持value又支持ref的,所以我们在这个方法中对于不同的key有不通的返回类型:refRuntimeBeanReference类型,valueTypedStringValue类型。
其他的类例如:RuntimeBeanReference、TypedStringValue等都是一些简单的实体类,具体的查看github即可。
现在我们离set注入属性值更近了一步。只需要把RuntimeBeanReference、TypedStringValue解析成具体的值赋值给类属性即可。我们先写一下测试用例:

public class BeanDefinitionValueResolverTest {

    DefaultBeanFactory factory ;
    XmlBeanDefinitionReader reader ;
    BeanDefinitionValueResolver resolver ;

    @Before
    public void setUp(){
        factory = new DefaultBeanFactory();
        reader = new XmlBeanDefinitionReader(factory);
        reader.loadBeanDefinitions(new ClassPathResource("petstore-v2.xml"));
        resolver = new BeanDefinitionValueResolver(factory);

    }


    @Test
    public void testResolveRuntimeBeanReference(){
        RuntimeBeanReference reference = new RuntimeBeanReference("accountDao");
        Object value = resolver.resolveValueIfNecessary(reference);
        Assert.assertNotNull(value);
        Assert.assertTrue(value instanceof AccountDao );
    }


    @Test
    public void testResolveTypedStringValue(){
        TypedStringValue stringValue = new TypedStringValue("test");
        Object value = resolver.resolveValueIfNecessary(stringValue);
        Assert.assertNotNull(value);
        Assert.assertEquals("test",value);
    }
}

这个测试用例是为了实现一个类(BeanDefinitionValueResolver)可以把xml转成具体的实体类或值。

public class BeanDefinitionValueResolver {

    private final BeanFactory beanFactory;

    public BeanDefinitionValueResolver(BeanFactory beanFactory){
        this.beanFactory = beanFactory;
    }

    /**
     * 根据propertyValue的值返回具体的内容
     * @param value
     * @return
     */
    public Object resolveValueIfNecessary(Object value){
        if (value instanceof RuntimeBeanReference){
            //如果value的类型为RuntimeBeanReference,则从beanFactory中获取bean
            RuntimeBeanReference ref = (RuntimeBeanReference)value;
            String refName = ref.getBeanName();
            Object bean = this.beanFactory.getBean(refName);
            return bean;
        } else if (value instanceof TypedStringValue){
            //如果value的类型为TypedStringValue,则返回具体的值
            return ((TypedStringValue)value).getValue();
        } else {
            //TODO 拓展其他类型
            throw new RuntimeException("the value " + value + "has not implemented");
        }
    }
}

上述我们完成了PropertyValue的value转换成具体的实体或值。还需要把具体的(实体或值)赋值给实体的属性。我们来写一下这个完整功能的测试用例吧。

public class ApplicationContextTestV2 {

    @Test
    public void testGetBean(){
        ApplicationContext ctx = new ClassPathXmlApplicationContext("petstore-v2.xml");
        PetStoreService petStore = (PetStoreService)ctx.getBean("petStore");
        Assert.assertNotNull(petStore.getAccountDao());
        Assert.assertNotNull(petStore.getItemDao());


        Assert.assertTrue(petStore.getAccountDao() instanceof AccountDao);
        Assert.assertTrue(petStore.getItemDao() instanceof ItemDao);
    }


}

思考一下具体的赋值应该在创建bean的时候,那自然是要修改DefaultBeanFactory的部分代码。修改后的代码如下:

public class DefaultBeanFactory extends DefaultSingletonBeanRegistry
        implements ConfigurableBeanFactory, BeanDefinitionRegistry{

    private ClassLoader beanClassLoader;

    private final Map beanDefinitionMap =
            new ConcurrentHashMap();

    public void registerBeanDefinition(String id, BeanDefinition bd) {
        this.beanDefinitionMap.put(id,bd);
    }

    public BeanDefinition getBeanDefinition(String BeanId) {
        return this.beanDefinitionMap.get(BeanId);
    }

    /**
     * 通过BeanId获取bean
     * @param beanId
     * @return
     */
    public Object getBean(String beanId) {
        //获取bean的定义
        BeanDefinition bd = this.getBeanDefinition(beanId);
        if (bd == null){
            return null;
        }
/*        //获取默认类加载器
        ClassLoader cl = this.getBeanClassLoader();
        //获取bean的name
        String beanClassName = bd.getBeanClassName();
        try {
            Class clazz = cl.loadClass(beanClassName);
            //反射出类的实体
            return clazz.newInstance();
        } catch (Exception e) {
            throw new BeanCreationException("create bean for " + beanClassName + " fail");
        }*/
        //判断要创建的bean是不是单例的。
        if (bd.isSingleton()){
            //先从singletonObjects这个map中获取
            Object bean = this.getSingleton( beanId );
            //如果没有获取到则创建一个,同时放进singletonObjects中
            if (null == bean){
                bean = createBean(bd);
                this.registerSingleton( beanId , bean );
            }
            return bean;
        }
        //直接创建一个bean
        return createBean(bd);
    }

/*    *//**
     * 通过BeanDefintion创建bean
     * @param bd
     * @return
     *//*
    private Object createBean(BeanDefinition bd) {
        //获取默认类加载器
        ClassLoader cl = this.getBeanClassLoader();
        //获取bean的name
        String beanClassName = bd.getBeanClassName();
        try {
            Class clazz = cl.loadClass(beanClassName);
            //反射出类的实体
            return clazz.newInstance();
        } catch (Exception e) {
            throw new BeanCreationException("create bean for " + beanClassName + " fail");
        }
    }*/

    /**
     * 创建bean
     * @param bd
     * @return
     */
    private Object createBean(BeanDefinition bd){
        //创建实例
        Object bean = instantiateBean(bd);
        //设置属性
        populateBean(bd,bean);
        return bean;
    }

    /**
     * 初始化bean
     * @param bd
     * @return
     */
    private Object instantiateBean(BeanDefinition bd){
        //获取类加载器
        ClassLoader cl = this.getBeanClassLoader();
        String beanClassName = bd.getBeanClassName();
        try {
            Class clz = cl.loadClass(beanClassName);
            //反射出类的实体
            return clz.newInstance();
        } catch (Exception e) {
            throw new BeanCreationException("create bean for " + beanClassName + "failed ", e);
        }
    }

    /**
     * 给bean的属性进行赋值
     * @param bd
     * @param bean
     */
    protected void populateBean(BeanDefinition bd,Object bean){
        //获取beanDefintion的value
        List pvs = bd.getPropertyValues();
        if (pvs == null || pvs.isEmpty()){
            return;
        }
        BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this);
        try {
            for (PropertyValue pv : pvs){
                String propertyName = pv.getName();
                Object originalValue = pv.getValue();
                //获取具体的实体或值
                Object resolvedValue = valueResolver.resolveValueIfNecessary(originalValue);
                //利用java自带的bean方法,对bean实体属性进行赋值。
                BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());
                PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
                for (PropertyDescriptor pd : pds){
                    if (pd.getName().equals(propertyName)){
                        pd.getWriteMethod().invoke(bean,resolvedValue);
                        break;
                    }
                }
            }
        }catch (Exception e){
            throw new BeanCreationException("Failed to obtain BeanInfo for class ["+bd.getBeanClassName()+"]");
        }
    }

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        this.beanClassLoader = classLoader;
    }

    @Override
    public ClassLoader getBeanClassLoader() {
        return (this.beanClassLoader != null ? this.beanClassLoader : ClassUtils.getDefaultClassLoader());
    }
}

至此,我们已经完成了set注入的大部分功能。后续我们还会支持set其他类型的数据。包括int、boolean等。


                                                                                                生活要多点不自量力

你可能感兴趣的:(spring(四):set注入(上))