java的泛型
[TOC]
一、介绍
泛型实现了参数化类型的概念,使代码可以应用于多种类型,设计的初衷应该是希望类或者方法能够具备最广泛的表达能力。在引入泛型之前,一般都是依赖于Object
顶层对象实现类似泛型的功能,但是使用Object
有一个缺陷是如果类型转换异常,编译器在编译期无法检测这种异常,只有在字节码的运行时期才会抛出类型转换异常。而JDK 1.5
之后引入的泛型,在编译期就会对类型进行检查,使得问题可以及早发现。
[TOC]
泛型实现了参数化类型的概念,使代码可以应用于多种类型,设计的初衷应该是希望类或者方法能够具备最广泛的表达能力。在引入泛型之前,一般都是依赖于Object
顶层对象实现类似泛型的功能,但是使用Object
有一个缺陷是如果类型转换异常,编译器在编译期无法检测这种异常,只有在字节码的运行时期才会抛出类型转换异常。而JDK 1.5
之后引入的泛型,在编译期就会对类型进行检查,使得问题可以及早发现。
[TOC]
过滤器
Filter
是通过实现java.servlet.filter
接口实现过滤器功能,作用是用于对传入的request
和响应的response
进行一些处理,比如对请求参数进行校验,或者设置、检验头部信息,再或者对一些非法行为进行校验。由实现的接口可知,过滤器是依赖于servlet
容器。所以由于过滤器不依赖于spring
容器,它也就无法获取到容器中的对象。
[TOC]
在mybatis
中从sql
的解析到最后结果集的返回,经过了一系列的内部组件,比如参数处理器parameterHandler
,语句处理器StatementHandler
,结果集处理器ResultSetHandler
等。若开发者需要对SQL
执行的某一环节进行一些特定的处理,比如参数类型的转换,数据分页功能,打印执行的SQL
语句等都可以通过mybatis
的插件机制实现。
[TOC]
因为需要,研究了可以通过InheritableThreadLocal
进行父子线程中如何传递本地线程变量,通过阿里开源项目TransmitableThreadLocal
进行进行线程池传递本地线程变量(详解可查看以往博客)。在查找资料的过程中无意发现了Dobbo
的InternalThreadLocal
,其实Dobbo
的InternalThreadLocal
和netty
的FastThreadLocal
有异曲同工之妙。之前学netty
的时候有了解一点,为了加深一下ThreadLocal
种群的了解,使用本博客记录一下。
[TOC]
在Thread
中除了有属性threadLocals
引用ThreadLocal.ThreadLocalMap
,其实还有一个属性,也就是inheritableThreadLocals
,threadLocals
的作用是保存本地线程变量,而inneritableThreadLocals
的作用是传递当前线程本地变量InheritableThreadLocal
到子线程的本地变量InheritableThreadLocal
中。
[TOC]
Thread
有一个内部变量ThreadLocal.ThreadLocalMap
,这个类是ThreadLocal
的静态内部类,它的实现与HashMap
类似,当线程第一次调用ThreadLocal
的get/set
方法时会初始化它。它的键是这个ThreadLocal
对象本身,值是需要存储的变量。也就是说ThreadLocal
类型的本地变量是存放在具体的线程空间里。当不断的使用get
方法获取时,是到线程独有线程空间中获取变量,使得其他线程无法访问到,也就达到了线程安全的目的。在使用完成之后,可以通过remove
方法,移除不使用的本地变量。
[TOC]
上文说到父子线程传递本地变量可以通过InheritableThreadlocoal
进行传递,但是如果采用线程池,不一定能传递,因为在线程在线程池中的存在不是每次使用都会进行创建,InheritableThreadlocal
是在线程初始化时intertableThreadLocals=true
才会进行拷贝传递。所以若本次使用的子线程是已经被池化的线程,从线程池中取出线下进行使用,是没有经过初始化的过程,也就不会进行父子线程的本地变量拷贝。
由于在日常应用场景中,绝大多数都是会采用线程池的方式进行资源的有效管理。目前知道的阿里有一个开源项目就是为了解决这个问题ThansmittableThreadLocal
。
由于项目包结构的修改对feign
调用的类的编写修改了路径,原来的路径为cn.com.projectname.feign
包下,新的feign
需添加到cn.com.projectname.common.feign
下。首次添加到该包下发现,新的feign
类没有被加载到spring
容器中,所以推测是扫包路径的问题,故通过@EnableFeignClients(basePackages = {"cn.com.projectname.feign", "cn.com.projectname.common.feign"})
指定扫包路径,神奇的事情发生了,启动之后报错,内容为cn.com.projectname.common.feign
下新增的那个feign
类已经被加载到spring
容器中,不允许二次加载!找了许久并没有找到什么问题。还尝试通过@FeignClient(contextId = "xxx")
的方式指定创建类名称,也还是失败了,最后发现玄机是在@EnableFeignClients
的basePackages
上,由于指定的两个包路径出现重叠部分,导致在扫包过程中重复添加某对象。
1 | public static void main(String[] args) { |
由于b
是包装类型,而a
是基本数据类型,在比较时对b
进行拆箱,所以会通过((Integer)null).intValue()
进行拆箱,从而引发NPE
。
问题:在业务场景中,某些敏感数据需加密之后保存到数据库,而加密字段刚好又是频繁触发查询的字段。在一次插入数据时引发唯一索引异常。原因是索引长度小于数据库字段长度,在比对数据唯一性时截取了该字段的前面部分长度,导致匹配成功,抛出唯一索引异常。
方案:这种情况下不能随意调整索引的长度,这涉及到全局的修改。故通过新增一个字段保存改加密字段MD5
值,并将索引挂在这个新增字段上,这样既保证了索引的唯一性,又可以不用修改索引长度。