springbootPro
一、aop中执行顺序
@Pointcut定义切点
③
@Before → @Method
① ↑② ↙④
AOP -------→ @Around
↓⑤
@After
↓⑥
@AfterReturn
反射机制允许程序在运行时取得任何一个已知名称的class的内部信息,包括包括其modifiers(修饰符),fields(属性),methods(方法)等
并可于运行时改变fields内容或调用methods
我们可以更灵活的编写代码,代码可以在运行时装配,无需在组件之间进行源代码链接,降低代码的耦合度;还有动态代理的实现等等
但是需要注意的是反射使用不当会造成很高的资源消耗
getName():获得类的完整名字
getFields():获得类的public类型的属性。
getDeclaredFields():获得类的所有属性。包括private 声明的和继承类
getMethods():获得类的public类型的方法。
getDeclaredMethods():获得类的所有方法。包括private 声明的和继承类
getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字,parameterTypes 参数指定方法的参数类型。
getConstructors():获得类的public类型的构造方法。
getConstructor(Class[] parameterTypes):获得类的特定构造方法,parameterTypes 参数指定构造方法的参数类型
newInstance():通过类的不带参数的构造方法创建这个类的一个对象
二、swagger的使用
首先引入两个依赖 springfox-swagger2 和 springfox-swagger-ui
@Api 作用在类上,说明该类的作用 ,其中tags和description会显示在swagger的管理页面上
@ApiOperation:注解来给API增加方法说明, 其中notes和value也会显示在swagger的管理页面上
@OnlineApi这个注解是写的自定义的注解(新模块是开发人员写的,用于只有添加了ApiOperation注解的method才在API中显示)
#需要引入SwaggerConfig类
三、ResultDTO的使用
依赖ResultUtils类,依赖CodeMsg类
返回ResultUtils.success()或者是ResultUtils.fail
三、redis的使用
redis数据库在本地虚拟机上启动
redis安装在 /usr/local/redis文件下 到bin目录下 启动 ./redis-server &
更改redis密码,使用命令 ./redis-cli (首先redis-server先要启动)
config set requirepass 1234
使用redisdeskManager客户端连接redis数据库 命令keys XX 查找key为XX的数据缓存
redis的使用配置 RedisConfig类, 项目一启动,就加载该类,包含一些redis数据库的连接信息
还有有一个 RedisUtilService类,该类是操作RedisTemplate,进行key的添加,删除和获取缓存值
在项目中使用RedisController作为入口进行redis使用的测试
四 spring事务
1、要想事务起作用,必须是主方法名上有@Transactional注解,方法体内不能用try catch;如果要用,则catch中必须用throw new Exception
2、只有来自外部的方法调用才会把AOP代理捕捉,类内部方法嗲用类内部的其他方法,子方法并不会引起事务行为,即使被调用的方法上使用有@Transactional注解
总结: A方法有事务,调用了B方法,不管B有没有加事务注解,只要B抛出异常,两个方法都会回滚,(抛出异常一直向上抛,也不管有没有写throw)
如果将异常进行捕获,等于没抛出异常,自然也就不存在回滚。
什么情况下会回滚(只有在抛出异常的时候)
内层事务抛出异常,外层方法进行捕获。此时会有异常,此时需要在内层事务上加“PROPAGATION_NESTED”,即内层回滚,外层正常提交(注:该解释适用于内层事务在另外一个类中)
但如果在同一类中,内层事务是不起作用,外层事务捕获了异常,即两者都正常提交
同一类中事务A调用事务B,事务B会失效,如果事务A捕获了B的异常,B不会回滚,A正常提交,如果不对B进行捕获,AB 会抛出异常,事务也不会提交
《嵌套事务:就是事务方法A调用事务方法B,外层调用方法和内层被调用方法都是事务方法的情况》
我们一般情况下,会有以下三种需求:
外层调用方法和内层被调用方法,有异常一起回滚,没问题一起提交。(共用一个事务)
内层被调用方法回滚与否,不会影响外层调用方法。而外层调用方法出异常回滚,也不会回滚内层被调用方法(两个独立的事务)
内层被调用方法回滚与否,不会影响外层调用方法。而外层调用方法出异常回滚,也会回滚内层被调用方法(嵌套事务)
此处有一个坑:在同一类中事务A方法,事务B方法,B方法有异常抛出,在A方法中进行了捕获,此时两者都会提交,因为B事务实际上是不生效的(不会回滚的),如果A方法不经捕获,
异常一直向上抛,A,B都不会提交。 现实的情况是有大批量的for循环,我们不能让它抛出异常,且要让它有异常的回滚(这才是问题)
疑问:如果for循环,内层事务抛错了,需要内层,外层也回滚,但是不能影响到for循环下一条的执行,怎么做
五 关于xxl-job传入时间的问题
linux上传入的时间是GTC时间,也即时间标准时间,比中国时间晚了8个小时
所以传入的GTC时间,我们的Java项目接收json数据时间会转化为当前的CST时间,即中国时区时间
如果在xxl-job 上要需要获取昨天的GTC时间的话,需 cur_date=date -d yesterday -u "+%Y-%m-%dT%H:%M:%SZ"
示例 xxl上记录的时间 当前日期 2019-05-07T16:02:52Z
getPersonByDateAndName方法:打印传入的date时间:Wed May 08 00:02:52 CST 2019,建金中心,姓名:wangxiaomei
打印当前系统的时间:Thu May 09 00:02:52 CST 2019
六 ftp数据的上传和下载,可以使用FTPUtil,其中搭建ftp服务,可以在linux系统上搭建,搭建流程可参考
七 redis锁的应用
参考逾期跑批时加入redis锁,防止重复跑批
八 twitter的雪花算法
雪花算法产生的字符串的长度为18位,按递增,每秒可产生26万个不重复的ID
UUID是由一组32位数的16进制数字所构成,如550e8400-e29b-41d4-a716-446655440000
每秒产生10亿笔UUID,100年后只产生一次重复的机率是50%。如果地球上每个人都各有6亿笔GUID,发生一次重复的机率是50%。产生重复GUID并造成错误的情况非常低
UUID的唯一缺陷在于生成的结果串会比较长
自增ID:对于数据敏感场景不宜使用,且不适合于分布式场景。
GUID:采用无意义字符串,数据量增大时造成访问过慢,且不宜排序。
九 RabbitMQ之消息确认
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false); // false只确认当前一个消息收到,true确认所有consumer获得的消息
注:做过的项目使用的是这种方式
System.out.println("消息已重复处理失败,拒绝再次接收...");
channel.basicReject(message.getMessageProperties().getDeliveryTag(), true); // 拒绝消息
System.out.println("消息即将再次返回队列处理...");
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true); // requeue为是否重新回到队列
十 内存和cpu的关系
CPU是负责运算和处理的,内存是交换数据的
当程序或者操作者对CPU发出指令,这些指令和数据暂存在内存里,在CPU空闲时传送给CPU,CPU处理后把结果输出到输出设备上,输出设备就是显示器,打印机等。
十一 http请求第三方接口加密加签和验签解密
在src\main\java\com\example\demo\test\sign\SignTest.java类中
数字签名
就是只有信息的发送者才能产生的,别人无法伪造的一段数字串,它同时也是对发送者发送的信息的真实性的一个证明
签名
对需要上送的报文[en_data]计算特征值[sign_block],相关算法包括[md5、sha1、sha256、sha512等等]。
使用私钥[pri_key]对sign_block加密获得数字签名[sign]。
将sign与en_data打包发送给对方。
#验签
解析收到的报文,拆分为sign 及 en_data。
对en_data计算特征值[sign_block],相关算法包括[md5、sha1、sha256、sha512等等双方协商]。
使用公钥[pub_key]对sign解密,获得sign_block1。
比较sign_block 和 sign_block1,若匹配则验证成功,报文未被篡改。
十二、ApiAuthAop的使用
aop扫描com.example.demo.test.listmap.controller
所有访问这个controller下的接口,都会经过验签
比如说访问getUser接口,需要通过小米支付代发签名验证,首先这个controller上需要有 @ApiAuth(value = "MI_PAY_LOAN")
#之后通过 verifyChannelSignService.verifyMipayLoanSign(request), 使用对方的公钥对sign进行解密,和原始的json串content进行比较,相等则通过签名验证
在上一步需要注意 name和age参数是封装在encryptData中的,postman里面不需要写name和age参数,再加上encryptKey和sign这三个参数即可访问验签代码
#通过验签后,正式访问接口,接口使用DTO进行接收参数,DTO上需要加上注解 @ModelAttribute @RequestBody,尤其是@ModelAttribute,否则会报错,它用于从model、
Form表单或者URL请求参数中获取属性值
取出encryptKey,使用自己的私钥将encryptKey进行解密,得到原始AES KEY,再使用这个AES KEY将encryptData进行解密,得到原始的json串后进行业务操作
十三、redis在虚拟机上的启动
在虚拟机上的位置在 /usr/local/redis/bin
在bin目录下执行 ./redis-server /usr/local/redis/etc/redis.conf & (这种方法启动的时候,会去带上配置文件redis.conf,里面设置的有redis启动密码requirepass)
之后执行redis客户端 ./redis-cli
auth 123456 (是进入客户端)
设置密码 命令:config set requirepass 123456
查询keys 命令: keys *
十四、logback的使用
项目可以直接加上logback-spring.xml文件(直接添加就会生效),这样项目的打印日志就会如配置中的配置进行设置
如: spring-boot-wild [http-nio-48890-exec-1] [] - INFO [UserController.java:41] : 打印say的结果
催收项目日志打印
03:00:00.122 [http-nio-48810-exec-2] [42375a4b-7320-4827-abbe-d33fe97f279d] INFO [BatchService.java:114] - 当前批次号:20190916,上期批次号:20190816
其中[http-nio-48810-exec-2] 是项目集群中的启用端口
其中[42375a4b-7320-4827-abbe-d33fe97f279d]这个是只要请求同一个接口的所有操作,这个id是一样的
日志拦截器LogInterceptor.java (@Component,自启动)日志拦截器,打印出该请求所有方法链的requestId
注意:logback是slf4j的官方实现,log4j是另一个实现,logback和log4j才是二选一,slf4j是门面日志的api。
lombok注解中有 import lombok.extern.slf4j.Slf4j; 所以打印日志只需引入lombok注解即可
十五、http url 拼接
src\main\java\com\example\demo\util\StringConcatUtil.java 拼接url参数字符串
String.format(parm1,parm2,parm3)
十六、根据身份证地址的市级名称查询是否属于准入城市列表中之一
两个单元测试类 AddressListFilter是测试是否属于准入城市之一,AddressUtilsTest补全身份证的不全地址
用到service类 : AddressService
用到DTO是 AddressDTO UserAreaDTO 还有一个解析准入城市列表的DTO CityNameDTO
使用上面的类可以实现 根据不全的地址信息推算完整的地址信息,以过滤满足条件的城市
十七、抽奖的例子
src\main\java\com\example\demo\exercise\lottery\Lottery.java
随机产生一注奖或者多注奖,可以设置
十八、nginx配置负载均衡转发
nginx安装之后执行./configuration make make install
之后会自动生成一个nginx文件夹,这个文件夹就是安装目录,之后对这个安装目录进行配置就行
此处浪费了几个小时,在nginx安装包内的配置文件进行操作,完全不起作用,最后发现应该操作的是安装后的目录
即自动生成的那个nginx目录,需要改这里面的配置才会生效
总结:应该从最简单的改动,比如改下nginx启动页面的显示信息,看下时候改动生效,只想着实现功能,最后却发现改配置
改错了,浪费了很长时间,寻找问题所在从最简单的地方入手,才能更快发现问题
之后就可以使用nginx实现减库存(redis分布式锁的实现20191211 00:30)
使用JMeter工具
加上锁之后测试发现出现超卖情况,需要继续研究为什么会出现并发,扣减剩余后的相同数字
十九、使用诸葛老师讲的redis分布式锁
在exercise文件夹下的deduct_stock减库存的例子中
第一个版本使用生成一个UUID,在解锁的时候判断该线程是否执行完,再解锁
为什么使用UUID呢,是因为第二个线程进来的时候,第一个线程把第二个线程的锁给删掉了
第一线程10s已经失效,在执行到15s的时候,第二个线程执行5s,给第二个线程上锁,这个时候第一个线程15s的时候就将第二个线程的锁给删除了,是会有问题的
这时候还会有第三个线程来,接着第四个,第五个
高并发场景下无法预料线程什么时候来,造成锁失效,就会有问题。如果在秒杀情况下,一万的库存,可能就会有几十万的订单
#本线程加的锁,被其他线程给释放掉了的问题解决,就是给每个线程加一个UUID,在释放锁的时候,判断这个线程的UUID(redis锁的value),判断要删除的锁的线程是否是
当前线程
第二个版本使用redission分布式锁
由于导入不了redisson的依赖,所以redisson只做了解
RLock redissonLock = redisson.getLock(lockKey)
redissonLock.lock(30, TimeUnit.SECONDS)
redissonLock.unlock();
将Boolean relationIdLock = 注释掉
将finally里面的if语句也给去掉
DateUtil增加一个方法用于计算出当前时间和第二天凌晨之间的相隔的秒数
原本想通过这个方法将一个key设置为只有当天失效,其实也可以设置一个含有年与日的key,这样的话,第二天redis的key就会变为第二天的
key,重新统计当天这个key存储的次数
场景是:每个用户增加每日手输实名认证次数限制,每个自然日(0点-24点)有三次手输入实名认证机会,当手输实名请求次数大于3次时,拒绝请求,
并给出前端返回相应错误码,这样我可以设置一个key= 前缀+客户号+年月日作为当天含若干次数的key,到第二天key重新取值
安装idea插件
Alibaba Java Coding Guidelines 代码规范检查
FindBugs-IDEA 这个插件可以帮助我们查找隐藏的bug,比较重要的功能就是查找潜在的null指针
Maven Helper 分析依赖冲突的插件
Grep Console 日志高亮显示插件
Rainbow Brackets 可以实现配对括号相同颜色,并且实现选中区域代码高亮的功能
Free Mybatis plugin 增强idea对mybatis支持的插件,点击箭头可以实现跳转