# 后端
## Java泛型和变长参数
最近在写毕设使用到`Arrays.asList(T... a)`的情况,发现不能用在Long[]上,却可以用在String[]上。经过查询这里涉及到两方面的内容:
1. 泛型。只有非基本类型才能被泛化,故此接口不能接受基本类型。
2. 变长参数。变长参数可以传入一个数组,并且其实现原理、使用方法上和数组类似。
具体内容可见:
> [java数组转list](https://www.cnblogs.com/zheyangsan/p/6910476.html)
> [Java数组传递及可变参数](https://blog.csdn.net/lxxiang1/article/details/82312799)
## Java泛型中占位符?和T的区别
简而言之,?可以指多种类,不对类进行单一的限制
而T限制了只能定义泛型为一种。
具体见:
> [泛型<?>和<T>的区别](https://www.jianshu.com/p/ec59ae2ed473)
## 构建ResponseEntity应该放在Controller层面
自从从`一个Service注入多个Repo`变更至`一个Service对应一个Repo`后,Service之间会产生注入关系。此时如果再讲ResponseEntity构建放在Service层面,可能会导致Service层的方法输出类型混乱:ResponseEntity及其他类型混杂。
同时,如果使用ResponseEntity作为Serivce方法的返回值,在Serivce之间调用会存在不方便。
故我认为Service应该统一返回DTO或PO,剩下的事情交由Controller或者调用方Service/Compnent进行处理。
2020年10月20日补充:
上完TWUC之后的GTB和晓峰哥具体交流之后,发现了一种新的对PO/BO/DTO的定义。从而能够很好地在逻辑上解释我上面采取的这些操作。
在对Bean的定义上我们还可以再更具体一些:
+ PO:持久层的操作实体,是ORM框架直接操作的对象
+ BO:业务逻辑实体。
+ DTO:数据传输实体,主要是用于与其他客户端(浏览器、应用客户端等)传输数据。
这里的BO需要单独提出来说明一下。在我之前的开发的项目中,并没有很经常地用到BO这个概念。`是因为我在之前认为,BO的所代表的职责在一定程度上与DTO重叠。`现在我也不认为我这个想法是错误的,因为在我处理的这些简单的、面向单一客户端的项目上单独使用DTO是没有问题的。
但是在大型项目上,很多时候其实面对的不是一个客户端,而是有可能面对浏览器、app、移动端应用等多方面的客户端,他们的接口或返回值都是不一样的,这就导致了DTO非常可能是不一样的。这样如果使用上面的举措,DTO就没有办法很好地满足我们的需求了。`此时引入BO,BO专注于逻辑处理流程,并按需在BO和DTO之间进行转换。`Java下也提供了比较方便的工具如`Dozer`进行BO和DTO之间的转换。
## 在枚举类中进行依赖注入比较困难
自从看了 [答应我,别再if/else走天下了可以吗](https://mp.weixin.qq.com/s/ufRf8DQQRYQI0q2VxG3hQg) 后就对其中的枚举类解耦情有独钟,结果发现用在实际业务中是有所限制的。
固然上面文章中的枚举类的解耦方案代码高度聚合,可读性非常强,但是由于枚举类的特殊限制(静态构造器?没有去深究,迟点补课),Spring很难向其中注入属性。所以枚举解耦的方案适合在不需要依赖注入的时候进行解耦。
推荐是使用工厂模式或者其他方法进行解决。
## 注意处理嵌套事务回滚
最近在做毕设的时候遇到了嵌套事务回滚异常的情况。
解释在这里:[UnexpectedRollbackException解决方案](https://segmentfault.com/a/1190000016418596)
简单地说就是内层的方法也开启了事务管理,同时采用的默认事务传播方式(加入当前已存在的事务),内层抛出异常时由Spring的全局事务管理设置了事务回滚,并且将异常向上层传递。
关键在于,由于外层也没有try-catch语句捕获异常,继续将异常向上层传递,此时Spring的全局事务管理再次捕获到了这个异常并尝试再一次设置事务回滚,从而抛出异常。
```java
...
authDetailRepo.save(authDetailPO);
// 此处调用会抛出异常
long defaultValueId = authValueService.createValues(authDetailPO.getId(), createAuthDTO.getValues(), authType);
authDetailPO.setDefaultValueId(defaultValueId);
authDetailRepo.save(authDetailPO);
...
```
我个人认为比较好的解决方法是在外层写一个try-catch语句,不要将内层的异常直接抛出去,在catch中设置回滚后再抛,即可消除这个问题。
```java
...
long defaultValueId;
try {
defaultValueId = authValueService.createValues(authDetailPO.getId(), createAuthDTO.getValues(), authType);
} catch (Exception e) {
// 手动设置回滚事务,报错消除
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
throw e;
}
authDetailPO.setDefaultValueId(defaultValueId);
authDetailRepo.save(authDetailPO);
...
```
# 前端
## Vue.js + TypeScript使用
最近做毕设用到新的`TypeScript`。
网上查阅资料找到一个很有用的文章,无论是从技术方面还是从架构方面都很值得学习。
> [Vue + TypeScript + Element 项目实践(简洁时尚博客网站)及踩坑记](https://segmentfault.com/a/1190000018720570)
## Vue.js + TypeScript 自定义Prototype所要做的额外工作
在Vue.js里,在main.js中定义Vue.prototype.*可以让全局Vue实例使用自定义变量。
但在结合了ts之后,ts无法解析自定义prototype。
此时需要用到ts的`模块补充 (module augmentation)`。
参考文档:
> [增强类型以配合插件使用](https://cn.vuejs.org/v2/guide/typescript.html#%E5%A2%9E%E5%BC%BA%E7%B1%BB%E5%9E%8B%E4%BB%A5%E9%85%8D%E5%90%88%E6%8F%92%E4%BB%B6%E4%BD%BF%E7%94%A8)
其中my-property.d.ts貌似可以放在任意位置。
2020年4月28日补充:发现在上面的项目实践最后的踩坑记录中有提到。
## TypeScript用法学习
基础用法可参考菜鸟教程:
> [TypeScript 入门教程](https://www.runoob.com/typescript/ts-tutorial.html)
其中,`default`在`export`中的用法指的是默认导出模块的对象,在其他模块中`import`时直接引入该对象。
如果不使用`default`,在其他模块中`import`时,必须要指定引入对象。
## JavaScript中await/async
### 个人理解
今晚看了一些文章,从await/async一直到Promise,并且和吴大佬进行了一下交流,总结了以下几点主要内容:
1. Promise是一个优化异步计算、解决回调地狱的链式调用组件,可携带计算后的结果。从携带结果的角度上来看,有点类似于Java的FutureTask
2. await/async是一种优化Promise写法的语法,将Promise写法进一步简化为类同步写法。
而吴大佬补充道:
> Promise并不是用于异步计算的,而是用于异步事件的。毕竟在常规的js概念上,js是一个基于消息队列的单线程模型,promise所做的就是把事件回调挂到队列里。
### 参考文章
Promise:
> [Javascript 中的神器——Promise](https://www.jianshu.com/p/063f7e490e9a)
> [promise](https://www.jianshu.com/p/1b63a13c2701)
await/async:
> [理解 JavaScript 的 async/await](https://segmentfault.com/a/1190000007535316)
### Promise
Promise是什么?
> 以下摘自:[Javascript 中的神器——Promise](https://www.jianshu.com/p/063f7e490e9a)
所谓 Promise,就是一个对象,用来传递异步操作的消息。它代表了某个未来才会知道结果的事件(通常是一个异步操作),并且这个事件提供统一的 API,可供进一步处理。
Promise有以下两个特点:
1. 对象的状态不受外界影响。Promise 对象代表一个异步操作,有三种状态:Pending(进行中)、Resolved(已完成,又称 Fulfilled)和 Rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是 Promise 这个名字的由来,它的英语意思就是「承诺」,表示其他手段无法改变。
2. 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise 对象的状态改变,只有两种可能:从 Pending 变为 Resolved 和从 Pending 变为 Rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。就算改变已经发生了,你再对 Promise 对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
Promise 也有一些缺点。首先,无法取消 Promise,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。第三,当处于 Pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
---
Promise的一般写法如下:
```javascript
var promise = new Promise(function(resolve, reject) {
if (/* 异步操作成功 */){
resolve(value); // 改变Promise状态为成功resolve/fulFilled
} else {
reject(error); // 改变Promise状态为失败reject
}
});
promise.then(function(value) { // then回调处理事件
// success
}, function(value) {
// failure
});
```
其中,在then中可以嵌套返回新的Promise。具体用法在上面的参考文章`promise`中有提到。
### await/async
大部分概念已在上面的参考文章中解释的非常详尽了。
这里单独拿出一点来说,就是上面参考文章中提到的await/async的优势:[async/await 的优势在于处理 then 链](https://segmentfault.com/a/1190000007535316#item-2-5)
这使得处理多条Promise调用看起来像同步方法一样,并且传递变量也变得比较简单。
## JavaScript中的this
参考:
> [普通函数跟箭头函数中this的指向问题](cnblogs.com/qdlhj/p/9877881.html)
其中开头总结得很好:
**箭头函数和普通函数的区别如下:**
+ 普通函数:根据调用我的人(谁调用我,我的this就指向谁)
+ 箭头函数:根据所在的环境(我在哪个环境中,this就指向谁,最外层非箭头函数的this)
其中由于箭头函数类似于匿名函数,一般作为参数传入到其他方法中。同时**箭头函数没有自己的this,它的this是继承而来的**,默认指向在定义它时所处的对象(宿主对象),而不是执行时的对象
## JavaScript中for...of和for...in的区别
参考:
> [for of 和 for in 的区别](https://blog.csdn.net/shangyanaf/article/details/80415236)
个人总结如下:
+ for...in:用于遍历对象的所有可枚举属性。可枚举属性指的是包括有效载荷在内的被遍历对象的所有内部存储的属性。其中可能还包括从原型中继承的属性、用于管理的属性等。**for...in输出的顺序是不固定的**
+ for...of:在可迭代对象中创建一个迭代循环(类似于Java的迭代器),迭代有效载荷。
## vue-axios中delete的区别
在实现一个功能需要用到delete方法。其中,请求的Body如下:
```javascript
let requestData{
data: []
}
```
然而当我与POST一样,将请求的Body按照如下方法传入时,发现构造出来的Body和POST的不一致。
```javascript
this.$https.delete(url, requestData)
```
构造出来的body如下
```javascript
["6", "14"]
```
随即想到应该是delete的方法参数与POST不一样,上网一查果然如此。
具体参考:
[vue-axios中delete的使用](https://www.jianshu.com/p/31e9cd9f4842)
# 其他
## git commit的良好习惯
最近不知道在哪个论坛上看到的一篇文章,是在讨论大家开发中常用的git命令,其中有一层在讨论说是不是大家平时在git commit 之前都会 git add -A。
有一名网友回复到会先进行git diff查看提交的内容,**保证只提交一个逻辑**。
我觉得这是一个很好的习惯,并且以后打算遵循这个原则。
## json设计的一些思考
开发毕设过程中,有如下的json设计:
```javascript
{
"name": "权限1",
"codeName": "AUTH_1",
"type": "string",
"comment": "权限测试1",
"appId": "",
"values": [{
"valueName": "取值1",
"valueCodeName": "VALUE_1",
"isDefault": true,
"comment": "取值1"
}]
}
```
现在在写前端,发现其实values内的设计并不是很合理。前缀重复。这里认为更加好的设计应该是这样的:
```javascript
{
"name": "权限1",
"codeName": "AUTH_1",
"type": "string",
"comment": "权限测试1",
"appId": "",
"values": [{
"name": "取值1",
"codeName": "VALUE_1",
"isDefault": true,
"comment": "取值1"
}]
}
```
【笔记】2020年开发笔记