項目源碼地址git
由於公司使用的是spring框架,spring是什麼?它就像一應俱全的容器,咱們什麼均可以往裏面填,好比集合持久層的hibernate或mybatis框架,相似於攔截器的的shiro框架等等。算法
它的好處是能夠自動建立對象。之前,在沒有使用spring框架時,咱們必須本身建立對象。但自從有了spring框架後,Java開發就像迎來了春天,一切都變的那麼簡單。spring
它有幾種自動建立對象的方式,好比構造器建立對象,set建立對象。。。若是想要對其有更多的瞭解,那麼,如今有不少博客,都對其作了詳細的介紹。我在這裏沒必要再作詳解了。設計模式
本項目使用了logback和slf4j記錄日誌信息,由於它們兩個是常常合做的。同時,也使用了lombok框架,這個框架能夠自動生成set、get、toString、equals、hashcode方法等。mybatis
下面,便詳細介紹個人這個項目。框架
本項目採用工廠和建造者設計模式。dom
在沒有使用註解的前提下,咱們把全部的將要建立對象的信息寫進配置文件中,這就是咱們常說的依賴注入。而當代碼加載時,須要加載這些配置文件。函數
這裏須要兩個類來支撐。一個是XmlConfigBean,記錄每一個xml文件中的bean信息。XmlBeanProperty這裏記錄每一個bean中的屬性信息。ui
加載文件方法中調用了這兩個類,固然,我是用了org下的jdom來讀取xml文件,正如如下代碼所示。this
/** * 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; }
上面使用到了文件工廠設計模式,內部使用深度遞歸算法。若是初始目錄下,仍舊有子目錄,調用自身的方法,直到碰見文件,如代碼所示:
/** * Created By zby on 14:04 2019/2/14 * 獲取文件的集合 */ 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的美化,核心值如何實現自動裝配。