双亲委派模式
类加载器有三种:在java8里
1:启动类加载器:由c++编写,系统首先创建的就是启动类加载器 加载路径:jre/lib/xxx.jar 和 jre/classes/bbb.class
2:扩展类加载器:加载jre/ext/yyy.jar 只能加载jar包
3:应用类加载器 : 包含 启动类加载器和扩展类加载器的范围
这里的双亲指的是一个类里面的一个存放父加载器的实例变量
在java14里,扩展类加载器变了
什么是双亲委派模式?
如果其中一个类加载器收到了类加载的请求,它并不会自己去加载而是会将该请求委托给父类的加载器去执行,如果父类加载器还存在父类加载器,则进一步向上委托,如此递归,请求最终到达顶层的启动类加载器。如果父类能加载,则直接返回,如果父类加载不了则交由子类加载,
类加载的过程
当加载一个类的时候,从应用类加载器开始,
1:应用类加载器首先让自己的 parent (扩展累加载器)加载
2:扩展类加载器则让自己的parent (启动类加载器)加载
3: 如果启动类加载器 路径 里面有,则由启动类加载器 加载
4:如果没有,则从扩展累加载器路径里寻找,如果有则由
扩展类加载器加载
5:如果没有,则由应用类加载器加载
类的加载是有方向的,如果 A类 被扩展类加载器,那么A类依赖的其他类,只能在扩展类加载器 或者 启动类加载器 路径里寻找,即使没找到,也不能再应用类加载器路径里寻找
为什么要用双亲委派模式?
1:避免类的重复加载,当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次。
2:其次是考虑到安全因素,java核心api中定义类型不会被随意替换,假设用户创建了一个全限定类名为java.lang.Integer的类,
通过双亲委托模式传递到启动类加载器,而启动类加载器在核心Java API发现这个名字的类,发现该类已被加载,并不会重新加载用户自己的java.lang.Integer,而直接返回已加载过的Integer.class,这样便可以防止 核心API库被随意篡改。
为何包名要域名反写?
双亲委派模式中某个类加载器 判断某个类是否在该类加载路径下,依据应该是类的全限定类型(也就是有包名前缀!!!
我们不能创建跟jdk中一样的包,这一是我们域名反写创建包的原因之一,保证唯一性。
proxy.newProxyInstance 为什么要传入参数类加载器?
确保加载被代理类的类加载器不是null
if (VM.isSystemDomainLoader(loader) && !VM.isSystemDomainLoader(ccl)) { sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION); }
public static boolean isSystemDomainLoader(ClassLoader var0) { return var0 == null; }
如何防止sql注入?
1:前端比如可以过滤掉“;”分号
2:后端可以用PrepareStatment 预编译sql语句,用 ? 占位符
mybatis #{} 就是这个原理 ${} 没有预编译,所以需要做前端参数校验
3:正则表达式?
什么是 CSRF 攻击,如何避免?
try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?
会。
执行顺序是:try catch finally
1:返回值类型是普通类型
如果finally 中有return,无论try或者catch中有没有return 则以finally 的返回结果为准
如果finally中没有 return,catch中有return,则以catch中return时的返回值为准,后来执行的finally无论怎样都不会改变返回值结果(因为结果已经在执行finally之前的 catch 中返回过去了)
如果finally中没有 return,try中有return,则以try返回的值为准。
2:返回值类型是引用类型
引用是一个,无论是在try catch finally 那个步骤中 对引用的对象做了修改,都会影响返回的最后结果
其实就不能说是“返回”结果了,只能说是返回后再修改
跨域
CORS
常见的Spring注入方式有几种?
比如我们要在UserServiceimpl层里注入userDOMapper
1:用注解 @Autowried (+@Qualifieri) @Resource
2:setter方法注入(需要写xml
3:构造方法注入(需要写xml
spring 支持几种 bean 的作用域?
五种
singleton:单例模式,在整个Spring IoC容器中,使用singleton定义的Bean将只有一个实例
prototype:原型模式,每次通过容器的getBean方法获取prototype定义的Bean时,都将产生一个新的Bean实例
request:对于每次HTTP请求,使用request定义的Bean都将产生一个新实例,即每次HTTP请求将会产生不同的Bean实例。只有在Web应用中使用Spring时,该作用域才有效
session:对于每次HTTP Session,使用session定义的Bean豆浆产生一个新实例。同样只有在Web应用中使用Spring时,该作用域才有效
globalsession:每个全局的HTTP Session,使用session定义的Bean都将产生一个新实例。典型情况下,仅在使用portlet context的时候有效。同样只有在Web应用中使用Spring时,该作用域才有效几乎90%以上的业务使用singleton单实例就可以,所以spring默认的类型也是singleton,singleton虽然保证了全局是一个实例,对性能有所提高,但是如果实例中有非静态变量时,会导致线程安全问题,共享资源的竞争
当设置为prototype时:每次连接请求,都会生成一个bean实例,也会导致一个问题,当请求数越多,性能会降低,因为创建的实例,导致GC频繁,gc时长增加
@Component @Service @Controller @Repository 本质都是一样的 都是@Component
@Configuration 相比其他多了一个变量
@Bean是一个方法级别上的注解,主要用在@Configuration注解的类里,也可以用于标注其他注解。添加的bean的id为默认为方法名
对象创建的过程
https://www.jianshu.com/p/8672a72538a7
分配内存:指针碰撞 或者 空闲链表
反编译字节码-new对象的过程
分配一个对象过程:
1:在常量池中定位符号引用→2:没有加载的先加载→3:为新生对象分配内存→4:(将分配到的内存空间都初始化为零值→对对象进行必要的设置→执行<init>方法把对象进行初始化)→ 5:设置instance指向刚分配的内存地址
第四部中的三个环节,可以认为都是对象的初始化
步骤4和步骤5 可能被指令重排序!!!
CheckException(受检) 和 UnCheckException(不受检:运行时异常)
https://blog.csdn.net/nlznlz/article/details/53271045
除了运行时异常外都是受检异常,运行时异常是不受检的
受检的意思就是:我们在写代码阶段,就必须捕获或者抛出的异常
不受检的就是,我们在写代码的时候可以不处理非受检的异常,但是代码运行期间,一旦非受检的异常,抛出了,程序就崩溃了,比如 3/0
redis常用数据结构的底层实现妙不可言
String:SDS 简单动态字符串(simple dynamic string,SDS)
struct
sdshdr{
//记录buf数组中已使用字节的数量
//等于 SDS 保存字符串的长度
int
len;
//记录 buf 数组中未使用字节的数量
int
free``;
//字节数组,用于保存字符串
char
buf[];
}
list:双向链表
typedef struct listNode{
struct listNode *prev;
struct listNode *next;
void *value;
}listNode
通过多个 listNode 结构就可以组成链表,这是一个双端链表,Redis还提供了操作链表的数据结构:
typedef struct list{
listNode *head;
listNode *tail;
//链表所包含的节点数量
unsigned long len;
//节点值复制函数
void (*free``) (``void *ptr);
//节点值释放函数
void (*free``) (``void *ptr);
//节点值对比函数
int (*match) (``void *ptr,``void *key);
}list;
set
hash:数组+链表
跟 java 里面的hashMap 差不多,
先hash 取模 定位,然后挂链,不过redis链表不会变成红黑树,
达到一定阈值之后,也会扩容,不过不是原来容量的两倍,而是已存节点数量的两倍,比如数组大小为10,存了20个节点,则扩容至40
hash:用的底层是 hashTable
字典的定义:一个entry数组的首指针,一些数组大小,已经存了多少节点等变量
typedef struct dictht{
//哈希表数组,Entry数组
dictEntry **table;
//哈希表大小
unsigned long size;
//哈希表大小掩码,用于计算索引值,总是等于 size-1
unsigned long sizemask;
//该哈希表已有节点的数量
unsigned long used;
} dictht;定义Entry:一个key 一个 value 一个指向下一个节点的指针,用于挂链
typedef
struct
dictEntry{
//键
void
*key;
//值
union{
void
*val;
uint64_tu64;
int64_ts64;
}v;
//指向下一个哈希表节点,形成链表
struct
dictEntry *next;
} dictEntry;
set
如果集合里都是数字,不重复,并且数量小于512(默认),则用整数集合实现,整数集合能保证有序性!
如果有重复,或者有非数字类型的元素加进来了,就用hashTable;跟java里面hashSet 就是用的hashMap原理差不多
zset
有序集合是由 ziplist (压缩列表) 或 skiplist (跳跃表) 组成的。
当数据比较少时,有序集合使用的是 ziplist 存储的,有序集合使用 ziplist 格式存储必须满足以下两个条件:
- 有序集合保存的元素个数要小于 128 个;
- 有序集合保存的所有元素成员的长度都必须小于 64 字节。
如果不能满足以上两个条件中的任意一个,有序集合将会使用 skiplist 结构进行存储。
链表+索引 == 跳跃表
Redis 过期删除策略(惰性删除+定期删除)
https://www.cnblogs.com/xuliangxing/p/7151812.html
Redis集群的三种模式
https://blog.csdn.net/honger_hua/article/details/99947348
RDB AOF
RDB && AOF https://www.cnblogs.com/itdragon/p/7906481.html
AOF https://blog.csdn.net/luolaifa000/article/details/84178289
布隆过滤器解决缓存穿透
把有效的key,提前加载到布隆过滤器里,布隆过滤器里没有就一定没有,布隆过滤器里有则大概率有!!!
https://zhuanlan.zhihu.com/p/66485901
Epoll Select 原理
select 大概是这样 核心就是 轮询
在用户态 和 核心态 都有一份 fd 的集合,select 检查有没有准备好IO的fd的时候,现将用户态的fd集合传到核心态,核心态去检查,那些准备好了,再将
集合和准备好的个数 传给 用户态,用户发现准备好的个数不是0,但是不知道谁准备好了,所以轮询每一个fd
fd 最多 1024个
epoll 核心是 回调
当网卡把数据写入到内存后,网卡向cpu发出一个中断信号,操作系统便能得知有新数据到来,再通过网卡中断程序去处理数据,通过回调函数
fd 就知道自己就绪了, 就会被放到 就绪链表里
三个系统调用
epoll_create:用来创建 两个数据结构 用来存所有fd的红黑树 和 用来存就绪fd的就绪链表
epoll_ctl 用来 添加 删除 修改 红黑树里面的fd
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
上面那个函数,会去检查就绪链表是否为空,如果不为空,则将就绪fd拷贝给 用户,通过上面那个events指针