SpringBoot如何实现拦截

SpringBoot如何实现拦截

本文探讨SpringBoot中如何实现拦截的几种方式

1.实现 Filter 接口

实现方式:

  1. 新建一个类实现Filter接口,然后将此类注册到FilterRegistrationBean,配置拦截路由等。
  2. 加上@WebFilter注解,参数传入拦截路由等,需要配合@ServletComponentScan才能生效。

2.实现 HanlderInterceptor 接口

HandlerInterceptor 用于拦截 Controller 方法的执行,主要声明3个方法,preHandle、postHandle和afterCompletion,分别代表方法执行前调用、方法执行后视图渲染前调用和试图渲染后调用,最后别忘了对Interceptor进行注册。

3. @ExceptionHandler注解对异常进行拦截

@ExceptionHandler 的用途是捕获方法执行时抛出的异常,通常可用于捕获全局异常,并输出自定义的结果。@ExceptionHandler 需要与 @ControllerAdvice配合使用其中 @ControllerAdvice的 assignableTypes 属性指定了所拦截类的名称。

4. @Aspect注解

@Aspect使用此注解面向切面编程,是Spring核心理念之一的AOP思想。

SpringBoot解决跨域

SpringBoot解决跨域

什么是跨域请求,当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域,如‘https://www.baidu.com/:8080’,其中‘https’,‘www’,‘baidu。com’,‘8080’任意一个元素不同,即为跨域。

Access-Control-Allow-Origin:https://test.baidu.com

解决方法

使用Cors协议,CORS全称为Cross Origin Resource Sharing(跨域资源共享), 每一个页面需要返回一个名为Access-Control-Allow-Origin的http头来允许外域的站点访问。

  1. @CrossOrigin注解
    在Controller上使用@CrossOrigin注解,则该类下的所有接口都可以通过跨域访问,在CrossOrigin注解中写上允许跨域访问的URL。

  2. CORS全局配置-实现WebMvcConfigurer
    新建一个跨域配置类,实现WebMvcConfigurer接口,在里面配置相应的信息。

  3. 拦截器
    可以配置一个拦截器,将所有请求都拦截,加上Access-Control-Allow-Origin等参数后放行。

线程间通信(同步)

线程间通信(同步)

在面试中遇到一个问题,问线程间是怎么通信的,一时间有点懵,这个问题面试官想要问的是啥呢,结束面试查阅资料后,才理解这个问题考察的是线程间等待唤醒机制等,就是线程间同步,ORZ!一个送分题,因为没理解想问什么知识题白给了。

线程间通信(同步)的4种方式

  1. volatile关键字
    对变量使用此关键字修饰后,一个线程对此变量的修改,其他线程是立马可见的,即所有volatile修饰的变量一旦被某个线程更改,必须立即刷新到主内存,同时所有volatile修饰的变量在使用之前必须重新读取主内存的值,此为可见性,保证了线程间的通信。

  2. 等待/通知机制
    即wait/notify、notifyAll,线程调用wait方法后即会将自己置于等待状态,直至被其他线程唤醒,notify和notifyAll方法会将线程唤醒,执行后续操作,notify唤醒随机一个线程,notifyAll唤醒全部线程。

  3. join方式
    join可以保证线程的顺序执行,对其理解是join相当于一个容器,一个线程执行join方法后,其他线程会暂停等待,直到执行join方法的线程执行完毕。

  4. ThreadLocal(可以解决全局通用变量线程不安全的问题)
    :ThreadLocal中有一个ThreadLocalMap内部类,ThreadLocalMap可以维护线程内部变量,是key-value形式,key为当前ThreadLocal,value就是变量的副本。因此,这样对于SimpleDateFomat此类线程不安全的类,同时又是一个全局通用的变量来说,可以使用ThreadLocal管理和维护一份即可。建议使用InheritableThreadLocal,此类继承自ThreadLocal,不仅仅本线程可以获取存的值,它的子线程也可以。

  5. 阻塞队列LinkedBlockingQueue

HashMap的底层实现

HashMap

众所周知,HashMap是一种以Key-value形式表现的容器,在java日常开发中非常常见,使用频率很高,但是究其底部原理,本人其实并不太了解,因此写此文章探讨一下。

实现原理

当我们对HashMap进行put的时候,首先会通过hashCode()方法计算出key的hash值,然后再用这个hash值去找在HashMap中对应的位置,如果对应位置为空,那么就直接存储进去了,如果不为空,则会调用equals()方法判断此位置上存储的key和想要存的key是否重复(equals()方法的工作原理是判断对象的值是否相等,即所存内存空间上的值是否相等),如果相等则直接结束,如果不相等,则会以链表的形式存储,当链表长度大于8且HashMap长度大于64时,会将链表改为红黑树的形式存储。

存储形式

HashMap的存储形式是一个位桶数组数组Node<K,V>[],Node可以理解为我们平时使用的Entry,它实现了Entry接口,Node中存储了对象的hash、key和value,当对HashMap进行getValue()操作时,首先会用key计算出hash值,然后在HashMap中找到对应的位置,判断此位置上的对象的key和手上的key是否相等,如果不相等,则遍历链表,直到找到相等的key或者全部遍历结束为止。

负载因子

HashMap默认的负载因子是0.75,即当容器填满了75%容量的时候,就会创建一个容量是原来两倍的新容器,并将原来容器中的对象放入新容器中。

HashMap的key有什么必要条件

  1. 遵守equals()方法和hashCode()方法。
  2. 是不可变的,即在put()以后就不会再改变了。

HashMap不适用于多线程

HashMap不建议在多线程下使用,多线程建议使用ConcurrentHashmap。

因为HashMap存在扩容机制,在多线程条件下,可能会产生循环链的情况,即线程A和B都发现HashMap需要扩容了,同时进行扩容,但是由于扩容的进度不一致,可能会导致循环链的情况,从而导致get()死锁。

MySQL优化

MySQL基本优化方法

  1. 查询语句中不要使用select *
  2. 尽量减少子查询,使用关联查询(left join,right join,inner join)替代
  3. 减少使用IN或者NOT IN ,使用exists,not exists或者关联查询语句替代
  4. or 的查询尽量用 union或者union all 代替(在确认没有重复数据或者不用剔除重复数据时,union all会更好)
  5. 应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。
  6. 应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如: select id from t where num is null 可以在num上设置默认值0,确保表中num列没有null值,然后这样查询: select id from t where num=0。
  7. 建索引。
  8. 不要在列上进行运算。select * from users where YEAR(adddate)<2007;将在每个行上进行运算,这将导致索引失效而进行全表扫描,因此我们可以改成select * from users where adddate<‘2007-01-01’;
  9. 排序的索引问题。mysql查询只使用一个索引,因此如果where子句中已经使用了索引的话,那么order by中的列是不会使用索引的。因此数据库默认排序可以符合要求的情况下不要使用排序操作;尽量不要包含多个列的排序,如果需要最好给这些列创建复合索引。
  10. 索引不会包含有NULL值的列。只要列中包含有NULL值都将不会被包含在索引中,复合索引中只要有一列含有NULL值,那么这一列对于此复合索引就是无效的。所以我们在数据库设计时不要让字段的默认值为NULL。
  11. 尽量避免在字段开头like模糊查询,会导致数据库引擎放弃索引进行全表扫描。
  12. 尽量避免在where条件中等号的左侧进行表达式、函数操作,会导致数据库引擎放弃索引进行全表扫描。如SELECT * FROM T WHERE score/10 = 9改为SELECT * FROM T WHERE score = 10*9。
  13. 多表关联查询时,小表在前,大表在后。因为执行 from 后的表关联查询是从左往右执行的(Oracle相反)。

大表如何处理

  1. 写查询SQL时务必不能写没带任何查询条件的SQL。
  2. 数据库拆分,读写分离,主库负责写,从库负责读。
  3. 垂直拆分,即将一张大表拆成两张小表。
  4. 水平分区,对存储数据进行数据分片,即保持数据结构不变,将一张表的数据存放在不同的表或者库中(具体如何实现还未了解,只是知道有这么一种方法)。

多线程-锁

多线程-锁

在实际开发中,经常会出现多线程的情况,但是本人此前的工作中并不处于多线程的成产环境,并未对多线程的处理有很多理解,因此特此写下文章记录对于多线程处理的学习。

悲观锁

见名之意,悲观锁就是用悲观的态度对待可能发生的数据冲突,即假设每一次对数据的修改操作都会发生冲突,所以每一次修改数据前都会将数据锁住,直到释放锁以后其他人才能操作。可以完全保证数据的独占性和正确性,但是性能低。是阻塞的

乐观锁

乐观锁就是假设数据冲突的频率不高,不会对操作的数据加锁,只有到数据提交的时候才去验证数据是否冲突,即判断新值和旧值是否相等,相等则不冲突,但是可能发生ABA问题,即线程1取出数据为A,线程2取出数据也为A,然后线系2对数据进行操作修改为B,再进行操作修改为A,此时线程1继续对数据进行操作,仍然会成功,解决办法可以增加一个版本号进行验证,通过版本号加旧值进行验证。是非阻塞的

分布式锁

在分布式系统架构上,传统的单机应用加锁就会失效了。因为可能会在多台服务器创建多个对象,每个对象都能获取到自己的锁。此时就需要分布式锁了。
分布式锁有如下3种方法:

  1. 数据库实现。即创建一个表,表中包含方法名等字段,并在方法名字段创建唯一索引,需要执行某个方法时,就使用这个方法名向表中插入数据,成功插入则获取锁,执行完成后删除对应的行数据释放锁。
  2. 基于缓存(Redis等)实现分布式锁。主要需要用到的命令:SETNX、expire、delete。利用Jedis。
  3. redisson实现。相对于Jedis而言,Redisson更强大。它里面也实现了分布式锁,而且包含多种类型的锁。

参考文章:

  1. https://www.cnblogs.com/liuqingzheng/p/11080501.html
  2. https://www.jianshu.com/p/47fd7f86c848

税友实习日记:一周小结(6)

税友实习日记第六周小结

本周工作内容

  1. 本周主要进行的工作是对与开发模块的bug修改,不得不说,整个流程下来,最让我头疼觉得烦的就是现在的这个测试阶段了,改bug真是个麻烦的事情,尤其是对于前端样式的修改,对于我这种后台开发人员来说,真是麻烦啊。

  2. 本周还在进行一个通道的开发,是全新学习的一个内容,我的理解是数据库的数据交互,将一个数据库中的数据同步到另一个数据库,但是有一个细节上的问题感觉很麻烦,大体上一条业务只需要执行一次同步就可以了,但是这个业务中有一个细节需要封装成一个集合进行多次同步,很麻烦。

  3. 对于改bug的体验就是,一生二,二生三,三生万物,改完一个bug,衍生出其他的bug,好烦啊。

本周开发中的一些小问题

  1. 对于开发中的需求理解及自测需要更加注重,不然到了测试阶段真是麻烦啊。

  2. 学习新东西的过程很痛苦,但是学完以后会很有成就感,不要排斥,慢慢接受,这样才会有成长。

总结

对于开发过程中忽视的东西在测试过程中有了更清晰的认识,争取同一个错误下次不要再犯,还是要细心!细心!细心啊!!!。


以上就是在税友实习的第六周小结,加油,继续努力。

税友实习日记:一周小结(7)

税友实习日记第七周小结

本周工作内容

  1. 本周的工作内容是对于通道的学习与使用,对于通道的理解是分为发送端与接收端,发送端用于传入参数然后查询出符合条件的需要进行传输的数据库记录,而接受端则执行对于的方法进行数据库的读写,其中发送端与接收端的连接是一个自己定义的PO实体对象。

  2. bug的修改已经基本完成,剩下通道方面的东西。

本周开发中的一些小问题

  1. 对于通道,似乎是公司自己封装的一个东西,使用起来极为不方便的地方是只能在测试环境进行测试是否正确,因为这涉及到两个独立的数据库之间的数据传输,而开发环境没有这个条件,还有就是通道无法debug,只能自己拉日志进行排查,有些错误实在不知道哪错了,有点难受。

总结

通道的东西已经了解了大概八十的样子,已经会使用了,只是还有一些坑,以及进行错误排查及修改的方法需要多加练习,其中本周碰到的一个坑是PO实体对象中的数据类型必须和数据库中一一对应,比如数据库中为NUMBER,DATE等数据类型无法用String来接收。

本周还进行了为期两天半的培训,我本人是有点社恐的,一开始还有点难受,但是随着时间久了,竟然觉得这么一个小team的感觉还挺好,很有爱,还进行了各种团体活动,反正挺温暖的,最后还进行了情景剧的表演,表演完以后培训也就结束了,大家也就就此别过了,有点不舍。


以上就是在税友实习的第七周小结,加油,继续努力。

税友实习日记:一周小结(5)

税友实习日记第五周小结

本周工作内容

  1. 新模块的开发以及处于完成阶段,剩下一个页面的功能需要等待前端完成进行继续开发,其页面的后台功能已基本完成。

  2. 有一个页面中的排序功能,是同时排序,主关键字段相同时,则用第二关键字段进行排序,实现此功能可以使用order by多个条件,但是要注意的是排序的时候要对空值进行处理,否则空值会默认排在第一个。

  3. 对于开发中粗心的地方有了更多注意,比如后台返回数据是集合还是一个对象,如果前端调用的方法是对于集合的,即使这个功能只会返回一条记录,也该返回一个集合。

  4. 对于后台提取到的数据是HTML类型的,要在前台页面显示部分,可以只提取其中的纯文本内容,用String发replaceAll加正则过滤html标签及特殊字符,然后再对中英文的长度进行处理,因为中英文的字节数不同。

本周开发中的一些小问题

  1. 后台该返回的数据类型,应该和前台调用的方法匹配。

  2. 对于开发过程中出现的问题,未必是逻辑上或者代码上有错误,可能是项目的配置中与使用的东西不匹配,此时需要往项目的配置中钻,找到对应的地方进行匹配解决。

总结

开发能力已经处于上手水平,对于基本的开发内容已经可以顺风顺水的完成,找错误的能力也有所提高,对于SQL的理解也有了更深的理解,可以比较灵活的运用,对于问题的解决思路也更加有思路了。


以上就是在税友实习的第五周小结,加油,继续努力。

税友实习日记:一周小结(4)

税友实习日记第四周小结

本周工作内容

  1. 对于负责的任务管理模块进行了细节上的优化,下周进行代码评审与测试,希望没啥大问题。

  2. 有一个新负责的模块,是没有接触过的几乎全新的内容,是一个论坛性质的模块。

  3. 对于一些问题的解决有了更灵活的思路,比如查数据库数据时,有时可以不用创建两个方法,只要在SQL中操作就行,比如JS中两个方法要先后执行,可以不这么做,转到后台去完成。

本周开发中的一些小问题

  1. 前台页面显示的问题,datagrid中有一个输入框,此时想要使它有默认值,只能在后台操作,因为他的数据是写上去的,直接设置默认值是无效的。

总结

对于开发中,开发速度有了很大提高,只是方法中老有问题,要是开发过程一路顺利,那么很快就能完成,但是我喜欢钻牛角尖,一旦遇到一个小问题就会花很长时间去解决它,甚至有时候还是白费时间,而这个问题又是个小问题,因此会耽误很长时间,以后开发时需要改掉这个毛病,实在一时解决不了的小问题就先放掉,先做大头。


以上就是在税友实习的第四周小结,加油,继续努力。

请我喝杯咖啡吧~

支付宝
微信