spring(六):构造器注入

上一节我们完成了set注入,接下来我们实现构造器注入。咱们可以先来看一下spring构造器注入的使用方法。

    
        
        
        
    

    
    

    
    

构造器新增加了constructor-arg标签。通过constructor-arg标签传递参数,当然它还支持指定位置、类型。而这里只是为了大家能够理解spring的构造器注入就不在这实现这些功能了。
按照惯例我们先来写一个测试用例:

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

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

        Assert.assertEquals("org.litespring.service.v3.PetStoreService",bd.getBeanClassName());

        ConstructorArgument args = bd.getConstructorArgument();
        List valueHolders = args.getArgumentValues();

        Assert.assertEquals(3,valueHolders.size());

        RuntimeBeanReference ref1 = (RuntimeBeanReference)valueHolders.get(0).getValue();
        Assert.assertEquals("accountDao" , ref1.getBeanName());
        RuntimeBeanReference ref2 = (RuntimeBeanReference)valueHolders.get(1).getValue();
        Assert.assertEquals("itemDao" , ref2.getBeanName());

        TypedStringValue stringValue = (TypedStringValue)valueHolders.get(2).getValue();
        System.out.println(stringValue.getValue());
        Assert.assertEquals("1",stringValue.getValue());
        
    }

这里我们提供了两个类ConstructorArgumentValueHolderConstructorArgument是存储xml解析的实体,而一个ValueHolder存储一个,因为只有ConstructorArgument类会用到ValueHolder,Spring实现的ValueHolder类,为ConstructorArgument类的静态内部类,这里也充分体现了高内聚思想:

public class ConstructorArgument {

    private final List argumentValues = new LinkedList();

    public ConstructorArgument(){

    }

    public void addArgumentValue(Object value , String type){
        this.argumentValues.add(new ValueHolder(value,type));
    }

    public void addArgumentValue(ValueHolder valueHolder){
        this.argumentValues.add(valueHolder);
    }

    public List getArgumentValues(){
        return Collections.unmodifiableList(this.argumentValues);
    }

    public int getArgumentCount(){
        return this.argumentValues.size();
    }

    public boolean isEmpty(){
        return this.argumentValues.isEmpty();
    }

    public void clear(){
        this.argumentValues.clear();
    }

    public static class ValueHolder{

        private Object value ;
        private String type;
        private String name;

        public ValueHolder(Object value){
            this.value = value;
        }

        public ValueHolder(Object value , String type){
            this.value = value ;
            this.type = type ;
        }

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

        public Object getValue() {
            return value;
        }

        public void setValue(Object value) {
            this.value = value;
        }

        public String getType() {
            return type;
        }

        public void setType(String type) {
            this.type = type;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }

}

此时我们已经完成了实体的构建,还需要我们把xml解析成具体的实体,而xml解析操作是在XmlBeanDefintionReader中进行的,我们来看一下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";

    public static final String CONSTRUCTOR_ARG_ELEMENT = "constructor-arg";

    public static final String TYPE_ATTRIBUTE = "type";

    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 ) );
                }
                //进行构造器注入
                parseConstructorArgElements(element,bd);
                //进行set注入
                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");
        }
    }

    /**
     * 进行构造器注入
     * @param beanEle
     * @param bd
     */
    public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) {
        Iterator iter = beanEle.elementIterator(CONSTRUCTOR_ARG_ELEMENT);
        while(iter.hasNext()){
            Element ele = (Element)iter.next();
            //xml解析成beanDefintion
            parseConstructorArgElement(ele, bd);
        }

    }

    /**
     * 把xml解析成BeanDefintion中
     * @param ele
     * @param bd
     */
    public void parseConstructorArgElement(Element ele , BeanDefinition bd){
        //获取标签属性
        String typeAttr = ele.attributeValue(TYPE_ATTRIBUTE);
        String nameAttr = ele.attributeValue(NAME_ATTRIBUTE);
        //根据ref、value返回相应类型
        Object value = parsePropertyValue(ele,bd,null);
        //生成valueHolder
        ConstructorArgument.ValueHolder valueHolder = new ConstructorArgument.ValueHolder(value);
        if (StringUtils.hasLength(typeAttr)){
            valueHolder.setType(typeAttr);
        }
        if (StringUtils.hasLength(nameAttr)){
            valueHolder.setName(nameAttr);
        }
        //放到beanDefintion中
        bd.getConstructorArgument().addArgumentValue(valueHolder);
    }
}

构造器注入采用了ValueHolder,这里的思想其实和set注入如出一辙,set注入采用了PropertyValue。都是解析成具体的实体放到BeanDefintion中,使后面的使用起来思路更清晰,日常开发中也可以采用这种方式来解藕。目前我们已经完成了xml解析操作,可以通过上述的测试用例了。后面只需要我们创建bean的时候完成通过BeanDefintion获取这些信息注入到创建的bean中即可。我们先写一个测试用例:

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

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

        ConstructorResolver resolver = new ConstructorResolver(factory);
        PetStoreService petStore = (PetStoreService)resolver.autowireConstructor(bd);

        //验证参数version,正确地通过此构造函数做了初始化
        //PetStoreService(....)
        Assert.assertEquals(1,petStore.getVersion());
        Assert.assertNotNull(petStore.getAccountDao());
        Assert.assertNotNull(petStore.getItemDao());
    }

这里的测试目的是,通过把xml解析成valueHolder根据valueHolderList查找可用的构造函数,根据构造函数反射生成一个bean。
通过测试用例我们可以看到这里包含一个新的类ConstructorResolver,通过这个类找到对应的构造函数并初始化。

public class ConstructorResolver {

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

    private final ConfigurableBeanFactory beanFactory;

    public ConstructorResolver(ConfigurableBeanFactory beanFactory){
        this.beanFactory = beanFactory;
    }

    /**
     * 通过beanDefintion找到具体的构造函数并生成
     * @param bd
     * @return
     */
    public Object autowireConstructor(final BeanDefinition bd){
        Constructor constructorToUse = null ;
        Object[] argsToUse = null ;
        Class beanClass = null ;
        try {
            beanClass = this.beanFactory.getBeanClassLoader().loadClass(bd.getBeanClassName());
        }catch (ClassNotFoundException e){
            throw new BeanCreationException(bd.getId() , "Instantiation of bean failed , can't resolve class" , e);
        }
        //获取该bean的构造函数
        Constructor[] candidates = beanClass.getConstructors();
        //返回具体的类型。根据TypedStringValue或者RuntimeBeanReference
        BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this.beanFactory);
        //获取解析后的ConstructorArgument的参数
        ConstructorArgument cargs = bd.getConstructorArgument();
        //返回类型
        SimpleTypeConverter typeConverter = new SimpleTypeConverter();
        //查询对应的构造函数
        for (int i = 0 ; i < candidates.length ; i ++){
            Class[] paramterTypes = candidates[i].getParameterTypes();
            //如果参数数量不对 就继续下一轮查找
            if (paramterTypes.length != cargs.getArgumentCount()){
                continue;
            }
            argsToUse = new Object[paramterTypes.length];
            //参数数量对应上了,判断类型是否一致
            boolean result = this.valuesMatchTypes(paramterTypes,cargs.getArgumentValues(), argsToUse,
                    valueResolver, typeConverter);
            if (result){
                constructorToUse = candidates[i];
                break;
            }
        }

        //找不到一个合适的构造函数
        if(constructorToUse == null){
            throw new BeanCreationException(bd.getId() , "can't find a apporiate construvtor ");
        }

        try {
            //返回通过构造函数实例化的bean
            return constructorToUse.newInstance(argsToUse);
        }catch (Exception e){
            throw new BeanCreationException(bd.getId() , "can't find a create instance useing " + constructorToUse);
        }
    }


    /**
     * 判断构造器是否符合
     * @param parameterTypes
     * @param valueHolders
     * @param argsToUse
     * @param valueResolver
     * @param typeConverter
     * @return
     */
    private boolean valuesMatchTypes(Class [] parameterTypes,
                                     List valueHolders,
                                     Object[] argsToUse,
                                     BeanDefinitionValueResolver valueResolver,
                                     SimpleTypeConverter typeConverter ){
        for (int i = 0 ; i < parameterTypes.length ; i ++){
            ConstructorArgument.ValueHolder valueHolder = valueHolders.get(i);
            //获取参数的值,可能是TypedStringValue,也可能是RuntimeBeanReference
            Object originalValue = valueHolder.getValue();

            try {
                //获取真正的值
                Object resolvedValue = valueResolver.resolveValueIfNecessary(originalValue);
                //如果参数类型是int,但是值是字符串,例如3,还需要转型
                //如果转型失败,则抛出异常。说明这个构造函数不可用
                Object convertedValue = typeConverter.convertIfNecessary(resolvedValue,parameterTypes[i]);
                //转型成功,记录下来
                argsToUse[i] = convertedValue;
            }catch (Exception e){
                logger.error(e);
                return false;
            }

        }
        return true;
    }
}

我们实现了各个零件,到了组装的时刻了,通过ConstructorResolver可以反射生成一个类,而DefaultBeanFactory也有一个createBean方法,这样就会创建两个类,所以我们从createBean方法入手,这里面掉用instantiateBean方法进行初始化,我们修改这个方法。

    private Object instantiateBean(BeanDefinition bd){
        //判断beanDefintion中是否是根据构造函数构造的。
        if (bd.hasConstructorArgumentValues()){
            ConstructorResolver resolver = new ConstructorResolver(this);
            return resolver.autowireConstructor(bd);
        }else {
            //获取类加载器
            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);
            }
        }
    }

这里会有一个编译错误bd.hasConstructorArgumentValues(),我们在BeanDefintion中增加hasConstructorArgumentValues方法,同样我们需要在GenericBeanDefintion中来实现此方法。具体内容可以查看:litespring_06


                                                                                                生活要多点不自量力

你可能感兴趣的:(spring(六):构造器注入)