1.Spring核心特性?
IoC 控制反转
IoC(控制反转)是一种编程思想,将原本在程序中手动创建对象的控制权,交给IoC容器,由容器统一管理对象间的依赖关系,负责创建对象、完成依赖的注入。
IoC把开发者从复杂的依赖关系中解放出来,无需关心对象的创建过程,大大简化了应用的开发、降低了开发难度,增加了项目的可维护性。
IoC只是一种编程思想,所有实现了IoC的容器都叫做IoC容器,Spring容器只是IoC的其中一种实现,Spring容器 ≠ IoC容器,只是IoC容器中的一种。
IoC容器的优点
避免在各处使用new来创建实例,降低类之间的耦合度,易于维护
由容器自动创建类的实例、注入所需依赖,开发者无需关心对象创建的细节、对象间复杂的依赖关系
IoC常见的实现方式
DI:依赖注入,IoC容器主动给bean注入所需的依赖
DL:依赖查找,bean主动到IoC容器中寻找所需的依赖
DL是EJB时代的方式,现在的主流方式是DI
Spring容器提供的功能
依赖检查、依赖注入
自动装配
可以设置bean的初始化方法、销毁方法、回调方法
AOP 面向切面编程
aop: Aspect-Oriented Programming 面向切面编程,是面向对象编程的一种补充,把和业务逻辑无关的系统服务级的横切逻辑剥离出来,不与业务代码耦合在一起,减少系统中的重复代码,使系统变得高内聚、低耦合,更易维护,常见的应用比如日志记录、事务管理、安全检查、缓存更新等。
Spring底层篇
2.Aop怎么实现的?JDK动态代理具体怎么实现的
Spring AOP 是通过在目标方法执行前、执行后、抛出异常时等切入点执行切面代码的一种机制。其实现原理是使用动态代理技术,在方法运行时动态生成代理对象,然后插入切面代码。当执行目标方法时,由动态代理对象拦截方法并在适当的时间点执行切面代码,然后再调用实际的目标方法。在 Spring 中定义切面,提供一些特定的注解,如 @Before、@After、@Around 等,将切面适切地应用于业务逻辑中。
JDK底层实现原理:使用接口实现方式,运行时,在内存中动态构建出一个类,然后编译,执行。这个类是一次性的,JVM停止,代理类就消失。通过实现 InvocationHandler 接口创建自己的调用处理器;
通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。
3.什么是反射?怎么实现的?里面有哪些关键方法和关键字?
JAVA机制反射是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.
原理主要是通过Class类来实现的。Class类是Java中反射机制的核心类,它可以在运行时动态地获取一个类的信息。Class类的实例对象可以通过三种方式获取:
(1)使用Class.forName()方法获取Class对象。Class.forName()方法接受一个字符串参数,该参数为完整类名,它将返回该类的Class对象。
(2)使用类名.class获取Class对象。例如:String.class。
(3)使用对象.getClass()方法获取Class对象。例如:String str = “hello”,则str.getClass()将返回String类的Class对象。
Java 反射与Java反射的常用方法总结
4.cglib?
CGLIB是一个强大、高性能的字节码生成库。使用CGLib实现动态代理,完全不受代理类必须实现接口的限制,而且CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用Java反射效率要高。需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类。
写的比较好的介绍
5.MySQL索引类型有哪些?主键索引值可以为null吗?为什么不能为null?存储的数据结构是什么样子?
普通索引、唯一索引、主键索引、组合索引、全文索引。各种使用
主键索引字段值不能是null,也不能重复。 只能作用于一个字段(列)。唯一标识,
B+ 树
6.三张联查怎么写?左查询比右查询好?最左匹配原则?
select *from a LEFT JOIN b on a.id=b.a_id
LEFT JOIN c on b.id=c.b_id
左连接where只影向右表,右连接where只影响左表 左联是以左边的表为主,右边的为辅,右联则相反 一般要使得数据库查询语句性能好点遵循一下原则: 在做表与表的连接查询时,大表在前,小表在 不使用表别名,通过字段前缀区分不同表中的字段 查询条件中的限制条件要写在表连接条件前 尽量使用索引的字段做为查询条件
7.多表查询要注意什么?联合索引怎么用?索引失效场景?
1、需要查询的信息在那几张表,
2、表和表之间的对应关系和主副关系,
3、表和表之间的连接条件;SQL多表查询的注意点,以及 join on 、where 执行的顺序
1.联合索引不满足最左匹配原则
2.在联合索引下,尽量使用明确的查询列来趋向于走覆盖索引;
3.索引列参与了运算,会导致全表扫描,索引失效。
4.索引列参与了函数处理,会导致全表扫描,索引失效。
5.模糊查询时(like语句),模糊匹配的占位符位于条件的首部。
6.参数类型与字段类型不匹配,导致类型发生了隐式转换,索引失效。
7.查询条件使用or关键字,其中一个字段没有创建索引,则会导致整个查询语句索引失效;or两边为“>”和“<”范围查询时,索引失效。
8.两列数据做比较,即便两列都创建了索引,索引也会失效。详见
8.优化MySQL?一般你们多少秒算慢sql?
1、选择最合适的字段属性
2、尽量把字段设置为NOT NULL
3、使用连接(JOIN)来代替子查询
4、使用联合(UNION)来代替手动创建的临时表
5、使用索引常见9种典型优化
一般是10-20毫秒
9.线程怎么用的?线程池有哪些?怎么创建的?参数有哪些?注意什么?工作机制?
线程池创建,通过 Executor 框架的工具类 Executors 来实现。
a、newCachedThreadPool
创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。速度最快new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue());
核心线程数为0,最大线程数为max 及2的31次方减一,过期时间60秒,SynchronousQueue同步队列,因为核心线程数为零所以每次的任务会创建一个非核心线程, 优点 速度快 不会产生OOM 只会cpu
b、newFixedThreadPool
创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
核心线程数和最大线程数都是自定义的,过期时间是0毫秒,队列LinkedBlockingQueue 容量是2的31次方减1
c、newScheduledThreadPool
创建一个周期线程池,支持定时及周期性任务执行。
d、newSingleThreadExecutor
创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
跟newFixedThreadPool的参数是一样的,只不过是创建了一个
Executor 和 sumit
corePoolSize: 核心线程数为 5。
maximumPoolSize :最大线程数 10
keepAliveTime : 等待时间为 1L。
unit: 等待时间的单位为 TimeUnit.SECONDS。
workQueue:任务队列为 ArrayBlockingQueue,并且容量为 100;
handler:饱和策略为 CallerRunsPolicy。
线程在有任务的时候会创建核心的线程数corePoolSize
当线程满了(有任务但是线程被使用完)不会立即扩容,而是放到阻塞队列中,当阻塞队列满了之后才会继续创建线程。
如果队列满了,线程数达到最大线程数则会执行拒绝策略。
当线程数大于核心线程数事,超过KeepAliveTime(闲置时间),线程会被回收,最终会保持corePoolSize个线程。
10.线程池解决什么问题?使用线程池需要注意什么问题?
线程池解决的核心问题就是资源管理问题。在并发环境下,系统不能够确定在任意时刻中,有多少任务需要执行,有多少资源需要投入。这种不确定性将带来以下若干问题:
1)频繁申请/销毁资源和调度资源,将带来额外的消耗,可能会非常巨大。
2)对资源无限申请缺少抑制手段,易引发系统资源耗尽的风险。
3)系统无法合理管理内部的资源分布,会降低系统的稳定性。
为解决资源分配这个问题,线程池采用了“池化”(Pooling)思想。池化,顾名思义,就是将资源统一在一起管理的一种思想。
死锁、系统资源不足、并发错误、线程泄漏和任务过载等问题。详细解释
11.JAVA里有哪些锁,悲观锁有哪些?乐观锁有哪些?乐观锁怎么实现的原理?
JAVA里面主要有ReentrantLock ,synchronized,Lock三种,类别也是不一样
synchronized:属于独占锁、悲观锁、可重入锁、非公平锁
ReentrantLock:继承了Lock类,可重入锁、悲观锁、独占锁、互斥锁、同步锁。
Lock:Java中的接口,可重入锁、悲观锁、独占锁、互斥锁、同步锁参考参考
常见的乐观锁实现方式有两种,分别是:1、版本号机制;2、CAS算法。参考
乐观锁是相对悲观锁来说,它认为数据在一般情况下不会造成冲突,别人不会去修改,所以在访问记录前不会加排它锁。但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号,时间戳来等记录。因为不加锁,所以乐观锁在多读的情况下,可以极大的提升我们的吞吐量。
12.java8新特性用过吗?
Lambda 表达式 − Lambda 允许把函数作为一个方法的参数(函数作为参数传递到方法中)。
Stream API −可以让你以一种声明的方式处理数据。
Optional 类 − Optional 类已经成为 Java 8 类库的一部分,用来解决空指针异常。
13.分布式锁用过吗?redis用吗?怎么用?
1、什么是分布式锁
我个人理解的分布式锁,顾名思义就是分布式系统下的锁。在分布式系统中,若数据只有一份,那么在同一时刻,或许会有多个机器访问修改该数据,那么为了保证该数据的一致性和可见性,我们就需要一个锁,当有机器访问该数据时,就把该数据锁住,来提醒别的机器,“我”已经有别的机器获取到了,你们再等会。
2、那么分布式锁,具备什么条件呢?
在分布式系统下,同一个方法在同一时间只能被一台机器上的一个线程执行。具备可重入锁特性(防止死锁)、具备阻塞锁特性、具备公平锁特性。 高性能、高可用的获取和释放锁,不能死锁。
3、利用redis做分布式锁。参考
在redis中,我们可以利用redis 的 setnx()
、expire()
方法做分布式锁。首先 setnx()
方法它有两个参数:setnx(key,value)
,含义就是,如果key不存在,则设置当前key成功,并返回状态码1。如果key存在,则设置当前key失败,并返回状态码0。并且由于该方法是原子性的,所以可以考虑使用该方法。并且配合expire()
方法,来设置key的过期时间。
整体流程如下:1. setnx(key,value)如果返回0,代表key已存在,设置失败;如果返回1,代表key不存在,设置成功。
2. 然后使用expire(key)
,对key设置超时时间,避免死锁问题。
3. 然后线程访问完毕后,调用delete命令删除该key。代表使用完毕,其他线程可以竞争该锁。
14.使用redis需要注意哪些问题啊?知道雪崩吗?(雪崩就算是需要注意的问题)?淘汰策略?
参考
缓存穿透:缓存穿透一般都是这几种情况产生的:
业务不合理的设计,比如大多数用户都没开守护,但是你的每个请求都去缓存,查询某个userid查询有没有守护。
业务/运维/开发失误的操作,比如缓存和数据库的数据都被误删除了。
黑客非法请求攻击,比如黑客故意捏造大量非法请求,以读取不存在的业务数据。
如何避免缓存穿透呢? 一般有三种方法。
如果是非法请求,我们在API入口,对参数进行校验,过滤非法值。
如果查询数据库为空,我们可以给缓存设置个空值,或者默认值。但是如有有写请求进来的话,需要更新缓存哈,以保证缓存一致性,同时,最后给缓存设置适当的过期时间。(业务上比较常用,简单有效)
使用布隆过滤器快速判断数据是否存在。即一个查询请求过来时,先通过布隆过滤器判断值是否存在,存在才继续往下查。
这里是引用:布隆过滤器原理:它由初始值为0的位图数组和N个哈希函数组成。一个对一个key进行N个hash算法获取N个值,在比特数组中将这N个值散列后设定为1,然后查的时候如果特定的这几个位置都为1,那么布隆过滤器判断该key存在。
缓存雪崩指缓存中数据大批量到过期时间,而查询数据量巨大,请求都直接访问数据库,引起数据库压力过大甚至down机。
缓存雪奔一般是由于大量数据同时过期造成的,对于这个原因,可通过均匀设置过期时间解决,即让过期时间相对离散一点。如采用一个较大固定值+一个较小的随机值,5小时+0到1800秒酱紫。
Redis 故障宕机也可能引起缓存雪奔。这就需要构造Redis高可用集群啦。
淘汰策略
淘汰策略8种:volatile-lru,针对设置了过期时间的key,使用lru算法进行淘汰。
volatile-lfu,针对设置了过期时间的key,使用lfu算法进行淘汰。
allkeys-lru,针对所有key使用lru算法进行淘汰。
allkeys-lfu,针对所有key使用lfu算法进行淘汰。
volatile-random,从所有设置了过期时间的key中使用随机淘汰的方式进行淘汰。
allkeys-random,针对所有的key使用随机淘汰机制进行淘汰。
volatile-ttl,删除生存时间最近的一个键。
noeviction(默认),不删除键,值返回错误。
15.什么是分布式事务?@Transational能保证事务一定成功吗?哪些会导致事务失效?
在微服务架构下,服务之间通过RPC远程调用,相对单机事务来说,多了“网络通信”这一不确定因素,使得本来服务的调用只有“成功”和“失败”这两种返回结果,变为“成功”、“失败”和“未知”三种返回结果。系统之间的通信可靠性从单一系统中的可靠变成了微服务架构之间的不可靠,分布式事务其实就是在不可靠的通信下实现事务的特性。一般因为网络导致的异常可能有机器宕机、网络异常、消息丢失、消息乱序、数据错误、不可靠的TCP、存储数据丢失、其他异常等等。分布式事务
1.访问权限为private,这样最终会导致事务失效,spring要求被代理方法必须是public的。
2. 方法用final修饰;某个方法是static的,同样无法通过动态代理,变成事务方法。
3. 同一个类中的方法直接内部调用,会导致事务失效。
4.未被spring容器管理.(spring事务的前提是:对象要被spring管理,@Controller、@Service、@Component、@Repository等注解。)