導讀
因爲公司使用的是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;
}
上面使用到了文件工廠設計模式,內部使用深度遞歸算法。如果初始目錄下,仍舊有子目錄,調用自身的方法,直到遇見文件,如代碼所示:
/**
- 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加載上下文文件
首先需要一個構造器,形參時文件的名字;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的美化,核心值如何實現自動裝配。