共计 17406 个字符,预计需要花费 44 分钟才能阅读完成。
导读
项目源码地址
因为公司使用的是 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 的美化,核心值如何实现自动装配。