项目源码地址
因为公司使用的是spring框架,spring是什么?它就像包罗万象的容器,我们什么都可以往里面填,比如集合持久层的hibernate或mybatis框架,类似于拦截器的的shiro框架等等。
它的好处是可以自动创建对象。以前,在没有使用spring框架时,我们必须自己创建对象。但自从有了spring框架后,Java开发就像迎来了春天,一切都变的那么简单。
它有几种自动创建对象的方式,比如构造器创建对象,set创建对象。。。如果想要对其有更多的了解,那么,下载有很多博客,都对其做了详细的介绍。我在这里不必再做详解了。
项目使用了logback和slf4j记录日志信息,因为它们两个是经常合作的。同时,也使用了lombok框架,这个框架可以自动生成set、get、toString、equals、hashcode方法等。
下面,便详细介绍我的这个项目。
本项目采用工厂和建造者设计模式。
在没有使用注解的前提下,我们把所有的将要创建对象的信息写进配置文件中,这就是我们常说的依赖注入。而当代码加载时,需要加载这些配置文。
这里需要两个雷来支撑。一个是XmlConfigBean,记录每个xml文件中的bean信息。XmlBeanProperty这里记录每个bean中的属性信息。
加载文件方法中调用了这两个类,当然,我是用了org下的jdom来读取xml文件,正如以下代码所示。
/** * Created By zby on 22:57 2019/3/4 * 加载配置文件 * * @param dirPath 目录的路径 */ public static LoadConfig loadXmlConFig(String dirPath) { if (StringUtils.isEmpty(dirPath)){ throw new RuntimeException("路径不存在"); } if (null == config) { File dir = new File(dirPath); List<File> files = FactoryBuilder.createFileFactory().listFile(dir); if (CollectionUtil.isEmpty(files)) { throw new RuntimeException("没有配置文件files=" + files); } allXmls = new HashMap<>(); SAXBuilder saxBuilder = new SAXBuilder(); Document document = null; for (File file : files) { try { Map<String, XmlConfigBean> beanMaps = new HashMap<>(); //创建配置文件 String configFileName = file.getName(); document = saxBuilder.build(file); Element rootEle = document.getRootElement(); List beans = rootEle.getChildren("bean"); if (CollectionUtil.isNotEmpty(beans)) { int i = 0; for (Iterator beanIterator = beans.iterator(); beanIterator.hasNext(); i++) { Element bean = (Element) beanIterator.next(); XmlConfigBean configBean = new XmlConfigBean(); configBean.setId(attributeToConfigBeanProps(file, i, bean, "id")); configBean.setClazz(attributeToConfigBeanProps(file, i, bean, "class")); configBean.setAutowire(attributeToConfigBeanProps(file, i, bean, "autowire")); configBean.setConfigFileName(configFileName); List properties = bean.getChildren(); Set<XmlBeanProperty> beanProperties = new LinkedHashSet<>(); if (CollectionUtil.isNotEmpty(properties)) { int j = 0; for (Iterator propertyIterator = properties.iterator(); propertyIterator.hasNext(); j++) { Element property = (Element) propertyIterator.next(); XmlBeanProperty beanProperty = new XmlBeanProperty(); beanProperty.setName(attributeToBeanProperty(file, i, j, property, "name")); beanProperty.setRef(attributeToBeanProperty(file, i, j, property, "ref")); beanProperty.setValue(attributeToBeanProperty(file, i, j, property, "value")); beanProperties.add(beanProperty); } configBean.setProperties(beanProperties); } beanMaps.put(configBean.getId(), configBean); } } allXmls.put(configFileName, beanMaps); } catch (JDOMException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } return new LoadConfig(); } return config; } 上面使用到了文件工厂设计模式,内部使用深度递归算法。如果初始目录下,仍旧有子目录,调用自身的方法,直到遇见文件,如代码所示:
/**
*/
private void local(File dir) {
if (dir == null) { logger.error("文件夹为空dir=" + dir); throw new RuntimeException("文件夹为空dir=" + dir); } File[] fies = dir.listFiles(); if (ArrayUtil.isNotEmpty(fies)) { for (File fy : fies) { if (fy.isDirectory()) { local(fy); } String fileName = fy.getName(); boolean isMatch = Pattern.compile(reg).matcher(fileName).matches(); boolean isContains = ArrayUtil.containsAny(fileName, FilterConstants.FILE_NAMES); if (isMatch && !isContains) { fileList.add(fy); } } }
}
这里用来修饰类信息的。比如,将类名的首字母转化为小写;通过类路径转化为类字面常量,如代码所示:
/** * Created By zby on 20:19 2019/2/16 * 通过类路径转为类字面常量 * * @param classPath 类路径 */ public static <T> Class<T> classPathToClazz(String classPath) { if (StringUtils.isBlank(classPath)) { throw new RuntimeException("类路径不存在"); } try { return (Class<T>) Class.forName(classPath); } catch (ClassNotFoundException e) { logger.error("路径" + classPath + "不存在,创建失败e=" + e); e.printStackTrace(); } return null; }
如果不是用户自定义的类型,我们需要使用类型转化器,将配置文件的数据转化为我们Javabean属性的值。因为,从配置文件读取过来的值,都是字符串类型的,加入Javabean的id为long型,因而,我们需要这个类型转换。
/** * Created By zby on 22:31 2019/2/25 * 将bean文件中的value值转化为属性值 */ public final class Transfomer { public final static Integer MAX_BYTE = 127; public final static Integer MIN_BYTE = -128; public final static Integer MAX_SHORT = 32767; public final static Integer MIN_SHORT = -32768; public final static String STR_TRUE = "true"; public final static String STR_FALSE = "false"; /** * Created By zby on 22:32 2019/2/25 * 数据转化 * * @param typeName 属性类型的名字 * @param value 值 */ public static Object transformerPropertyValue(String typeName, Object value) throws IllegalAccessException { if (StringUtils.isBlank(typeName)) { throw new RuntimeException("属性的类型不能为空typeName+" + typeName); } if (typeName.equals(StandardBasicTypes.STRING)) { return objToString(value); } else if (typeName.equalsIgnoreCase(StandardBasicTypes.LONG)) { return stringToLong(objToString(value)); } else if (typeName.equals(StandardBasicTypes.INTEGER) || typeName.equals(StandardBasicTypes.INT)) { return stringToInt(objToString(value)); } else if (typeName.equalsIgnoreCase(StandardBasicTypes.BYTE)) { return stringToByte(objToString(value)); } else if (typeName.equalsIgnoreCase(StandardBasicTypes.SHORT)) { return stringToShort(objToString(value)); } else if (typeName.equalsIgnoreCase(StandardBasicTypes.BOOLEAN)) { return stringToBoolean(objToString(value)); } else if (typeName.equalsIgnoreCase(StandardBasicTypes.DOUBLE)) { return stringToDouble(objToString(value)); } else if (typeName.equalsIgnoreCase(StandardBasicTypes.FLOAT)) { return stringToFloat(objToString(value)); } else if (typeName.equals(StandardBasicTypes.DATE)) { return stringToDate(objToString(value)); } else if (typeName.equals(StandardBasicTypes.BIG_DECIMAL)) { return stringToBigDecimal(objToString(value)); } else { return value; } } /** * Created By zby on 22:32 2019/2/25 * 数据转化 */ public static void transformerPropertyValue(Object currentObj, Field field, Object value) throws IllegalAccessException { if (null == currentObj && field == null) { throw new RuntimeException("当前对象或属性为空值"); } String typeName = field.getType().getSimpleName(); field.setAccessible(true); field.set(currentObj, transformerPropertyValue(typeName, value)); } /** * Created By zby on 23:29 2019/2/25 * obj to String */ public static String objToString(Object obj) { return null == obj ? null : obj.toString(); } /** * Created By zby on 23:54 2019/2/25 * String to integer */ public static Integer stringToInt(String val) { if (StringUtils.isBlank(val)) { return 0; } if (val.charAt(0) == 0) { throw new RuntimeException("字符串转为整形失败val=" + val); } return Integer.valueOf(val); } /** * Created By zby on 23:31 2019/2/25 * String to Long */ public static Long stringToLong(String val) { return Long.valueOf(stringToInt(val)); } /** * Created By zby on 23:52 2019/2/26 * String to byte */ public static Short stringToShort(String val) { Integer result = stringToInt(val); if (result >= MIN_SHORT && result <= MAX_SHORT) { return Short.valueOf(result.toString()); } throw new RuntimeException("数据转化失败result=" + result); } /** * Created By zby on 0:03 2019/2/27 * String to short */ public static Byte stringToByte(String val) { Integer result = stringToInt(val); if (result >= MIN_BYTE && result <= MAX_BYTE) { return Byte.valueOf(result.toString()); } throw new RuntimeException("数据转化失败result=" + result); } /** * Created By zby on 0:20 2019/2/27 * string to double */ public static Double stringToDouble(String val) { if (StringUtils.isBlank(val)) { throw new RuntimeException("数据为空,转换失败"); } return Double.valueOf(val); } /** * Created By zby on 0:23 2019/2/27 * string to float */ public static Float stringToFloat(String val) { if (StringUtils.isBlank(val)) { throw new RuntimeException("数据为空,转换失败"); } return Float.valueOf(val); } /** * Created By zby on 0:19 2019/2/27 * string to boolean */ public static boolean stringToBoolean(String val) { if (StringUtils.isBlank(val)) { throw new RuntimeException("数据为空,转换失败val=" + val); } if (val.equals(STR_TRUE)) { return true; } if (val.equals(STR_FALSE)) { return false; } byte result = stringToByte(val); if (0 == result) { return false; } if (1 == result) { return true; } throw new RuntimeException("数据转换失败val=" + val); } /** * Created By zby on 0:24 2019/2/27 * string to Date */ public static Date stringToDate(String val) { if (StringUtils.isBlank(val)) { throw new RuntimeException("数据为空,转换失败val=" + val); } SimpleDateFormat format = new SimpleDateFormat(); try { return format.parse(val); } catch (ParseException e) { throw new RuntimeException("字符串转为时间失败val=" + val); } } /** * Created By zby on 0:31 2019/2/27 * string to big decimal */ public static BigDecimal stringToBigDecimal(String val) { if (StringUtils.isBlank(val)) { throw new RuntimeException("数据为空,转换失败val=" + val); } return new BigDecimal(stringToDouble(val)); } }
/** * Created By zby on 13:50 2019/2/23 * 装配类型 */ public class AutowireType { /** * 缺省情况向,一般通过ref来自动(手动)装配对象 */ public static final String NONE = null; /** * 根据属性名事项自动装配, * 如果一个bean的名称和其他bean属性的名称是一样的,将会自装配它。 */ public static final String BY_NAME = "byName"; /** * 根据类型来装配 * 如果一个bean的数据类型是用其它bean属性的数据类型,兼容并自动装配它。 */ public static final String BY_TYPE = "byType"; /** * 根据构造器constructor创建对象 */ public static final String CONSTRUCTOR = "constructor"; /** * autodetect – 如果找到默认的构造函数,使用“自动装配用构造”; 否则,使用“按类型自动装配”。 */ public static final String AUTODETECT = "autodetect"; }
/** * Created By zby on 22:44 2019/2/25 * 类型常量池 */ public class StandardBasicTypes { public static final String STRING = "String"; public static final String LONG = "Long"; public static final String INTEGER = "Integer"; public static final String INT = "int"; public static final String BYTE = "Byte"; public static final String SHORT = "Short"; public static final String BOOLEAN = "Boolean"; public static final String DOUBLE = "double"; public static final String FLOAT = "float"; public static final String DATE = "Date"; public static final String TIMESTAMP = "Timestamp"; public static final String BIG_DECIMAL = "BigDecimal"; public static final String BIG_INTEGER = "BigInteger"; }
首先需要一个构造器,形参时文件的名字;getBean方法,形参是某个bean的id名字,这样,根据当前bean的自动装配类型,来调用响应的方法。
/** * Created By zby on 11:17 2019/2/14 * 类的上下文加载顺序 */ public class ClassPathXmlApplicationContext { private static Logger logger = LoggerFactory.getLogger(ClassPathXmlApplicationContext.class.getName()); private String configXml; public ClassPathXmlApplicationContext(String configXml) { this.configXml = configXml; } /** * Created By zby on 18:38 2019/2/24 * bean对应的id的名称 */ public Object getBean(String name) { String dirPath="../simulaspring/src/main/resources/"; Map<String, Map<String, XmlConfigBean>> allXmls = LoadConfig.loadXmlConFig(dirPath).getAllXmls(); boolean contaninsKey = MapUtil.findKey(allXmls, configXml); if (!contaninsKey) { throw new RuntimeException("配置文件不存在" + configXml); } Map<String, XmlConfigBean> beans = allXmls.get(configXml); contaninsKey = MapUtil.findKey(beans, name); if (!contaninsKey) { throw new RuntimeException("id为" + name + "bean不存在"); } XmlConfigBean configFile = beans.get(name); if (null == configFile) { throw new RuntimeException("id为" + name + "bean不存在"); } String classPath = configFile.getClazz(); if (StringUtils.isBlank(classPath)) { throw new RuntimeException("id为" + name + "类型不存在"); } String autowire = configFile.getAutowire(); if (StringUtils.isBlank(autowire)) { return getBeanWithoutArgs(beans, classPath, configFile); } else { switch (autowire) { case AutowireType.BY_NAME: return getBeanByName(beans, classPath, configFile); case AutowireType.CONSTRUCTOR: return getBeanByConstruct(classPath, configFile); case AutowireType.AUTODETECT: return getByAutodetect(beans, classPath, configFile); case AutowireType.BY_TYPE: return getByType(beans, classPath, configFile); } } return null; } } 下面主要讲解默认自动装配、属性自动装配、构造器自动装配
如果我们没有填写自动装配的类型,其就采用ref来自动(手动)装配对象。
/** * Created By zby on 18:33 2019/2/24 * 在没有设置自动装配时,通过ref对象 */ private Object getBeanWithoutArgs(Map<String, XmlConfigBean> beans, String classPath, XmlConfigBean configFile) { //属性名称 String proName = null; try { Class currentClass = Class.forName(classPath); //通过引用 ref 创建对象 Set<XmlBeanProperty> properties = configFile.getProperties(); //如果没有属性,就返回,便于下面的递归操作 if (CollectionUtil.isEmpty(properties)) { return currentClass.newInstance(); } Class<?> superClass = currentClass.getSuperclass(); //TODO 父类的集合 // List<Class> superClasses = null; //在创建子类构造器之前,创建父类构造器, // 父类构造器的参数子类构造器的参数 Object currentObj = null; //当前构造器 Object consArgsObj = null; String consArgsName = null; boolean hasSuperClass = (null != superClass && !superClass.getSimpleName().equals("Object")); if (hasSuperClass) { Constructor[] constructors = currentClass.getDeclaredConstructors(); ArrayUtil.validateArray(superClass, constructors); Parameter[] parameters = constructors[0].getParameters(); if (parameters == null || parameters.length == 0) { consArgsObj = constructors[0].newInstance(); } else { ArrayUtil.validateArray(superClass, parameters); consArgsName = parameters[0].getType().getSimpleName(); //配置文件大类型,与参数构造器的类型是否相同 for (XmlBeanProperty property : properties) { String ref = property.getRef(); if (StringUtils.isNotBlank(ref) && ref.equalsIgnoreCase(consArgsName)) { classPath = beans.get(ref).getClazz(); Class<?> clazz = Class.forName(classPath); consArgsObj = clazz.newInstance(); } } currentObj = constructors[0].newInstance(consArgsObj); } } else { currentObj = currentClass.newInstance(); } for (XmlBeanProperty property : properties) { //这里适合用递归,无限调用自身 //通过name找到属性,配置文件中是否有该属性,通过ref找到其对应的bean文件 proName = property.getName(); Field field = currentClass.getDeclaredField(proName); if (null != field) { String ref = property.getRef(); Object value = property.getValue(); //如果没有赋初值,就通过类型创建 if (null == value && StringUtils.isNotBlank(ref)) { boolean flag = StringUtils.isNotBlank(consArgsName) && null != consArgsObj && consArgsName.equalsIgnoreCase(ref); //递归调用获取属性对象 value = flag ? consArgsObj : getBean(ref); } field.setAccessible(true); Transfomer.transformerPropertyValue(currentObj, field, value); } } return currentObj; } catch (ClassNotFoundException e) { logger.error("名为" + classPath + "类不存在"); e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (NoSuchFieldException e) { logger.error(classPath + "类的属性" + proName + "不存在"); throw new RuntimeException(classPath + "类的属性" + proName + "不存在"); } return null; }
根据构造器constructor创建对象
/** * Created By zby on 23:06 2019/3/2 * * @param classPath 类路径 * @param configFile 配置文件 */ private Object getBeanByConstruct(String classPath, XmlConfigBean configFile) { try { Class currentClass = Class.forName(classPath); Set<XmlBeanProperty> properties = configFile.getProperties(); if (CollectionUtil.isEmpty(properties)) { return currentClass.newInstance(); } ///构造器参数类型和构造器对象集合 Object[] objects = new Object[properties.size()]; Class<?>[] paramType = new Class[properties.size()]; Field[] fields = currentClass.getDeclaredFields(); int i = 0; for (Iterator iterator = properties.iterator(); iterator.hasNext(); i++) { XmlBeanProperty property = (XmlBeanProperty) iterator.next(); String proName = property.getName(); String ref = property.getRef(); Object value = property.getValue(); for (Field field : fields) { Class<?> type = field.getType(); String typeName = type.getSimpleName(); String paramName = field.getName(); if (paramName.equals(proName) && ObjectUtil.isNotNull(value) && StringUtils.isBlank(ref)) { objects[i] = Transfomer.transformerPropertyValue(typeName, value); paramType[i] = type; break; } else if (paramName.equals(proName) && StringUtils.isNotBlank(ref) && ObjectUtil.isNull(value)) { objects[i] = getBean(ref); paramType[i] = type; break; } } } return currentClass.getConstructor(paramType).newInstance(objects); } catch (ClassNotFoundException e) { logger.error("名为" + classPath + "类不存在"); e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } return null; }
根据属性名事项自动装配,如果一个bean的名称和其他bean属性的名称是一样的,将会自装配它。
/** * Created By zby on 21:16 2019/3/1 * 根据属性名事项自动装配, * @param classPath 类路径 * @param configFile 配置文件 */ private Object getBeanByName( String classPath, XmlConfigBean configFile) { String proName = null; try { Class currentClass = Class.forName(classPath); Class superclass = currentClass.getSuperclass(); Method[] methods = currentClass.getDeclaredMethods(); List<Method> methodList = MethodHelper.filterSetMethods(methods); Object currentObj = currentClass.newInstance(); Set<XmlBeanProperty> properties = configFile.getProperties(); //配置文件中,但是有父类, if (CollectionUtil.isEmpty(properties)) { boolean isExit = null != superclass && !superclass.getSimpleName().equals("Object"); if (isExit) { Field[] parentFields = superclass.getDeclaredFields(); if (ArrayUtil.isNotEmpty(parentFields)) { if (CollectionUtil.isNotEmpty(methodList)) { for (Field parentField : parentFields) { for (Method method : methodList) { if (MethodHelper.methodNameToProName(method.getName()).equals(parentField.getName())) { //如果有泛型的话 Type genericType = currentClass.getGenericSuperclass(); if (null != genericType) { String genericName = genericType.getTypeName(); genericName = StringUtils.substring(genericName, genericName.indexOf("<") + 1, genericName.indexOf(">")); Class genericClass = Class.forName(genericName); method.setAccessible(true); method.invoke(currentObj, genericClass); } break; } } break; } } } } return currentObj; } //传递给父级对象 service -- 》value List<Method> tmpList = new ArrayList<>(); Map<String, Object> map = new HashMap<>(); Object value = null; for (XmlBeanProperty property : properties) { proName = property.getName(); if (ArrayUtil.isNotEmpty(methods)) { String ref = property.getRef(); value = property.getValue(); for (Method method : methodList) { String methodName = MethodHelper.methodNameToProName(method.getName()); Field field = currentClass.getDeclaredField(methodName); if (methodName.equals(proName) && null != field) { if (null == value && StringUtils.isNotBlank(ref)) { value = getBean(ref); } else if (value != null && StringUtils.isBlank(ref)) { value = Transfomer.transformerPropertyValue(field.getType().getSimpleName(), value); } method.setAccessible(true); method.invoke(currentObj, value); map.put(proName, value); tmpList.add(method); break; } } } } tmpList = MethodHelper.removeMethod(methodList, tmpList); for (Method method : tmpList) { Class<?>[] type = method.getParameterTypes(); if (ArrayUtil.isEmpty(type)) { throw new RuntimeException("传递给父级对象的参数为空type=" + type); } for (Class<?> aClass : type) { String superName = ClassHelper.classNameToProName(aClass.getSimpleName()); value = map.get(superName); method.setAccessible(true); method.invoke(currentObj, value); } } return currentObj; } catch (ClassNotFoundException e) { logger.error("名为" + classPath + "类不存在"); e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (NoSuchFieldException e) { logger.error("类" + classPath + "属性" + proName + "不存在"); e.printStackTrace(); } return null; }
这里没有使用注解,我们可以使用注解的方式实现自动装配,但这不spring的核心,应该时spring的美化,核心值如何实现自动装配。