利用commons-BeanUtils实现简易的依赖注入框架

 

来自   http://www.blogjava.net/ldd600/archive/2008/09/25/231171.html

最近在完成一个小小的framework项目,由于项目中不使用springjuice,自己实现了一个简易的依赖注入框架。

 

  1. 写一个xml文件作为配置的实际例子

 

<? xml version="1.0" encoding="UTF-8" ?>
< beans  xmlns ="http://www.ldd600.com/beanIoc"  xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"  xsi:schemaLocation ="http://www.ldd600.com/beanIoc beanIoc.xsd" >
    
< bean  id ="ftpSend"  class ="com.ldd600.bean.ioc.beans.impl.FTPTransporter" >
        
< params >
            
< param  value ="/ldd600"  key ="uploadFolder" ></ param >
            
< param  value ="com.ldd600.bean.ioc.beans.impl.JugUUIDGenerator"  key ="generator"  converter ="com.ldd600.bean.ioc.converters.UUIDGeneratorConverter" ></ param >
        
</ params >
    
</ bean >
        
< bean  id ="jmsSend"  class ="com.ldd600.bean.ioc.beans.impl.JMSTransporter" >
        
< params >
            
< param  value ="Ldd600Queue"  key ="destName" ></ param >
        
</ params >
    
</ bean >
</ beans >


2.根据这个 xml 文件,定义一个 schema 文件,验证配置文件
<? xml version="1.0" encoding="UTF-8" ?>
< xsd:schema  xmlns:sample ="http://www.ldd600.com/beanIoc"  xmlns:xsd ="http://www.w3.org/2001/XMLSchema"  targetNamespace ="http://www.ldd600.com/beanIoc"  elementFormDefault ="qualified"  attributeFormDefault ="unqualified" >
    
< xsd:complexType  name ="paramsType" >
        
< xsd:annotation >
            
< xsd:documentation >  of java class </ xsd:documentation >
        
</ xsd:annotation >
        
< xsd:sequence  maxOccurs ="unbounded" >
            
< xsd:element  name ="param" >
                
< xsd:complexType >
                    
< xsd:annotation >
                        
< xsd:documentation > key is property name, value is property value </ xsd:documentation >
                    
</ xsd:annotation >
                    
< xsd:simpleContent >
                        
< xsd:extension  base ="sample:leafType" >
                            
< xsd:attribute  name ="key"  type ="sample:notEmptyToken"  use ="required" />
                            
< xsd:attribute  name ="value"  type ="sample:notEmptyToken"  use ="required" />
                            
< xsd:attribute  name ="converter"  type ="sample:classAttributeType"  use ="optional" />
                        
</ xsd:extension >
                    
</ xsd:simpleContent >
                
</ xsd:complexType >
            
</ xsd:element >
        
</ xsd:sequence >
    
</ xsd:complexType >
        
< xsd:complexType  name ="idClassType" >
        
< xsd:all  minOccurs ="0" >
            
< xsd:element  name ="params"  type ="sample:paramsType" >
                
< xsd:key  name ="outerParamsKey" >
                    
< xsd:selector  xpath ="sample:param" />
                    
< xsd:field  xpath ="@key" />
                
</ xsd:key >
            
</ xsd:element >
        
</ xsd:all >
        
< xsd:attribute  name ="id"  type ="sample:notEmptyToken"  use ="required" />
        
< xsd:attribute  name ="class"  type ="sample:classAttributeType" >
            
< xsd:annotation >
                
< xsd:documentation > class name </ xsd:documentation >
            
</ xsd:annotation >
        
</ xsd:attribute >
    
</ xsd:complexType >
    
< xsd:simpleType  name ="leafType" >
        
< xsd:restriction  base ="xsd:string" >
            
< xsd:pattern  value ="(/s)*" />
        
</ xsd:restriction >
    
</ xsd:simpleType >
    
< xsd:simpleType  name ="classAttributeType" >
        
< xsd:restriction  base ="sample:notEmptyToken" >
            
< xsd:pattern  value ="([a-z|0-9|_]+/.)*[A-Z]([A-Z|a-z|0-9|_])*" />
        
</ xsd:restriction >
    
</ xsd:simpleType >
    
< xsd:simpleType  name ="notEmptyToken" >
        
< xsd:restriction  base ="xsd:token" >
            
< xsd:pattern  value ="(/S+/s*)+" />
        
</ xsd:restriction >
    
</ xsd:simpleType >
    
< xsd:element  name ="beans" >
        
< xsd:complexType >
            
< xsd:sequence  maxOccurs ="unbounded" >
                
< xsd:element  name ="bean"  type ="sample:idClassType" />
            
</ xsd:sequence >
        
</ xsd:complexType >
        
< xsd:key  name ="beanIdkey" >
            
< xsd:selector  xpath ="./sample:bean" ></ xsd:selector >
            
< xsd:field  xpath ="@id" ></ xsd:field >
        
</ xsd:key >
    
</ xsd:element >
</ xsd:schema >


 

类型

解释

paramType

表示类的字段,key是字段名,value字段值,converter如果有用来将String转换为所需要的实例。

idClassType

用来表示bean实例,并且该实例有唯一的id

notEmptyToken

非空token

leafType

叶子类型比如<a> </a>,中间允许有space,比如空格、换行符等,但是不能出现其他任何字符。

classAttributeType

负责javaclass全名的string类型,比如com.ldd600.BeanClass



 

 

  1. 解析这个文件,并marshalljava对象,用xmlbeans实现

使用xmlbeans生成schema对应的java类和xmlbeans jaxb需要的元数据。

·         创建xsdConfig文件,内容主要是生成的java类的package名,类simple name的前缀和后缀

< xb:config
 
xmlns:xb =
 "http://xml.apache.org/xmlbeans/2004/02/xbean/config"
>
 
< xb:namespace  uri ="http://www.ldd600.com/beanIoc" >
  
< xb:package > com.ldd600.bean.ioc.config.jaxb </ xb:package >
</ xb:namespace >
  
< xb:namespace  uriprefix ="http://www.ldd600.com/beanIoc" >
    
< xb:prefix > Xml </ xb:prefix >
  
</ xb:namespace >
  
< xb:namespace  uriprefix ="http://www.ldd600.com/beanIoc" >
    
< xb:suffix > Bean </ xb:suffix >
    
</ xb:namespace >
</ xb:config >

 

·         创建一个bat可执行文件,不用每次跑都去cmd下敲一堆命令,累啊。bat文件的内容:

scomp -d classes/beanIoc -src src/beanIoc -out beanIoc.jar beanIoc.xsd beanIoc.xsdconfig

·         双击bat,所需要的java类的.java文件,class文件和xmlbeans jaxb需要的元数据都横空出世了,这些元数据xmlbeans会用来对xml文件进行验证,检查是否符合schema定义的语义规则。

·         java文件和元数据信息都拷贝到eclipse中吧,或者直接把生成的jar包发布到maven repository中,然后再pomdependency它。


 
  1. 解析的过程中,需要生成被依赖注入的对象,并完成属性的动态设定。

4.1   生成依赖注入的对象

生成依赖注入的对象是通过反射直接生成类的实例,在这里要求有public的参数为空的构造函数。

    

  Class clazz  =  Class.forName(sNamclassNamee);

            
return  clazz.newInstance();

 

 

4.2  属性的动态设定

Commons-BeanUtils就是专门免费做这件事的好同志,我们可以利用它来完成,基本类型和一些经常使用的类型,Commons-BeanUtils责无旁贷的提供了自动转换的功能,beanutils不需要我们提供参数的类型,它可以自动完成转换,它是根据getset方法的类型来决定类型的,可参见PropertyDescriptor.getPropertyType()方法。使用方法如下:


if  ( ! PropertyUtils.isWriteable(object, key))  {
                        
throw new ConfigParseException(object.getClass()
                                
+ " doesn't have the property " + key
                                
+ "'s setter method!");
                    }

            String paramVal 
=  paramBean.getValue();
            BeanUtils.setProperty(object, key, paramVal);


 

isWriteable方法判断是否有可用的set方法,如果有就完成属性的动态设置。paramBean就是xml文件中定义的那个param

 

 

但是Beanutils默认帮我们转换的类型为基本类型和所有它已经提供了Converterclass类型,如果我们有特殊的类需要进行动态设定,必须自己提供converter,注册到它的converters map中。这样beanutils兄弟在动态设定属性值的时候,就会根据属性的类型去converter map中把取出该属性类型对应的自定义converter来转换。因为在这里配置文件中配置的都是String 所以我们对Converter接口做了修改:



public   abstract   class  BeanConverter  implements  Converter  {
    
public abstract Object convert(Class type, String value) throws ConfigParseException;

    
public Object convert(Class type, Object value) {
        
return this.convert(type, (String)value);
    }

}



 

我们强制规定了convert方法的参数必须是String,自己提供的converter必须继承BeanConverter抽象类。

String key  =  paramBean.getKey();
                    String converterClsName 
=  paramBean.getConverter();
                    
if  (StringUtils.isNotEmpty(converterClsName))  {
                        Class converterClazz 
= Class.forName(converterClsName);
                        
if (!BeanConverter.class
                                .isAssignableFrom(converterClazz)) 
{
                            
throw new ConfigParseException(
                                    
"converter must extend BeanConverter!");
                        }

                        
                        
if(!this.converters.containsKey(converterClsName)) {
                            
this.converters.put(converterClsName, converterClazz);
                            
// get property type
                            Class propertyClazz = PropertyUtils.getPropertyType(
                                    object, key);
                            
// register converter
                            ConvertUtils.register((Converter) converterClazz
                                    .newInstance(), propertyClazz);
                        }

                    }
 }


4.3属性逻辑规则的检查

在设置好属性以后,这个属性的值并不一定配置的正确,也不一定满足逻辑规则,比如希望int值在35之间,比如希望String 不要为空等等。为了在动态设定完属性后进行逻辑规则的校验,提供了InitializingBean接口



public   interface  InitializingBean  {
    
void afterPropertiesSet() throws Exception;
}



 

实现该接口的类,它们的逻辑规则需要在afterProperties提供,不符合规则的请抛异常,并会在属性设定完后检查


     public   void  afterPropertiesSet()  throws  Exception  {
        
if (StringUtils.isEmpty(uploadFolder)) {
            
throw new IllegalArgumentException(
                    
"upload folder is an empty string!");
        }

        
if (null == generator) {
            
throw new IllegalArgumentException("generator is null!");
        }

    }




if  (object  instanceof  InitializingBean)  {
            ((InitializingBean) object).afterPropertiesSet();
        }



  4.4bean注册到BeanContext

String id  =  idClassTypeBean.getId();
BeanContextFactory.getBeanContext().setBean(id, object);


 

    4.5清理环境

完成属性的动态注入后,还需要清理环境


private   void  cleanConfig()  {
        ConvertUtils.deregister();
        
this.converters = null;
    }

 

5.如何做到基于接口设计

·         Converter提供了基于接口设计的功能:我们可以动态的设置不同的实现。

·         用了该框架,本身就基于接口,我们可以在配置文件中修改bean的实现类。应用程序代码它不关心具体的实现类,它只关心id

 

Transporter transporter  =   (Transporter) BeanContextFactory.getBeanContext().getBean(TransporterParser.getTransportName());

 

不过,这里没有象springjuice那么强大的bean factory功能。因为这个东东只是一个小项目的一小部分,所以功能上满足小项目的需求就足够了。

 

6. Test

 就简单的测了一下,可以看源代码。

 

7.总结

主要是项目是基于接口设计的,所以一些类的实现需要在配置文件里设定,实现类的实例属性也要是可以扩展的,并且提供属性值的逻辑校验,所以就有了这么一个东东。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值