一.前言
通过前两篇文章的学习,我们已经对Java中这三个模块的知识有了初步的了解。为了将巩固之前的知识,今天我们将综合运用这三个模块的知识,来实现一个类似Spring中注入的案例。
二.项目结构
简单的描绘了一下项目流程图,如下
流程说明:Ipraser是一个接口,其中定义了一个praserBeans(),返回的是List<Bean>集合,两种数据源分别为XML和注解,对应的是AnnotationPraser和XmlPraser两个解析注解和XML的类,两个类都实现了Ipraser接口,与AnnotationPraser关联的是解析注解相关的辅助类,包括代表各种数据类型的各种注解定义类,还有一个是ClassUtil类用来获取指定包下的Java类。解析数据必须的类有Exception用于定义解析中出现的各种异常,Generator定义了生成类名和方法名称的接口,有两个实现类,Constant用于定义各种常量,Property代表一个bean中定义的所有属性,CollectionProperty是其中的一个变量,代表Java类中的集合属性,BaseProperty继承自CollectionProperty,定义了List、Map和Set中共有的属性,ListProperty、MapProperty和SetProperty继承自BaseProperty分别定义了List、Map和Set中特有的属性。
Config等类使用Builder方式定义了外界的一些配置信息,其中IConfigBuilder是抽象建造,定义了建造流程,Config是最终组装的信息类,其中的内部类ConfigBuilder是具体的构建类,所有信息最终被封装在ConfigEntry中。Method中是ConfigEntry的一个属性,通过枚举形式定义了XML和annotation两种注入方式。
PraserFactory是一个解析器的工厂类,使用策略方式通过参数Method返回不同的具体解析类。
ObjectFactory通过参数List<Bean>作为数据源生成各种对象,类的出口是将生成的对象放入ObjectPool对象池中,该对象实现Ipool接口定义了存取对象的规范,ObjectManager是外界访问的类,可以设置加载参数,然后调用各个PraserFactory、ObjectFactory完成对象的生成,还定义了访问对象的方法,通过访问对象Pool获取对象,然后返回。
三.具体实现
(1).XML读取
首先来看XML解析部分,对应的是XmlPraser,返回的是List<Bean>数据类型。我们这里使用Dom4j这个包来进行XML的解析,XML是和Spring中使用的XML基本一致,下面是缩略的xml。
<bean id="Family" class="com.sunjinxi.spring.test.Family">
<property name="number" value="4" />
<property name="familySalary" value="258.6" />
<!--set和List完全相同 set代表set结合,List代表List集合-->
<property name="employs">
<list>
<!--验证List中存放字符串
可以指定所有基本类型的值
-->
<value>张三丰</value>
<value>李白</value>
</list>
</property>
<!-- List嵌套bean List存放的是引用类型-->
<property name="employs2">
<list>
<bean></bean>
<bean></bean>
</list>
</property>
<!-- List嵌套引用,List存放的是引用-->
<property name="employs3">
<list>
<ref></ref>
<ref></ref>
</list>
</property>
<!-- List嵌套List或者Set或者Map,List中存放的是List,Set或者Map-->
<property name="employs3">
<list>
<List></List>
<set></set>
<map><entry> <key></key>
<value></value></entry></map>
</list>
<!--map标签对应HashMap-->
<property name="map">
<map>
<!--entry标签对应HashMap中的一个映射,key代表键,Value代表值-->
<entry><key>123</key><value>100</value></entry>
<!--Map中的Key可以为引用类型,对应<value-ref>和<value-bean>标签-->
<entry><key>address1</key>
<value_ref>user1</value_ref></entry>
<value_bean><bean></bean></value_bean>
</property>
<!-- set标签代表无序集合,支持基本类型,引用类型,List,Set,Map类型,配置和List标签完全相同--->
<set></set>
我们的注入在XML中支持基本类型的值,List集合,Set集合,Map类型,List嵌套List,Set和Map,引用类型,Set嵌套List,Set和Map,Map的key和value分别支持基本类型和引用类型,不支持和其他集合类型的嵌套。
首先是入口方法
@Override
public List<Bean> praserBeans(String param) {
// TODO Auto-generated method stub
File file=file=new File(param);
SAXReader saxReader=new SAXReader();
Document document=null;
Element rootElement=null;
try {
document=saxReader.read(file);
rootElement=document.getRootElement();
} catch (DocumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return inflateAll(rootElement);
}
首先根据xml的位置参数,创建File对象,使用SaxReader对象读取xml,生成Document文档对象,获取根节点,即RootElement,调用inflateAll方法遍历所有的元素节点。
private List<Bean> inflateAll(Element rootElement) {
List<Bean> beans=null;
beans=new ArrayList<Bean>();
Iterator<Element> beanIterator=rootElement.elementIterator();
while (beanIterator.hasNext()) {
Element beanElement=beanIterator.next();
Bean bean=inflateSingleBean(beanElement);
beans.add(bean);
}
return beans;
}
这段代码拿到所有的bean节点对应的Element对象,调用inflateSingleBean方法把返回的bean属性对象添加到集合中。
private Bean inflateSingleBean(Element beanElement) {
Bean bean=new Bean();
//首先解析各种各样的属性
String objectId=beanElement.attributeValue(Constant.XmlKey.ElementBean.KEY_ATTRIBUTE_ID);
String className=beanElement.attributeValue(Constant.XmlKey.ElementBean.KEY_ATTRIBUTE_CLASS);
String initMethod=beanElement.attributeValue(Constant.XmlKey.ElementBean.KEY_ATTRIBUTE_METHOD_INIT);
String initDestory=beanElement.attributeValue(Constant.XmlKey.ElementBean.KEY_ATTRIBUTE_METHOD_DESTORY);
//初始化属性集合
List<Property> properties=new ArrayList<Property>();
bean.setClassAddress(className);
bean.setDestoryMethod(initDestory);
bean.setInitMethod(initMethod);
bean.setId(objectId);
bean.setProperties(properties);
//拿到子元素,变量子元素
Iterator<Element> propertyIterator=beanElement.elementIterator();
while (propertyIterator.hasNext()) {
//遍历每一个属性
Element propertyElement=propertyIterator.next(); //初始化Property属性
Property property=new Property();
List<Element> elements=propertyElement.elements();
Element collectionElement=null;
//设置属性的名称
String propertyName=propertyElement.attributeValue(Constant.XmlKey.ElementProperty.KEY_ATTRIBUTE_NAME);
property.setName(propertyName);
//判断Property是否有子节点,有代表是一个集合属性,否则是普通属性
if (elements!=null&&elements.size()!=0) {
//属性值为集合,获取集合节点
collectionElement=elements.get(0);
CollectionProperty collectionValue=null;
String collectionName=collectionElement.getName();
if (collectionName.equals(Constant.XmlKey.ElementCollection.KEY_ELEMENT_LIST)) {
//处理List集合
collectionValue=inflatePropertyListAndSet(collectionElement,new ListProperty());
property.setType(Constant.Property.TYPE_LIST);
}else if (collectionName.equals(Constant.XmlKey.ElementCollection.KEY_ELEMENT_SET)) {
//处理set集合
collectionValue=inflatePropertyListAndSet(collectionElement,new SetProperty());
property.setType(Constant.Property.TYPE_SET);
}else if (collectionName.equals(Constant.XmlKey.ElementCollection.KEY_ELEMETN_MAP)) {
//处理Map集合
collectionValue=inflatePropertyMap(collectionElement);
property.setType(Constant.Property.TYPE_MAP);
}
property.setCollectionValue(collectionValue);
properties.add(property);
continue;
}
//如果属性值为引用值或者基本数据类型
String propertyValue=propertyElement.attributeValue(Constant.XmlKey.ElementProperty.KEY_ATTRIBUTE_VALUE);
String propertyRef=propertyElement.attributeValue(Constant.XmlKey.ElementProperty.KEY_ATTRIBUTE_REF);
property.setValue(propertyValue);
property.setRef(propertyRef);
if (propertyRef==null) {
property.setType(Constant.Property.TYPE_VALUE);
}else {
property.setType(Constant.Property.TYPE_REF);
}
properties.add(property);
//取出list
}
return bean;
}
这段代码首先从Element对象中获取相应的属性,包括id,class,init-method和destroy-method,这些常量都被封装在Constant类中方便查找和更改,然后初始化List<Property>,准备将所有的属性放入该集合中。之后取出属性节点,判断属性节点下是否存在子节点,如果存在则说明属性的值是Set,List或者Map类型,如果是List或者Set则调用inflatePropertyListAndSet,是Map则调用inflatePropertyMap方法,返回的是一个CollectionProperty上转型对象。如果是基本类型的数值的话,则直接取出属性中的值,设置到Property对象中即可。
再来看解析List和Set标签属性的方法inflatePropertyListAndSet
private CollectionProperty inflatePropertyListAndSet(Element element,BaseProperty result) {
// TODO Auto-generated method stub
Iterator<Element> iterator=element.elementIterator();
List<String> valueList=null;
List<String> refList=null;
List<Bean> beans=null;
List<CollectionProperty> collectionProperties=null;
while (iterator.hasNext()) {
Element childElement=iterator.next();
String childElementName=childElement.getName();
String value=childElement.getText();
int type=getCollectinValueType(childElementName);
result.setType(type);
switch (type) {
case Constant.Property.TYPE_VALUE:
if (valueList==null) {
valueList=new ArrayList<String>();
}
valueList.add(value);
result.setValueList(valueList);
break;
case Constant.Property.TYPE_REF:
if (refList==null) {
refList=new ArrayList<String>();
}
refList.add(value);
result.setRefList(refList);
break;
case Constant.Property.TYPE_BEAN:
if (beans==null) {
beans=new ArrayList<Bean>();
}
Bean bean=inflateSingleBean(childElement);
beans.add(bean);
result.setBeans(beans);
break;
case Constant.Property.TYPE_SET:
if (collectionProperties==null) {
collectionProperties=new ArrayList<CollectionProperty>();
}
CollectionProperty setProperty=inflatePropertyListAndSet(childElement,new SetProperty());
collectionProperties.add(setProperty);
result.setCollectionProperties(collectionProperties);
break;
case Constant.Property.TYPE_LIST:
if (collectionProperties==null) {
collectionProperties=new ArrayList<CollectionProperty>();
}
CollectionProperty listProperty=inflatePropertyListAndSet(childElement,new ListProperty());
collectionProperties.add(listProperty);
result.setCollectionProperties(collectionProperties);
break;
case Constant.Property.TYPE_MAP:
if (collectionProperties==null) {
collectionProperties=new ArrayList<CollectionProperty>();
}
CollectionProperty mapProperty=inflatePropertyMap(childElement);
collectionProperties.add(mapProperty);
result.setCollectionProperties(collectionProperties);
break;
default:
break;
}
}
return result;
}
这段代码首先遍历list或者set标签下的元素,list或者set标签支持基本数据类型value标签,引用数据类型ref标签和bean标签,以及set,list或者map标签,在这里是调用getCollectinValueType获取标签的类型,其实就是为每个标签对应了相应的数值常量,方便我们使用switch语句分类讨论,如果是value标签,我们则将对应的值放到valueList集合中,ref标签则放到refList中,bean标签则放到beans集合中,set或list标签我们会递归调用inflatePropertyListAndSet方法,然后把对应的属性放到collectionProperties集合中,map标签也是一样,只不过调用的是inflatePropertyMap方法。接着来看inflatePropertyMap方法的实现。
private CollectionProperty inflatePropertyMap(Element element) {
// TODO Auto-generated method stub
Iterator<Element> iterator=element.elementIterator();
MapProperty mapProperty=new MapProperty();
List<MapEntryProperty> mapEntryProperties=new ArrayList<MapEntryProperty>();
mapProperty.setEntrys(mapEntryProperties);
while (iterator.hasNext()) {
Element childElement=iterator.next();
Iterator<Element> entryIterator=childElement.elementIterator();
MapEntryProperty mapEntryProperty=null;
//解析Entry对象中的key,value
Element keyElement=entryIterator.next();
Element valueElement=entryIterator.next();
String keyName=keyElement.getName();
String valueName=valueElement.getName();
int keyType=getMapValueType(keyName);
int valueType=getMapValueType(valueName);
if (keyType==Constant.Property.TYPE_BEAN&&valueType==Constant.Property.TYPE_BEAN) {
mapEntryProperty=new MapEntryProperty<Bean, Bean>();
}else if (keyType==Constant.Property.TYPE_BEAN) {
mapEntryProperty=new MapEntryProperty<Bean, String>();
}else if (valueType==Constant.Property.TYPE_BEAN) {
mapEntryProperty=new MapEntryProperty<String, Bean>();
}else {
mapEntryProperty=new MapEntryProperty<String, String>();
}
switch (keyType) {
case Constant.Property.TYPE_VALUE:
mapEntryProperty.setKeyType(MapEntryType.value);
mapEntryProperty.setKey(keyElement.getText());
break;
case Constant.Property.TYPE_REF:
mapEntryProperty.setKey(keyElement.getText());
mapEntryProperty.setKeyType(MapEntryType.ref);
break;
case Constant.Property.TYPE_BEAN:
Bean bean=inflateSingleBean(keyElement.element(Constant.XmlKey.ElementBean.KEY_ELEMENT_NAME));
mapEntryProperty.setKey(bean);
mapEntryProperty.setKeyType(MapEntryType.bean);
break;
default:
break;
}
switch (valueType) {
case Constant.Property.TYPE_VALUE:
mapEntryProperty.setValueType(MapEntryType.value);
mapEntryProperty.setValue(valueElement.getText());
break;
case Constant.Property.TYPE_REF:
mapEntryProperty.setValueType(MapEntryType.ref);
mapEntryProperty.setValue(valueElement.getText());
break;
case Constant.Property.TYPE_BEAN:
Bean bean=inflateSingleBean(valueElement.element(Constant.XmlKey.ElementBean.KEY_ELEMENT_NAME));
mapEntryProperty.setValue(bean);
mapEntryProperty.setValueType(MapEntryType.bean);
break;
default:
break;
}
System.out.println(mapEntryProperty.toString());
mapEntryProperties.add(mapEntryProperty);
}
return mapProperty;
}
这段代码首先遍历map下的所有entry元素,之后取出entry下的key和value键值对,在这里key和value支持三种标签,key,value代表的是基础数据类型,value_bean和value_ref代表值是引用类型,key_ref和key_bean代表键是引用类型。每一个entry标签对应的是一个MapEntryProperty对象,首先看看MapEntryProperty对象。
//代表一个map属性
public class MapProperty extends CollectionProperty{
private List<MapEntryProperty> entrys;
public List<MapEntryProperty> getEntrys() {
return entrys;
}
public void setEntrys(List<MapEntryProperty> entrys) {
this.entrys = entrys;
}
public static class Support{
public static Class<?>[] TYPE_SUPPORT={HashMap.class,LinkedHashMap.class};
public static Class<?> TYPE_SUPPORT_INTERFACE=Map.class;
}
@Override
public String toString() {
return "MapProperty [entrys=" + entrys + "]";
}
/**
* @author Administrator
*代表键和值都是基本类型
*/
public static class MapEntryProperty<k,v>{
@Override
public String toString() {
return "MapEntryProperty [keyType=" + keyType + ", valueType="
+ valueType + ", key=" + key + ", value=" + value + "]";
}
private MapEntryType keyType;
private MapEntryType valueType;
private k key;
private v value;
public MapEntryType getValueType() {
return valueType;
}
public void setValueType(MapEntryType valueType) {
this.valueType = valueType;
}
public MapEntryType getKeyType() {
return keyType;
}
public void setKeyType(MapEntryType keyType) {
this.keyType = keyType;
}
public k getKey() {
return key;
}
public void setKey(k key) {
this.key = key;
}
public v getValue() {
return value;
}
public void setValue(v value) {
this.value = value;
}
}
/**键和值的两种类型
* @author Administrator
*
*/
public enum MapEntryType{
value,
ref,
bean
}
}
MapProperty类代表的是map标签,成员变量entrys存放的是entry键值对的集合,静态内部类MapEntryProperty代表的是一个entry标签,内部有keyType和valueType两个属性,分别代表键和值的类型,MapEntryType是一个枚举类,有ref,value和bean三种类型,由于在初始化对象的时候,三种类型的处理是不同的所以这里分割开来。key和value代表的是真实值,因为类型不同则类型可能是字符串类型也可能是bean类型,所以使用泛型参数来代替。Support内部类代表支持的集合类型的字节码对象,我们这里支持JavaBean声明的类型可以是HashMap或者LinkedHashMap,也可以支持Map接口声明,会默认以HashMap类型来处理。
回到inflatePropertyMap中,我们会根据key和value类型的不同来在初始化MapEntryProperty传入不同的泛型实参,之后对key和value分别分类讨论,根据不同的类型,mapEntryProperty对象设置不同的MapEntryType和value,key值。基本类型则调用Element对象的getText()方法取值,引用类型则递归调用inflateSingleBean方法获得Bena对象。
到这里,xml的解析工作已经做完了,最终返回的是List<Bean>对象,所有的属性都放在这里。
(2).Annotation读取
接着来看注解读取的部分,这里主要使用注解和反射的知识来完成。
@Override
public List<Bean> praserBeans(String param) {
// TODO Auto-generated method stub
List<Bean> beans=new ArrayList<Bean>();
List<Class<?>> clazzs=ClassUtil.getClasses(param);
for (int i = 0; i < clazzs.size(); i++) {
Class clazz=clazzs.get(i);
Bean bean=getBeanByClass(clazz);
if (bean!=null) {
beans.add(bean);
}
}
return beans;
}
首先根据包,获取指定包下的所有类的Class字节码对象,然后调用getBeanByClass方法获取指定类的Bean对象。
private Bean getBeanByClass(Class<?> clazz){
Annotation clazzAnnotation=clazz.getAnnotation(BeanType.class);
if (clazzAnnotation==null) {
return null;
}
Bean bean=new Bean();
BeanType beanType=(BeanType) clazzAnnotation;
String beanId=beanType.id();
bean.setId(beanId);
bean.setClassAddress(clazz.getPackage().getName()+"."+clazz.getSimpleName());
System.out.println(bean.getClassAddress());
Field[] declaredFields = clazz.getDeclaredFields();
List<Property> properties=new ArrayList<Property>();
bean.setProperties(properties);
for (int j = 0; j < declaredFields.length; j++) {
Field field=declaredFields[j];
Annotation[] annotations=field.getAnnotations();
if (annotations.length==0) {
continue;
}
Property property=new Property();
properties.add(property);
String fieldName=field.getName();
property.setName(fieldName);
for (int k = 0; k < annotations.length; k++) {
Annotation annotation=annotations[k];
if (field.isAnnotationPresent(BasicType.class)) {
BasicType basicType=(BasicType) annotation;
String value=getBasicProperty(basicType);
property.setValue(value);
property.setType(Constant.Property.TYPE_VALUE);
}else if (field.isAnnotationPresent(ListType.class)) {
ListType listType=(ListType) annotation;
ListProperty listProperty=getListProperty(listType);
property.setCollectionValue(listProperty);
property.setType(Constant.Property.TYPE_LIST);
}else if (field.isAnnotationPresent(SetType.class)) {
SetType setType=(SetType) annotation;
SetProperty setProperty=getSetProperty(setType);
property.setCollectionValue(setProperty);
property.setType(Constant.Property.TYPE_SET);
}else if (field.isAnnotationPresent(MapType.class)) {
MapType mapType=(MapType) annotation;
MapProperty mapProperty=getMapProperty(mapType);
property.setCollectionValue(mapProperty);
property.setType(Constant.Property.TYPE_MAP);
}else if (field.isAnnotationPresent(RefType.class)) {
RefType refType=(RefType) annotation;
String ref=getRefProperty(refType);
property.setRef(ref);
property.setType(Constant.Property.TYPE_REF);
}
}
}
return bean;
}
可以看到注解的解析相对简单,因为注解的局限性,暂时无法注入集合嵌套集合这种类型的数据类型,因为注解的值不能是注解元素本身,所以无法完成递归调用。这段代码首先获取BeanType注解,只有添加这个注解的才会被我们注入,如果没有这个注解则注解返回。之后调用getDeclaredFields方法获取该类的所有属性,然后遍历所有的属性。获取属性上作用的所有Annotation,遍历所有的Annotation,调用Filed的isAnnotationPresent方法判断是否有指定的注解作用在Field上。类型分别是BasicType,ListType,SetType,MapType,RefType,分别调用对应的读取注解的方法,然后给Property设置获取的注解值。
获取引用类型
private String getRefProperty(RefType refType){
return refType.id();
}
获取基本数据类型
private String getBasicProperty(BasicType basicType){
return basicType.value();
}
获取list类型
private ListProperty getListProperty(ListType listType){
ListProperty listProperty=new ListProperty();
listProperty.setType(Constant.Property.TYPE_VALUE);
String[] values = listType.value();
List<String> valueList=new ArrayList<String>();
for (int i = 0; i < values.length; i++) {
String value=values[i];
valueList.add(value);
}
listProperty.setValueList(valueList);
return listProperty;
}
获取set类型
private SetProperty getSetProperty(SetType setType){
SetProperty setProperty=new SetProperty();
setProperty.setType(Constant.Property.TYPE_VALUE);
String[] values = setType.value();
List<String> valueList=new ArrayList<String>();
for (int i = 0; i < values.length; i++) {
String value=values[i];
valueList.add(value);
}
setProperty.setValueList(valueList);
return setProperty;
}
获取map类型
private MapProperty getMapProperty(MapType mapType){
MapProperty mapProperty=new MapProperty();
List<MapEntryProperty> mapEntryProperties=new ArrayList<MapProperty.MapEntryProperty>();
MapEntry[] mapEntries=mapType.entry();
for (int i = 0; i < mapEntries.length; i++) {
MapEntryProperty<String, String> mapEntryProperty=new MapEntryProperty<String, String>();
mapEntryProperty.setKeyType(MapEntryType.value);
mapEntryProperty.setValueType(MapEntryType.value);
MapEntry mapEntry=mapEntries[i];
String key=mapEntry.key();
String value=mapEntry.value();
mapEntryProperty.setKey(key);
mapEntryProperty.setValue(value);
mapEntryProperties.add(mapEntryProperty);
}
mapProperty.setEntrys(mapEntryProperties);
return mapProperty;
}
到这里,我们的注解读取的工作也完成了。
(3).反射注入
接着是最核心的工作,所有的反射注入工作都在ObjectFactory类中完成。
首先看构造方法
public ObjectFactory(List<Bean> beans) {
// TODO Auto-generated constructor stub
classMap=new HashMap<String, Class<Object>>();
this.mBeans=beans;
this.mObjectManager=ObjectManager.getInstance();
this.mWrapClasses=new Class[]{Integer.class,Double.class,Float.class,
Short.class,Long.class,Byte.class,Boolean.class,Character.class};
this.mBasiClasses=new Class[]{int.class,double.class,float.class,
short.class,long.class,byte.class,boolean.class,char.class};
}
classMap是用来缓存Class字节码对象的,获取Class对象时首先会去缓存中查找如果没有再去调用方法加载。mWrapClasses和mBasiClasses是两个数组,前者放的是基础数据类型的包装类对象,后者是基础数据类型的Class对象。因为int.class和Integer.class不是一个对象,为了方便我们从字符串中解析出对应的数据类型值,我们需要将其转换成对应的包装类型,然后通过反射调用包装类型的静态方法valueOf方法获取真实的数据。
入口方法
public Object loadObject(Bean bean) throws ReferenceException, FieldTypeException{
String classAddress=bean.getClassAddress();
Class<Object> clazz=classMap.get(classAddress);
Object object=null;
if (clazz==null) {
ClassLoader classLoader=ObjectFactory.class.getClassLoader();
try {
//不进行类的初始化
//clazz=(Class<Object>) Class.forName(classAddress);
clazz=(Class<Object>) classLoader.loadClass(classAddress);
classMap.put(classAddress, clazz);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
}
//首先创建对象
try {
object = clazz.newInstance();
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
List<Property> properties=bean.getProperties();
for (int i = 0; i < properties.size(); i++) {
Property property=properties.get(i);
int type=property.getType();
String fieldName=property.getName();
Field field=null;
Class rawType=null;
try {
field = clazz.getDeclaredField(fieldName);
rawType=field.getType();
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
switch (type) {
case Constant.Property.TYPE_SET:
//注入set集合
Type setType=field.getGenericType();
SetProperty setProperty=(SetProperty) property.getCollectionValue();
Collection setResult=getListAndSet(setType,setProperty,SetProperty.Support.TYPE_SUPPORT_CLASS,SetProperty.Support.TYPE_SUPPORT_INTERFACE);
inject(clazz, object, fieldName, rawType, setResult);
break;
case Constant.Property.TYPE_LIST:
//注入List集合
Type listType=field.getGenericType();
ListProperty listProperty=(ListProperty) property.getCollectionValue();
Collection listResult=getListAndSet(listType,listProperty,
ListProperty.Support.TYPE_SUPPORT_CLASS,ListProperty.Support.TYPE_SUPPORT_INTERFACE);
inject(clazz, object, fieldName, rawType, listResult);
break;
case Constant.Property.TYPE_MAP:
//注入Map集合
//key key-ref key可以使基本数据类型,也可以是引用类型
//value 基本数据类型 value-ref配置 中的引用类型
//1.首先判断对应的变量类型是不是map,并确定targetClass
//2.判断泛型参数的类型与xml文件中是否一致
//3.填充数据
//1
Type mapType=field.getGenericType();
MapProperty mapProperty=(MapProperty) property.getCollectionValue();
Map map = getMap(mapProperty,mapType);
inject(clazz, object, fieldName, rawType, map);
break;
case Constant.Property.TYPE_REF:
//value是引用值
Object refObject=getRefObject(property.getRef());
try {
System.out.println(fieldName);
Class fieldClazz=field.getType();
inject(clazz, object, fieldName, fieldClazz, refObject);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
case Constant.Property.TYPE_VALUE:
//Vlaue是基本类型的值
System.out.println("rawType:"+rawType);
Object objectBasic=pareseBasicValue(getWrapFromBasic(rawType), property.getValue());
inject(clazz, object, fieldName, rawType, objectBasic);
}
}
return object;
}
首先根据指定的全类名从缓存Map中查找对应的Class字节码对象,如果为空则去加载Class对象,然后放到缓存map中。首先创建出对象,之后遍历List<Property>属性集合 ,开始注入各种值。然后取出属性的名称,调用getDeclaredField方法,根据属性的名称获取Field对象,调用Field对象的getType方法获取属性的数据类型,这里获取的是一个原始数据类型。
接下来调用Property的getType方法获取属性的类型,然后分类讨论。
首先讨论set集合,如果是set集合,则必须获取泛型参数的类型,这里是调用的Field的getGenericType方法,然后调用getListAndSet方法,该方法返回的是一个Collection上转型对象,这个对象便是根据属性类型创建出来的对象,其中已经设定好了值,然后进行注入。List集合的处理和Set集合基本相同。我们重点来看getListAndSet方法。
private Collection<?> getListAndSet(Type paramType,
BaseProperty listProperty,Class[] supportClasses,Class supportInterface)
throws FieldTypeException, ReferenceException {
//首先进行类型校验和Class选取
//第一步进行校验
ParameterizedType parameterizedType=null;
if (!(paramType instanceof ParameterizedType)) {
//不是List或set接口的子类,或者没有指定要注入的类型形参
throw new FieldTypeException(Constant.Exeception.EXECEPTION_FIELDTYPE);
} else {
parameterizedType=(ParameterizedType) paramType;
}
Class paramClazz=(Class) parameterizedType.getRawType();
//第二步 确定集合的Class对象
Type[] types=parameterizedType.getActualTypeArguments();
Type genericParamType= types[0];
Collection resultCollection=null;
//类型错误抛出异常
//注入集合类型的值
//集合中的数据类型 基本数据类型<value></value>、集合类型 List Map set、引用类型<ref></ref> 、bean
//获取泛型数据类型
//获取修饰符
//如果Class是一个List接口,默认创建ArrayList创建数据
//否则分为ArrayList和LinkeList来处理
//如果指定变量不是一个List集合类型则抛出异常
//为了保持数据的原有类型,在初始化对象的时候分别采用ArrayList和LinkeList的字节码来初始化
//但是寻找方法的时候依旧采用原始类型
//只处理ArrayList和LinkList,其他全部抛出异常
//注入前首先判断变量类型是否是集合类型
Class<?> targetClass=null;
//支持ArrayList和LinkedList,其余类型抛出异常
for (int i = 0; i < supportClasses.length; i++) {
Class<?> support= supportClasses[i];
if (support.isAssignableFrom(paramClazz)) {
targetClass=support;
break;
}
}
if (targetClass==null) {
if (supportInterface.isAssignableFrom(paramClazz)) {
targetClass=supportClasses[0];
}else {
throw new FieldTypeException(Constant.Exeception.EXECEPTION_FIELDTYPE);
}
}
//根据不同类型的值开始创建对象
int listValueType=listProperty.getType();
switch (listValueType) {
case Constant.Property.TYPE_VALUE:
//集合中是基本类型的值
try {
List<String> valueList=listProperty.getValueList();
Class genericParamClass=(Class) genericParamType;
resultCollection=getBasicList(genericParamClass, valueList, targetClass);
} catch (InstantiationException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (IllegalAccessException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
break;
case Constant.Property.TYPE_BEAN:
//集合中的值是一个bean
try {
//元素是bean类型
resultCollection=(Collection) targetClass.newInstance();
List<Bean> beanList=listProperty.getBeans();
for (int j = 0; j < beanList.size(); j++) {
Bean beanValue=beanList.get(j);
Object objectValue=loadObject(beanValue);
resultCollection.add(objectValue);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
case Constant.Property.TYPE_REF:
//集合中的值是一个引用
try {
resultCollection=(Collection) targetClass.newInstance();
List<String> list=listProperty.getRefList();
for (int j = 0; j <list.size(); j++) {
String ref=list.get(j);
Object resultObject=null;
for (int k = 0; k <mBeans.size(); k++) {
Bean beanRef=mBeans.get(k);
if (beanRef.getId().equals(ref)) {
if (beanRef.isInject()) {
resultObject=mObjectManager.getObjectById(ref);
//退出内层循环
break;
}else {
resultObject=loadObject(beanRef);
break;
}
}
}
resultCollection.add(resultObject);}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
case Constant.Property.TYPE_LIST:
try {
resultCollection=(Collection) targetClass.newInstance();
List<CollectionProperty> properties=listProperty.getCollectionProperties();
for (int j = 0; j <properties.size(); j++) {
ListProperty collectionProperty=(ListProperty) properties.get(j);
Collection<?> result=getListAndSet(genericParamType,collectionProperty,ListProperty.Support.TYPE_SUPPORT_CLASS,ListProperty.Support.TYPE_SUPPORT_INTERFACE);
resultCollection.add(result);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
case Constant.Property.TYPE_SET:
try {
resultCollection=(Collection) targetClass.newInstance();
List<CollectionProperty> propertiesSet=listProperty.getCollectionProperties();
for (int j = 0; j <propertiesSet.size(); j++) {
SetProperty collectionProperty=(SetProperty) propertiesSet.get(j);
Collection<?> result=getListAndSet(genericParamType,collectionProperty,SetProperty.Support.TYPE_SUPPORT_CLASS,SetProperty.Support.TYPE_SUPPORT_INTERFACE);
resultCollection.add(result);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
case Constant.Property.TYPE_MAP:
try {
resultCollection=(Collection) targetClass.newInstance();
List<CollectionProperty> propertiesMap=listProperty.getCollectionProperties();
for (int j = 0; j <propertiesMap.size(); j++) {
MapProperty mapProperty=(MapProperty) propertiesMap.get(j);
Map result=getMap(mapProperty, genericParamType);
resultCollection.add(result);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
default:
break;
}
return resultCollection;
}
这段代码可以分为三个步骤,首先是JavaBean中声明的属性类型是否和相应的集合类型对应。首先判断paramType是不是ParameterizedType类型的实例,ParameterizedType代表的是包含泛型参数的类型,如果在定义该属性时没有指定泛型参数,则无法确定元素的数据类型,也就是无法进行后续的注入,所有直接抛出异常。
第二部确定要使用的Clas类型,也就是在反射时我们要使用的是ArrayList字节码还是LinkedList字节码这个问题。在这里我们支持ArrayList和LinkedList以及HashSet以及LinkedHashSet类型,对于声明为接口类型,则默认为ArrayList和HashSet字节码。isAssignableFrom是用来判断支持的类型和实际的类型是否相同,如果是接口类型则使用默认的字节码对象进行初始化。
第三部根据配置的不同类型的值开始初始化对象,基本类型的值调用getBasicList方法获取对应的集合。bean引用类型需要我们递归调用loadObject方法,然后将返回的Object对象添加到集合中。ref类型我们要首先判断对应的对象是否已经被初始化,没有则加载该对象,有则将对象添加到集合张。对于List,set和map类型则需要递归调用getListAndSet和getMap方法。
private Map getMap(MapProperty mapProperty,Type mapType)
throws FieldTypeException, ReferenceException {
//确定TargetClass
ParameterizedType parameterizedType=null;
if (!(mapType instanceof ParameterizedType)) {
//没有泛型直接抛出异常
throw new FieldTypeException(Constant.Exeception.EXECEPTION_FIELDTYPE);
} else {
parameterizedType=(ParameterizedType) mapType;
}
Class targetClass=null;
Class mapClazz=(Class)parameterizedType.getRawType();
Class[] supportClass=MapProperty.Support.TYPE_SUPPORT;
for (int j = 0; j < supportClass.length; j++) {
if (mapClazz.equals(supportClass[j])) {
targetClass=supportClass[j];
break;
}
}
if (targetClass==null) {
if (!MapProperty.Support.TYPE_SUPPORT_INTERFACE.isAssignableFrom(mapClazz)) {
throw new FieldTypeException(Constant.Exeception.EXECEPTION_FIELDTYPE);
}else {
targetClass=supportClass[0];
}
}
//2进行初步的验证
Type[] mapTypes=parameterizedType.getActualTypeArguments();
Class keyClazz=(Class) mapTypes[0];
Class valueClazz=(Class) mapTypes[1];
List<MapEntryProperty> mapEntryProperties=mapProperty.getEntrys();
MapEntryProperty mapEntryProperty=mapEntryProperties.get(0);
int valueType=getGenericParamType(valueClazz);
int keyType=getGenericParamType(keyClazz);
MapEntryType keyMapType=mapEntryProperty.getKeyType();
MapEntryType valueMapType=mapEntryProperty.getValueType();
if ((keyType!=-1&&(keyMapType==MapEntryType.ref||keyMapType==MapEntryType.bean))
||(valueType!=-1&&(valueMapType==MapEntryType.ref||valueMapType==MapEntryType.bean))) {
throw new FieldTypeException(Constant.Exeception.EXECEPTION_FIELDTYPE);
}
if ((keyType==-1&&keyMapType==MapEntryType.value)||(valueType==-1&&valueMapType==MapEntryType.value)) {
throw new FieldTypeException(Constant.Exeception.EXECEPTION_FIELDTYPE);
}
//.填充数据
//直接通过对应的calss对象初始化对象,不存在泛型类型,所以不需要对不同的泛型参数初始化不同的对象
Map map=null;
try {
map=(Map) targetClass.newInstance();
for (int j = 0; j < mapEntryProperties.size(); j++) {
MapEntryProperty entryProperty=mapEntryProperties.get(j);
MapEntryType entryKeyType=entryProperty.getKeyType();
MapEntryType entryValueType=entryProperty.getValueType();
Object key=getMapValueByType(entryKeyType, entryProperty, true, keyClazz);
Object value=getMapValueByType(entryValueType, entryProperty, false, valueClazz);
map.put(key, value);
}
} catch (InstantiationException | IllegalAccessException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
return map;
}
这个方法的初步验证和上一个方法相同,不同的是这里我们要对Java类在声明map是做传入的泛型实参的类型和在xml中的类型,既key和value的类型分别进行校验。getGenericParamType方法获取的是泛型参数的类型,来看这个方法
public int getGenericParamType(Class<?> filedType){
int type=-1;
if (filedType.isAssignableFrom(Integer.class)) {
type=Constant.Data.TYPE_INTEGER;
}else if (filedType.isAssignableFrom(Double.class)) {
type=Constant.Data.TYPE_DOUBLE;
}else if (filedType.isAssignableFrom(Long.class)) {
type=Constant.Data.TYPE_LONG;
}else if (filedType.isAssignableFrom(Float.class)) {
type=Constant.Data.TYPE_FLOAT;
}else if (filedType.isAssignableFrom(Byte.class)) {
type=Constant.Data.TYPE_BYTE;
}else if (filedType.isAssignableFrom(Short.class)) {
type=Constant.Data.TYPE_SHORT;
}else if (filedType.isAssignableFrom(Boolean.class)) {
type=Constant.Data.TYPE_BOOLEAN;
}else if (filedType.isAssignableFrom(Character.class)) {
type=Constant.Data.TYPE_CHAR;
}else if (filedType.isAssignableFrom(String.class)) {
type=Constant.Data.TYPE_STRING;
}
return type;
}
我们这里之支持基本数据类型和引用类型,引用类型则返回-1。验证通过后开始进行注入,遍历entry集合,然后调用getMapValueByType方法获取key和value的值
//如何处理不同类型的注入
//1.判断所有的基础类型,解析不同的类型
//2.通过反射调用参数中对象的valueOf方法解析得到数据
public Object getMapValueByType(MapEntryType mapEntryType,MapEntryProperty mapEntryProperty,boolean isKey,Class clazz) throws ReferenceException, FieldTypeException{
System.out.println("注入bean"+mapEntryType.toString());
Object resultObject=null;
if(mapEntryType.equals(MapEntryType.value)){
//基本类型的值
String value=(String) (isKey?mapEntryProperty.getKey():mapEntryProperty.getValue());
if (clazz.isAssignableFrom(String.class)) {
resultObject=value;
}else {
Method method;
try {
method = clazz.getDeclaredMethod("valueOf", String.class);
resultObject=method.invoke(null, value);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}else if (mapEntryType.equals(MapEntryType.ref)) {
String value=(String) (isKey?mapEntryProperty.getKey():mapEntryProperty.getValue());
resultObject=getRefObject(value);
}else if (mapEntryType.equals(MapEntryType.bean)) {
Bean bean=(Bean) (isKey?mapEntryProperty.getKey():mapEntryProperty.getValue());
System.out.println("注入bean"+bean.toString());
resultObject=loadObject(bean);
}
return resultObject;
}
依旧是分类讨论,这里要注意的问题是对于基础数据类型,如何正确获取对应的类型的值,因为我们从注解或者xml中获取的值是字符串类型的,所有要把他们转化为8中对应的基础数据类型。刚开始的时候是对每个类型进行判断,然后调用对应类型的valueOf方法。其实我们可以再次进行反射,获取对应数据类型的valueOf方法的Method对象,因为所有的包装类型都有valueOf方法,因为他们都继承自Number类。
private Object pareseBasicValue(Class paramclazz,String value){
Object resultObject=null;
if (paramclazz.equals(String.class)) {
resultObject=value;
}else {
Method method=null;
try {
method=paramclazz.getDeclaredMethod("valueOf", String.class);
resultObject=method.invoke(null, value);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return resultObject;
}
这提取基础类型值的方法,通过反射,大大简化了代码。
private void inject(Class<Object> clazz, Object object, String keyName,
Class filedClass, Object value) {
String methodName=MethodNameGenerator.getInstance()
.generateName(MethodNameGenerator.PER_SETTER,
keyName);
Method method;
try {
method = clazz.getMethod(methodName, filedClass);
method.invoke(object, value);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
这是最终注入的代码,通过MethodNameGenerator来和成对应的方法名称,到这里核心的代码就介绍完了。
Config config=new ConfigBuilder().setInjectMethod(Method.annotation)
.setInjectParam("com.sunjinxi.spring.annotationinject").create();
ObjectManager objectManager=ObjectManager.getInstance();
objectManager.setConfig(config);
User user=(User) objectManager.getObjectById("User");
System.out.println(user.toString());
通过这样的代码,我们便可以轻松完成对JavaBean类进行各种信息的注入,是不是很方便呢。
四.反射的一些问题的总结
(1). 如何获取数据的类型
public static void getType(Type type){
if (type instanceof GenericArrayType) {
GenericArrayType genericArrayType=(GenericArrayType) type;
Type componentType=genericArrayType.getGenericComponentType();
System.out.println("componentType"+componentType);
getType(componentType);
}else if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType=(ParameterizedType) type;
Type[] arguments=parameterizedType.getActualTypeArguments();
for (int i = 0; i < arguments.length; i++) {
Type argumentsType=arguments[0];
System.out.println("ParameterizedType"+argumentsType);
getType(argumentsType);
}
}else if (type instanceof WildcardType) {
WildcardType wildcardType=(WildcardType) type;
Type[] lowerTypes= wildcardType.getLowerBounds();
Type[] upperTypes=wildcardType.getUpperBounds();
if (lowerTypes.length!=0) {
System.out.println("WildcardTypeLower"+lowerTypes[0]);
getType(lowerTypes[0]);
}else if (upperTypes.length!=0) {
System.out.println("WildcardTypeUpper"+upperTypes[0]);
getType(upperTypes[0]);
}
}else {
System.out.println("BasicType"+type);
System.out.println("我是基本数据类型");
}
}
//测试代码
Class<TestDom4j> clazz=TestDom4j.class;
Field field=null;
try {
field=clazz.getDeclaredField("listParam");
Type type=field.getGenericType();
getType(type);
} catch (NoSuchFieldException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
通过上面这段代码便可以获取各种对应的数据类型,在获取类型的时候一定要调用getGenericType()这个方法,否则无法获取的将是原始的数据类型,无法转换成其他数据类型。首先判断是不是GenericArrayType 类型,是的话则调用getGenericComponentType()方法获取数组元素的类型,然后递归调用该方法。第二步判断是不是ParameterizedType类型,是的话调用getActualTypeArguments方法获取每个泛型参数的类型,然后递归调用该方法。第三部判断是不是WildcardType通配符类型,是的话调用getLowerBounds和getUpperBounds获取通配符的上限和下限,然后递归调用该方法。
(2).通过反射调用类的静态方法时对象传递null值即可,比如上面代码中调用包装数据类型的Valueof方法时传递的也是null。
五.总结
通过这个案例的学习,我们学习到了反射技术的强大和方便,对我们的案例感兴趣的同学也可以下载代码来查看。在下一篇文章中,我们将总结反射和动态代理以及他们的应用。
最后有需要代码的同学可以点击链接下载。
代码下载