上一节我们完成了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());
}
这里我们提供了两个类ConstructorArgument
、ValueHolder
,ConstructorArgument
是存储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
生活要多点不自量力