spring从2004年开始诞生,spring1.0基于xml配置bean的形式,而发展到spring4.x开始支持全注解的形式配置bean,当前普遍使用的是spring5.x的版本;今年有spring6.x的版本准备发布中,spring中常用的注解有:
@Service,@Repository,@Configurable,@Bean,@ComponentScan,@RequestParam, @RequestBody,@RestController=@Controller+@ResponseBody,
@PostMapping=@RequestMapping(method = {RequestMethod.POST}),
@GetMapping=@RequestMapping( method = {RequestMethod.GET} ),
@Autowired+@Qualifier=@Resource 其中@Resource这个注解不是spring提供的,
@Resource注解优先通过byName方式查找@Autowired是通过byType的方式,如果类型一样再加上@Qualifier来限定名字
@Conditional 条件注解,符合条件的才会被加载 @Component包含@Indexed,其中Indexed为5版本提供的,为了加快容器启动ComponentScan扫描文件的速度,将需要扫描的文件在编译阶段都读到一个文件里,容器启动的时候就只从一个文件里读取就可以了。
BeanFactory提供基本的IOC和DI功能,而ApplicationContext提供高级功能,BeanFactory可用于测试和非生产使用,但ApplicationContext是功能更丰富的容器实现,应该优于BeanFactory
相同:
不同:
在使用Spring框架的时候,可以有两种事务的实现方式,一种是编程式事务,自己控制事务开启提交,回滚操作;还有一种是用@Transactional注解来进行实现
当一个方法添加@Transactional注解之后,spring会基于这个类生成一个代理对象,会将这个代理对象作为bean,当使用这个代理对象的方法的时候,把事务的自动提交给关闭,等具体的业务逻辑处理完毕,没有出现异常,事务会直接提交,如果出现异常情况,那么直接进行回滚操作,当然用户可以控制对哪些异常进行回滚操作。
ConfigurableApplicationContext 的run 方法里的prepareContext是自动装配的入口里面有load方法调用BeanDefinitionLoader类里的load()方法,然后通过判断启动类上是否有Component注解,然后完成启动类的注册(AnnotatedBeanDefinitionReader.register(source)),再接着run 方法里的refreshContext(context)里的refresh方法调用invokeBeanFactoryPostProcessors(beanFactory)完成自动装配
判断
mysql的索引类型跟存储引擎是相关的,区分聚簇索引和非聚簇索引非常简单,只要判断数据跟索引是否存储在一个文件就可以了。
innodb存储引擎在进行数据插入的时候,数据必须要跟索引放在一起,如果有主键就使用主键,没有主键就使用唯一键,没有唯一键就使用6字节的rowid,因此跟数据绑定在一起的就是聚簇索引,而为了避免数据冗余存储,其他的索引的叶子节点中存储的都是聚簇索引的key值,因此innodb中既有聚簇索引也有非聚簇索引,而myisam中只有非聚簇索引。
索引的数据结构和具体存储引擎的实现有关,mysql中使用较多的索引有hash索引,B+树索引,innodb的索引实现为B+树,memory存储引擎为hash索引。
B+树是一个平衡的多叉树,从根节点到每个叶子节点的高度差值不超过1,而且同层级的二节点间有指针相关连接,在B+树上的常规检索,从根节点到叶子节点的搜索效率基本相当,不会出现大幅波动,而且基于索引的顺序扫描时,也可以利用双向指针快速左右移动,效率非常高。
哈希索引就是采用一定的哈希算法,只需一次哈希算法即可立刻定位到相应的位置,速度非常快。
如果是等值查询,那么哈希索引明显有绝对优势,而B+树适合范围查找,顺序查找排序,以及like这样的部分模糊查询,联合索引的最左匹配规则
在进行索引设计的时候,应该保证索引字段占用的空间越小越好,这只是一个大的方向,还有一些细节点需要注意下:
1、适合索引的列是出现在where字句中的列,或者连接子句中指定的列
2、基数较小的表,索引效果差,没必要创建索引
3、在选择索引列的时候,越短越好,可以指定某些列的一部分,没必要用全部字段的值
4、不要给表中的每一个字段都创建索引,并不是索引越多越好
5、定义有外键的数据列一定要创建索引
6、更新频繁的字段不要有索引
7、创建索引的列不要过多,可以创建组合索引,但是组合索引的列的个数不建议太多
8、大文本、大对象不要创建索引
基于锁的属性分类:共享锁、排他锁。
基于锁的粒度分类:行级锁(innodb )、表级锁( innodb 、myisam)、页级锁( innodb引擎)、记录锁、间隙锁、临键锁。
基于锁的状态分类:意向共享锁、意向排它锁。
共享锁(share lock): 共享锁又称读锁,简称 S 锁;当一个事务为数据加上读锁之后,其他事务只能对该数据加读锁,而不能对数据加写锁,直到所有的读锁释放之后其他事务才能对其进行加持写锁。共享锁的特性主要是为了支持并发的读取数据,读取数据的时候不支持修改,避免出现重复读的问题。
排他锁(exclusive lock):排他锁又称写锁,简称 X 锁;当一个事务为数据加上写锁时,其他请求将不能再为数据加任何锁,直到该锁释放之后,其他事务才能对数据进行加锁。排他锁的目的是在数据修改时候,不允许其他人同时修改,也不允许其他人读取,避免了出现脏数据和脏读的问题。
表锁(table lock):表锁是指上锁的时候锁住的是整个表,当下一个事务访问该表的时候,必须等前一个事务释放了锁才能进行对表进行访问;特点:粒度大,加锁简单,容易冲突;
行锁:行锁是指上锁的时候锁住的是表的某一行或多行记录,其他事务访问同一张表时,只有被锁住的记录不能访问,其他的记录可正常访问,特点:粒度小,加锁比表锁麻烦,不容易冲突,相比表锁支持的并发要高
记录锁(Record lock):记录锁也属于行锁中的一种,只不过记录锁的范围只是表中的某一条记录,记录锁是说事务在加锁后锁住的只是表的某一条记录,加了记录锁之后数据可以避免数据在查询的时候被修改的重复读问题,也避免了在修改的事务未提交前被其他事务读取的脏读问题
页锁:页级锁是 MysQL 中锁定粒度介于行级锁和表级锁中间的一种锁.表级锁速度快,但冲突多,行级冲突少,但速度慢。所以取了折衷的页级,一次锁定相邻的一组记录。特点:开销和加锁时间界于表锁和行锁之间,会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。
间隙锁:是属于行锁的一种,间隙锁是在事务加锁后其锁住的是表记录的某一个区间,当表的相邻ID之间出现空隙则会形成一个区间,遵循左开右闭原则。范围查询并且查询未命中记录,查询条件必须命中索引、间隙锁只会出现在REPEATABLE_READ(重复读)的事务级别中。
临键锁(Next-Key lock):也属于行锁的一种,并且它是INNODB的行锁默认算法,总结来说它就是记录锁和间隙锁的组合,变成左闭右闭。临键锁会把查询出来的记录锁住,同时也会把该范围查询内的所有间隙空间也会锁住,再之它会把相邻的下一个区间也会锁住。
数据库并发场景有三种,分别为:
1、读读:不存在任何问题,也不需要并发控制
2、读写:有线程安全问题,可能会造成事务隔离性问题,可能遇到脏读、幻读、不可重复读
3、写写:有线程安全问题,可能存在更新丢失问题
MVCC是一种用来解决读写冲突的无锁并发控制,也就是为事务分配单项增长的时间戳,为每个修改保存一个版本,版本与事务时间戳关联,读操作只读该事务开始前的数据库的快照,所以MVCC可以为数据库解决一下问题:
1、在并发读写数据库时,可以做到在读操作时不用阻塞写操作,写操作也不用阻塞读操作,提高了数据库并发读写的性能
2、解决脏读、幻读、不可重复读等事务隔离问题,但是不能解决更新丢失问题,那么更新丢失问题可以通过乐观锁解决,比如更新的时候加上记录版本号的比较
mvcc的实现原理主要依赖于记录中的三个隐藏字段(DB_TRX_ID、DB_ROLL_PTR、DB_ROW_ID),undolog,read view来实现的。
注意:在RC隔离级别下,是每个快照读都会生成并获取最新的Read View,而在RR隔离级别下,则是同一个事务中的第一个快照读才会创建Read View,之后的快照读获取的都是同一个Read View.
InnoDB存储引擎:
MyISAM存储引擎:
注意:索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果非聚簇索引很多,一旦聚簇索引改变,那么所有非聚簇索引都会跟着变。
1、高并发中集合有哪些问题?
第一代线程安全的集合:Vector、Hashtable等,使用synchronized修饰方法*
第二代线程非安全的集合:ArrayList、HashMap等,如果需要线程安全使用 Collections.synchronizedList(list); Collections.synchronizedMap(m);
第三代线程安全的集合 java.util.concurrent.* 这个包下的集合
ConcurrentHashMap、CopyOnWriteArrayList 、CopyOnWriteArraySet等底层大都采用Lock锁(1.8的ConcurrentHashMap使用分段锁),保证安全的同时,性能也很高。
1、lambda表达式本质:函数式接口的匿名子类的匿名对象 ,使用ASM技术动态的生成接口实现类,并且调用唯一的抽象方法。
2、@FunctionalInterface是JDK8中新增加的一个函数式注解,表示该注解修饰的接口只能有一个抽象方法。
3、lambda表达式使用前提:
方法的参数或局部变量类型必须为接口才能使用Lambda
接口中有且仅有一个抽象方法(@FunctionalInterface)
4、在lambda表达式可以省略写法:
小括号内的参数类型可以省略
如果小括号内有且仅有一个参数,则小括号可以省略
如果大括号内有且仅有一个语句,可以同时省略大括号,return 关键字及语句分号
5、为什么会有lambda 表达式:为了简化函数式接口的使用--》简化匿名类的方法
6、当lambda 表达式所要完成的逻辑已经存在了,就是已经有函数实现了,那么就可以直接引用对应的函数(方法),即方法引用;普通的方法引用格式: 对象::方法
7、方法引用和lambda的本质一样,都是通过ASM技术动态生成一个内部匿名类,不过方法引用直接使用了已有的方法,而lambda 把方法体另外生产了一个匿名方法。
8、哪些方法可以引用:
类方法(类名::静态方法)、构造方法(类名::new)、成员方法(对象::方法名);
被引用的方法与函数式接口的抽象方法必须满足:参数列表相同、返回值类型兼容
9、stream 流式编程,基于lambda 表达式发展起来的。大大发挥了多处理器下的计算性能
10、stream的特点:
专注于对容器对象的聚合操作
提供串行/并行两种模式,其中并行是使用了fork/jion框架拆分任务再合并任务
提高编程效率、可读性
打开流--》中间操作---〉最终操作
1、string、 string buffer、 string builder 区别:
string是用final 修饰,值不可变的;string buffer、 string builder也用了final修饰,但是可以扩容,他们都继续了AbstractStringBuilder类,里面有append方法,stringbuffer 是1.0版本里的synchronized修饰的append方法,线程安全,而string builder是1.5出现,线程不安全,效率高,可扩容的原理是通过数组实现的。
2、怎样生命一个类不能被继承,什么场景使用?
用final修饰的类就不可以被继承,比如Math类,数学方法就是一个明确的公式,没有必要进行改写了,所以也不需要被继承。并且内部构造函数是私有的,内部的方法都是static修饰的方法。直接类名.方法名就可以使用了。
3、Throwable类、error和exception 都继承自Throwable类,如果我们自定义异常类可以继承这些类实现自定义异常。
4、equals 和hashcode :都是object 类的方法,equals的判断默认是this==obj ,所以需要重写,因为我们判断两个对象是否相等是希望内容相等,而不是一定要对象引用相等;所以需要重写equals方法,那么也必须将hashcode重写,因为要保证 对象相等了,hashcode也必须相等;
5、==和equals 区别:==是判断基本类型的相等,是数值的判断;而equals一般判断对象引用相等,默认是对象引用地址是否相等;如String 类就重写了equals和hashcode,都将内容参与判断是否相等。
6、jdk1.8的新特性:lambda表达式、函数式接口、方法引用和构造函器调用、stream API、接口中默认方法和静态方法、新时间日期API