实习记录:
1. <insert id="insertUser" parameterType="User" keyProperty="id">
insert时,加上keyProperty后,在插入user后,id会自动回显到user对象的id,不需要再手动set;不加keyProperty时,id不会回显,通过getId方法也无法获取,只能在一次从数据库中查数据才能获得id。
2. 表现层调用业务层,业务层调用数据层,所以数据层的异常也会抛出来到表现层,所以统一异常处理只需要处理表现层的异常即可。
3. 统一异常处理:resources ->templates -> error -> 状态码.html (eg: 404.html 500.html),这样当出现异常时,会自动跳转到对应状态码的Html页面。
4.
@ControllerAdvice(annotations = Controller.class)用于所有注解了@Controller的类,@ExceptionHandler({Exception.class})用在方法上,处理所有controller类的Exception异常。
5. AOP面向方面(切面)编程 Aspect-Oriented programing
AOP是一种编程思想,是对OOP的补充
日志记录:不能用控制器通知(ControllerAdvice),只能记录error日志;也不能用拦截器,拦截器只能对Controller记录日志,而服务层和数据访问层无法记录日志。
业务组件中最好不要有系统需求(记录日志)。
理解概念比较抽象,但是代码比较简单。
目标对象Target(业务模块:帖子业务、评论业务、私信业务)上织入代码的位置叫做Joinpoint;
Pointcut,是用来定义当前的横切逻辑准备织入到哪些连接点上
Advice,用来定义横切逻辑,即在连接点上准备织入什么样的逻辑
Aspect,是一个用来封装切点和通知的组件
Spring AOP限制了连接点必须是方法执行级别
@Aspect @Pointcut("execution(* com.nowcoder.community.service.*.*(..))") 切点
Service组件中每个方法都是连接点 @Before("pointcut()")
@AfterReturning("pointcut()") @After("pointcut()") @AfterThrowing("pointcut()")
@Around("pointcut()") 五种方法
Spring AOP只能织入到方法中,而且是运行时织入,不能解决所有的AOP问题,但可解决绝大多数问题(性价比价高),对于其他高级AOP需求,可配合AspectJ(Spring也支持AspectJ的集成)实现。
Spring AOP在运行时通过代理的方式织入代码,只支持方法类型的连接点。
AOP不需要在业务类中写任何代码,不会影响业务组件,在运行时切面内的通知会织入到连接点
6. spring中也支持redis事务,但是redis事务是把所有指令放到一个队列里,提交事务时一起执行,所以在事务中查询数据时不会立刻返回结果,所以要查数据要么在事务前要么在事务结束后,因此声明式事务在redis(@Transactional注解)中也不常用,大多使用编程式事务(把事务缩小,事务之外可以进行查询)。
7. 利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
拦截器@Interceptor和统一异常处理@ControllerAdvice也用到了AOP思想!
(思考)Spring AOP自定义实现(不使用拦截器,不implements HandlerInterceptor)对未登录的用户进行拦截:切点Pointcut为Controller类(拦截器是在请求到达Controller前进行拦截),通知advice(@Before)为验证ticket是否有效,JointPoint还是各个方法,target为需要登录的controller业务方法。
其实现原理是使用动态代理技术,在方法运行时动态生成代理对象,然后插入切面代码。
应用场景:
1)日志记录:记录方法的入参、出参以及方法的执行时间等信息;
2)安全检查:在方法执行前对用户进行身份验证,判断其是否具备访问方法的权限;
3)性能监控:记录方法的执行时间,方便分析程序性能瓶颈;
4)事务管理:在方法执行前开启事务,在方法执行后根据方法执行结果提交或回滚事务;
5)缓存:在方法执行前判断缓存中是否存在方法的结果,在方法执行后将结果存入缓存中,方便下次调用时使用。
8. 点赞使用的Redis数据库,数据访问层的操作十分简单,所以省去写数据访问层,直接写业务层。
9. redis存验证码:解决了分布式下的session共享问题,且可以设置过期时间。
redis存登陆凭证(LoginTicket):可以不再在mysql的表中存储;每次处理请求时,都会查询用户凭证(login_ticket表)和用户信息(user表),访问频率很高,对性能有影响。
redis缓存用户信息:缓存(有过期时间)的同时,还要在mysql中存储
10. 优化:
重构验证码功能:由于还未登录,所以无法获取userId,因此redisKey中只能用一个字符串来代替userId:后端generateUUID(),将结果存在cookie中,然后以此为key,value存生成的验证码text。(与session的思想类似,浏览器也是根据cookie来寻找对应的session)
重构登录凭证功能:key的格式为:ticket:生成的ticketUUID -> 序列化后的LoginTicket的json字符串。
redisTemplate.opsForValue().set(redisKey, loginTicket); // redis会自动将loginTicket对象序列化为json字符串存储
LoginTicket loginTicket = (LoginTicket)redisTemplate.opsForValue().get(redisKey);
缓存用户信息:1.优先从缓存中取值 2.取不到时初始化缓存数据 3.数据变更时清除缓存数据(修改的话可能会有并发错误,而且修改逻辑有些复杂,所以更新时直接删掉redis中的缓存数据,下次用的时候在再初始化)
先从redis中取值,如果没有的话,再从mysql中查找并且缓存到redis库中。
11. kafka和rebbitMQ等都是对阻塞队列做了深度封装,都是基于阻塞队列(BlockingQueue接口,java自带的)来做的。
BlockingQueue可以解决线程通信问题,类似Object的wait()和notify()函数。
生产者消费者模式,生产者put ->| | | | |->消费者take
实现类:ArrayBlockingQueue, LinkedBlockingQueue, PriorityBlockingQueue, SynchronousBlockingQueue, DelayQueue
12. java启动线程的四种方法
1)继承Thread类, 重写run方法,在main函数中,调用start方法。
2)实现Runnable接口,重写run方法,在main函数中,调用start方法。
3)实现Callable接口,重写call()方法,在main函数中调用start()方法。
4)线程池
13. Kafka的特点:
对硬盘的顺序存取的效率是很高的,甚至高于堆内存的存取,但是硬盘的随机读取是较慢的。Kafka的消息持久化就是将消息存储在硬盘上,它的高效就是因为对硬盘的顺序存取。
高可靠性:是分布式的,有冗余备份
高扩展性:机器不够用了,进行很少的配置就能加到集群中。
14.Kafka术语:
Broker:一个Kafka服务器
Zookeeper:用于管理集群的软件,Kafka中也有内置的集群管理组件
消息队列的模式:点对点(测试代码),订阅模式(Kafka,订阅了的消费者都可以获得消息)
Topic主题,类似于一个文件夹,用于存储消息的位置,(消息存放的地方,代表一个消息类别,比如点赞的消息/关注的消息)
Partition分区,一个主题可以有多个分区,提高并发能力
Offset消息在分区内存放的索引
Replica副本,Kafka是分布式的,备份有助于提高高可靠性
Leader Replica主副本,备份同时可以做出响应
Follower Replica从副本,只做备份,不能作出响应
主副本挂掉后,集群会从众多的从副本中选一个当作新的主副本。
Kafka是Apache的
15. 环境配置问题:
下载Kafka, 更改java环境变量优先级,JAVA_HOME中有"()",删掉后即可正常运行
16. 启动Kafka的命令:
第一个cmd: 进入F:\Kafka\kafka_2.12-2.3.0目录,运行命令启动Zookeeper : bin\windows\zookeeper-server-start.bat config\zookeeper.properties
第二个cmd: 进入F:\Kafka\kafka_2.12-2.3.0目录,运行命令启动Kafka: bin\windows\kafka-server-start.bat config\server.properties
第三个cmd: 进入F:\Kafka\kafka_2.12-2.3.0\bin\windows目录,运行命令创建主题topic:kafka-topics.bat --create --bootstrap-server localhost:9092 --replication-factor 1 --partitions 1 --topic test(服务器为localhost:9092,1个副本,1个分区,topic名叫test)
查看现有的主题topic: kafka-topics.bat --list --bootstrap-server localhost:9092
生产者给test主题发消息: kafka-console-producer.bat --broker-list localhost:9092 --topic test
第四个cmd: 进入F:\Kafka\kafka_2.12-2.3.0\bin\windows目录,运行命令读取test主题的消息:kafka-console-consumer.bat --bootstrap-server localhost:9092 --topic test --from-beginning
生产者可以发送消息,消费者可以接收消息
Kafka的cmd按下Ctrl+C,输入Y,再运行命令关闭Zookeeper: bin\windows\zookeeper-server-stop.bat
日志:
2023.07.17:
1. 实现私信列表和私信详情页面(没有新技术,都是业务方面的逻辑)
2. 实现发送私信,消息已读的功能
3. 微信公众号菜单是否可以跳转到外链(个人号菜单只能跳转内链,可以通过阅读原文、点击图片、自动回复、二维码来实现)
4. 统一异常处理(Spring boot项目可以直接把error文件夹放到resources->templates下,文件名为404.html...)
2023.07.18:
1. 统一处理异常,借助@ControllerAdvice和@ExceptionHandler两个注解,处理异步和同步两种请求(返回json和页面)
2. Spring整合Redis,RedisTemplate(@Bean注入第三方对象),调用时用redisTemplate.opsForValue().get(redisKey); BoundValueOperations绑定redisKey; Redis编程式事务
3. 理解AOP,并编写代码。@Component @Aspect @Pointcut("execution(* com...service.*.*(..))") @Before("pointcut()) @After @AfterReturning @Aroud @afterThrowing; 不用在业务类中写任何代码,只用在Aspect中写很少代码
4. 已学习完牛客论坛第三章
2023.07.19:
1. 实现对帖子和评论的点赞功能,用的Redis数据库,所以mysql中没有这个表,用set数据结构,存点赞的userId
2. 重构点赞功能,加上incr(targetUserId), decr(targetUserId),即以用户为key查询获赞数量
3. 实现关注和取消关注功能,高频操作再次利用redis存储
4. 实现关注列表和粉丝列表(未完成)
2023.07.20:
1. 实现关注列表、粉丝列表所有功能,我的帖子和我的回复页面跳转
2. 重构登录模块,redis存验证码(思想类似于session),redis存登录凭证(redis可以存value为LoginTicket或User的序列化json对象),redis缓存用户信息(更改用户信息时,直接在缓存中删除;每次访问先在redis中查找,取不到再去mysql查并且缓存到redis)
3. 牛客论坛第四章完结撒花
4. 调整长图大小
5. 整理步云流海小程序的json
2023.07.21:
1. 八股:三次握手、四次挥手
2. 生成小程序的json ;整理募捐活动照片、视频
3. 测试BlockingQueue类(ArrayBlockingQueue, put, take),看java启动线程的四种方式
4. 下载Kafka, 更改java环境变量优先级,JAVA_HOME中有(),删掉后即可正常运行(之后每次项目运行之前,都要保证Zookeeper和Kafaka是开启状态)