这各项目算是做的不太成功的一个项目,也算是工作也来独立处理事情最多的一个项目,预计是三周完成,最后用了两个多月,逾期的很夸张,其中跳了很多坑,有技术上的坑,也有非技术方面的,耽误了很长时间,但也得到了不少的收获,在这特别记录一下
1.经验教训
1.首先,从此项目中获得的最重要的,也算是遇到的最大的问题,就是思路和对项目理解,做一个项目之前,一定要仔细分析,对项目的需求,设计和思路一定要清晰,功能实现一定不能着急,千万不要着急下手去做,如果搞不清前提的话,肯定是要边做边改,越来越烂,有时候一个功能点做到一半发现需求理解错误,或者是其他问题需要改,就很影响心情,也很影响思路,对整体的代码整洁性和功能之间的交互也会有影响,此次项目的最初设计者是其他同事,而我在接手项目的时候,并没有搞清设计者的思路,就开始着手实现功能,导致项目中遇到了很多逻辑错误,连流程都不通,首先是没有太多经验,做项目之前上头,看了几眼人家的技术文档就开始实现功能,结果发现项目中隐藏了很多文档中没有提到的功能点和功能交互问题,在接手之后并没有积极的与设计者沟通(在后面沟通之后发现文档并不完整,写了一半就交接过来了),导致功能实现了之后根本没法联动起来,有的甚至功能实现都成问题,而且不理解整个思路和设计思想的话,就算不遇到坑,也肯定是磕磕绊绊,只专注于实现单独功能点的话肯定不能顺利完成,此次项目中的查询涉及到一个多表的纵向查询,一次查询五个表的数据,每次发现代码有错误,或者是有逻辑问题的时候,都要动辄修改几百行代码(其实我现在做完了也不太理解这个设计的目的,猜测应该是分了表),每次改的都会很烦躁,心情其实很影响思维,更影响工作效率,代码也更容易写错,跟同事交流也会情绪化,更拖慢了项目进度,一步崩步步崩,诱发性的事件比想象中的更可怕,所以在动手之前一定要整理好需求和设计思路,自己写的东西的目的,实现流程,设计目的,以及会影响到的点,三思而后行,千万别写让自己说不清的东西,到最后劳力更劳心。
2.第二就是沟通问题,前面提到的问题,其实也有很大一部分是没有积极沟通导致的,在接手项目之后没有和设计者沟通,遇到了问题也没有交流,导致到最后,写完了项目还是有部分不理解的设计,在开发过程中,也出现了很多因为沟通导致的错误,还有很多白白浪费的时间,大概有十几次因为没有清晰理解同事的意思,而导致做了两三个小时的无用功,可以说是非常的延误进度,其实这些都是可以避免的,如果发现问题及时沟通并理解的话,完全可以省出这些时间,还有一些因为沟通问题引起的错误,比延误时间还要严重,沟通完了之后会错意,我以为的不是你以为的,结果导致同事之间的工作对接不上,很多工作量都是白白的浪费掉的,甚至还要改回去,可以说是相当的头疼,所以一定要尽量避免。
3.第三就是准备问题,这次的逾期,也与我自身有很大的原因,事先准备不足,第一是对项目的准备不足,由于项目经验不是很丰富,习惯了专注于功能点,在项目开始的时候并没有认真做准备,而是根据交接过来的类图直接开始着手实现功能,致使功能实现完之后并不能准确的符合项目的需求,需要调整,甚至是重写,曾经还有一次因为看错需求而耽误了三天时间,还有许多功能在前期没有发现,等到写完的功能无法串联的时候才发觉到中间有许多被忽略的功能,像这些完全可以避免的问题犯了很多,第二,技术储备也有很大问题,设计中需要用到反射,而我对反射的应用并没有经验,只好在项目实现中慢慢摸索,其中还要感谢受到了许多同事的帮助,否则可能需要的时间要更长,像这些基础性的功能,最好是在平时早做准备,平时多多学习,增加技术储备,平时把太多的时间放在无用的东西上而不提高自己的话,到需要的时候,肯定是要花上更大的代价来弥补,出来混总是要还,所以一定要在东西用到之前准备,避免无谓的时间浪费。
2.技术总结
1.反射
指程序可以访问,检测和修改它本身状态或行为的一种能力,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。 反射是java中一种强大的工具,能够使我们很方便的创建灵活的代码,这些代码可以再运行时装配,无需在组件之间进行源代码链接。但是反射使用不当会成本很高
首先是反射相关的类
java.lang.Class
java.lang.reflect..Constructor;
java.lang.reflect.Field;
java.lang.reflect.Method;
java.lang.reflect.Modifier;
反射机制获取类有三种方法,此处用Want代表我们想要获取的类
第一种
ClassA = Class.forName("Want");
第二种
ClassB =Want.class;
第三种
Want = new Want();
//C为运行时类(e的运行时类是Want)
ClassC =e.getClass();
获取类之后创建他的对象,利用newInstance
Class c=Class.forName("Want");
//创建此Class对象所表示类的一个新实例
//调用了Want的无参构造方法
Objecto = c.newInstance();
获取所有属性
//获取整个类
Class c = Class.forName("java.lang.Integer");
//获取类的所有属性
Field[] = fs =c.getDeclaredFields();
//定义可变长度的字符串,用来存储属性
StringBuffer sb = new StringBuffer();
//通过追加的方法,将每个属性拼接到此字符串中
//最外面的public定义
sb.append(Modifier.toString(c.getModifiers()) + "class" + c.getSimpleName() + "{\n");
//里边的每一个属性
for(Field field:fs){
sb.append("/t");//空格
//获得属性的修饰符,例如public,static等等
sb.append(Modifier.toString(field.getModifiers())+" ");
//属性的类型的名字
sb.append(field.getType().getSimpleName() + " ");
//属性的名字+回车
sb.append(field.getName() + ";\n ");
}
sb.append("}");
System.out.println(sb);
方法关键字
getDeclaredMethods()获取所有的方法
getReturnType()获得方法的返回类型
getParameterTypes()获得方法的传入参数类型
getDeclaredMethod("方法名",参数类型.class,......)获得特定的方法
构造方法关键字
getDeclaredConstructors()获取所有的构造方法
getDeclaredConstructor(参数类型.class,.....)获取特定的构造方法
父类和父接口
getSuperclass()获取某类的父类
getInterfaces()获取某类的实现的接口
这样我们就可以获得类的各种内容,进行了反编译。对于JAVA这种先编译再运行的语言来说,反射机制可以使代码更加灵活,更加容易实现面向对象。
2.单元测试
JUnit4通过注解的方式来识别测试方法。目前支持的主要注解有:
@BeforeClass 全局只会执行一次,而且是第一个运行
@Before 在测试方法运行之前运行
@Test 测试方法
@After 在测试方法运行之后允许
@AfterClass 全局只会执行一次,而且是最后一个运行
@Ignore 忽略此方法
测试异常
JAVA中的异常处理也是一个重点,因此你经常会编写一些需要抛出异常的函数。那么,如果你觉得一个函数应该抛出异常,但是它没抛出,这算不算Bug呢?这当然是Bug,并JUnit也考虑到了这一点,来帮助我们找到这种Bug。例如,我们写的计算器类有除法功能,如果除数是一个0,那么必然要抛出“除0异常”。因此,我们很有必要对这些进行测试。代码如下:
@Test(expected = ArithmeticException.class)publicvoid divideByZero(){
calculator.divide(
参数化测试
JUnit4提出了“参数化测试”的概念,只写一个测试函数,把这若干种情况作为参数传递进去,一次性的完成测试。代码如下:
@RunWith(Parameterized.class)publicclassSquareTest{privatestatic Calculator calculator =new Calculator();privateint param;privateint result;@Parameterspublicstatic Collectiondata() {return Arrays.asList(new Object[][]{
{2,4},
{0,0},
{-3,9},
});
}
//构造函数,对变量进行初始化
public SquareTest(int param, int result){
this.param = param;
this.result = result;
}
@Test
public void square(){
calculator.square(param);
assertEquals(result, calculator.getResult());
}
}
描述相对详细的参数化测试
json格式数据测试
可以将前端传递的json格式数据直接用于测试类
工具类
package com.allqj.platform_common_note.business.jsontest;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.io.FileUtils;
import org.springframework.stereotype.Service;
import java.io.File;
import java.io.IOException;
/**
* @program: platform_common_note
* @description: 读取文件中的json
* @author: Xing
* @create: 2018-09-21 13:57
**/
@Service
public class ReadJsonFromFileUtil {
/**
* 读取文件中的json,返回对象
*
* @param filePath 文件路径+文件名+后缀
* @param t 转换目标类型
* @param 返回值类型
* @return 目标类型的对象
* @throws IOException IO异常
*/
public T readJson(String filePath, Class t) throws IOException {
ClassLoader classLoader = getClass().getClassLoader();
File file = new File(classLoader.getResource(filePath).getFile());
String jsonString = FileUtils.readFileToString(file, "UTF-8");
ObjectMapper objectMapper = new ObjectMapper();
T object = objectMapper.readValue(jsonString, t);
return object;
}
}
应用类
@Autowired
//注入工具类
private ReadJsonFromFileUtil readJsonFromFileUtil;
@Test
public void add() throws IOException {
//json实体
NoteFollowModel model = (NoteFollowModel) readJsonFromFileUtil.readJson("包名/json文件名.json",
NoteFollowModel.class);
assert noteFollowService.add(model, "operatorid");
}
JUnit框架JUnit对于特定的需求还提供了框架扩展,不过我并没有深入调研,这里提供一个JUnit框架的链接
在我们测试过程中,为了避免不需要的数据被添加到数据库,可以用注解调整方法的顺序,先添加,在修改,或者其他操作,最后删除,所有测试方法运行后可以保持数据库的数据不被破坏,还可以用rollback注解,在断言确定测试成功后事务回滚,不操作数据库。