面试题(图灵周瑜)

java基础面试题

面向对象

什么是面向对象?

对比面向过程,是两种不同的处理问题的角度

面向过程更注重事情的每一个步骤及顺序,面向对象更注重事情有哪些参与者(对象)、及各自需要做

什么

比如:洗衣机洗衣服

面向过程会将任务拆解成一系列的步骤(函数),1、打开洗衣机----->2、放衣服----->3、放洗衣粉-----

>4、清洗----->5、烘干

面向对象会拆出人和洗衣机两个对象:

人:打开洗衣机 放衣服 放洗衣粉

洗衣机:清洗 烘干

面向对象有封装、继承、多态的特性,所以易维护、易复用、易扩展。可以设计出低耦合的系统。

JDK JRE JVM

JDK:包含JRE

Java Develpment Kit java 开发工具,它提供了编译、运行java程序所需的各种工具和资源。

JRE:包含JVM

Java Runtime Environment java运行时环境,用于运行java字节码文件。

JVM:

java Virtual Machine java 虚拟机,是JRE的一部分,它是java实现跨平台的最核心的部分,负责运行字节码文件。

==和equals比较

==

如果是基本数据类型,比较的是值。如果是引用类型,比较的是引用地址。

equals

具体看各个类重写equals方法之后的比较逻辑,比如String类,虽然是引用类型,但是String类中重写了equals方法,方法内部比较的是字符串中的各个字符是否全部相等。

只能适用于引用数据类型
Object类中equals()和==的作用相同
像String、Data、File、包装类等中的equals都是重写了Object类中的equals()方法,比较的是实体内容是否相同

String、StringBuffer、StringBuilder

String是final修饰的,不可变,每次操作都会产生新的String对象。

StringBuffer和StringBuilder都是不变(在原对象上操作),StringBuffer是线程安全的,StringBuilder线程不安全的,因为StringBuffer方法加了synchronized修饰的。

性能:StringBuilder > StringBuffer > String

场景:经常需要改变字符串内容时使用后面两个

优先使用StringBuilder,多线程使用共享变量时使用StringBuffer

泛型中extends和super的区别

1、<? exteds T> 表示包括 T 包括在的任何 T 的子类

2、<? super T>表示包括 T 包括在的任何 T 的父类

重载和重写的区别

重载

发生在同一个类中,方法名必须相同,参数类型不同、个数不同、顺序不同,方法返回值和访问修饰符可以不同,发生在编译时。

public int add(int a,String b)
public String add(int a,String b)
  //编译报错,重写跟返回值是没有关系的
重写

发生在父子类中,方法名、参数列表必须相同,返回值范围小于等于父类,抛出的异常范围小于

等于父类,访问修饰符范围大于等于父类;如果父类方法访问修饰符为private则子类就不能重写该方

法。

list和set的区别

List

有序,按对象进入的顺序保存对象,可重复。允许多个Null元素对象。

可以使用Iterator取出所有元素,在逐一遍历,还可以使用==get(int index)==获取指定下标的元素

Set

无序,不可重复,最多允许有一个Null元素对象。

取元素时只能用Iterator接口取得所有元素,再逐一遍历各个元素。

ArrayList和LinkedList区别

ArrayList:基于动态数组,连续内存存储,适合下标访问(随机访问),扩容机制:因为数组长度固
定,超出长度存数据时需要新建数组,然后将老数组的数据拷贝到新数组,如果不是尾部插入数据还会
涉及到元素的移动(往后复制一份,插入新元素),使用尾插法并指定初始容量可以极大提升性能、甚
至超过linkedList(需要创建大量的node对象)
-----------
LinkedList:基于链表,可以存储在分散的内存中,适合做数据插入及删除操作,不适合查询:需要逐
一遍历
遍历LinkedList必须使用iterator不能使用for循环,因为每次for循环体内通过get(i)取得某一元素时都需
要对list重新进行遍历,性能消耗极大。
另外不要试图使用indexOf等返回元素索引,并利用其进行遍历,使用indexlOf对list进行了遍历,当结
果为空时会遍历整个列表。

1、首先,他们底层数据结构不同,ArrayList底层是基于数组实现的,LinkedList底层是基础链表实现的

2、由于底层数据结构不同,他们所适用的场景也不同,ArrayList更适合下标访问(随机查找),LinkedList更适用于数据的插入跟删除操作,时间复杂度不同

3、另外ArrayList和LinkedList都实现了List接口,但是LinkedList还额外实现了Deque接口,所以LinkedList还可以当做队列来使用

引用拷贝、浅拷贝、深拷贝

  • 引用拷贝只是复制对象的地址,并不会创建一个新的对象

  • 浅拷贝则会创建一个对象,并进行属性复制,不过对引用类型的属性,只会复制其对象地址

  • 深拷贝则是完全复制整个对象,包括引用类型的属性。

深拷贝和浅拷贝的区别?

浅拷贝(shallowCopy)只是增加了一个指针指向原有的内存地址。

深拷贝(deepCopy)是增加了一个指针并且申请了一个新的内存,使这个增加的指针指向这个新的内存。

使用深拷贝的情况下,我们可以通过 clone() 方法,在实际开发中不建议使用,它有抛出异常的风险。

如果真想让对象提供拷贝功能,可以自己编写其他方法来实现。

通过对象序列化实现深拷贝

将对象序列化为字节序列后,默认会将该对象的整个对象图进行序列化,再通过反序列即可完美地实现深拷贝。

什么是字节码?采用字节码的好处是什么?

Java中引入了虚拟机的概念, 虚拟机在任何平台上都提供了一个接口给编译程序,编译程序只需要面向虚拟机,生成虚拟机理解的代码,然后解释器将其转化为特定系统的机器代码执行,而这种供虚拟机理解的就是字节码 , 即编译后生成的字节码文件 (.class文件)。

好处

采用字节码的好处,一方面实现了跨平台,,另外一方面也提高了代码执行的性能,编译器在编译源代码时可以做一些编译期的优化,比如锁消除、标量替换、方法内联等。

Java语言通过字节码的方式,在一定程度上解决了传统解释型语言执行效率低的问题,同时又保留了解

释型语言可移植的特点。所以Java程序运行时比较高效,而且,由于字节码并不专对一种特定的机器,

因此,Java程序无须重新编译便可在多种不同的计算机上运行。

# Java类加载器

JDK自带有三个类加载器:bootstrap ClassLoader、ExtClassLoader、AppClassLoader。

BootStrapClassLoader是ExtClassLoader的父类加载器,默认负责加载%JAVA_HOME%lib下的jar包和class文件。

ExtClassLoader是AppClassLoade r的父类加载器,负责加载%JAVA_HOME%/lib/ext文件夹下的jar包和class类。

AppClassLoader是自定义类加载器的父类,负责加载classpath下的类文件。系统类加载器,线程上下文加载器

继承ClassLoader实现自定义类加载器

JVM中哪些是线程共享区

堆区和方法区是所有线程共享的,栈、本地方法栈、程序计数器是每个线程独有的

线程、并发相关

如何让线程顺序执行

1、使用线程的join方法

2、使用线程的wait方法

3、线程的 CountDownLatch(倒计数) ———细粒度高

4、等八种方法……一般说三种就够了

选择使用什么样的方式,取决于你的需求,如果你只是想顺序的执行那join和线程池都可以使用,因为这2种方式比较简单。如果需求是细粒度的,比如线程1执行到某个部分,线程2就得执行,就使用CountDownLatch。但是使用它时,一定要理清逻辑,不然可能await方法会阻塞

高并发如何保证数据的一致性

一:给缓存设置过期时间,是保证最终一致性的终极解决方案。

这种方案下,我们可以对存入缓存的数据设置过期时间,所有的写操作以数据库为准,对缓存操作只是尽最大努即可。也就是说如果数据库写成功,缓存更新失败,那么只要到达过期时间,则后面的读请求自然会从数据库中读新值然后回填缓存。因此,接下来讨论的思路不依赖于给缓存设置过期时间这个方案。

二:采用延时双删策略

    public void use(String key,Object data){

        redis.delKey(key);

        db.updateData(data);

        Thread.sleep(800);
        
        redis.delKey(key);
    }

ThreadLocal内存泄露原因,如何避免

ThreadLocal底层原理
  1. ThreadLocal 是 java 中所提供的线程本地存储机制,可以利用该机制将数据缓存在某个线程内部,该线程可以在任意时刻、任意方法中获取缓存的数据。
  2. ThreadLocal 底层是通过 ThreadLocal来实现的,每个Thread对象(注意不是 ThreadLocal对象)中存在一个 ThreadMap,Map的 key为 ThreadLocal对象,Map的 value为需要缓存的值
  3. 如果线程池中使用 ThreadLocal 会造成内存泄漏,因为当 ThreadLocal对象使用完之后,应该把设置的 key和value,也就是 Entry对象进行回收,但线程池中的线程不会回收,而线程对象是通过强引用指向ThreadLocalMap,ThreadLocalMap 也是通过强引用指向Entry对象,线程不被回收,Entry对象也就不会被回收,从而出现内存泄漏,解决办法就是,在使用完ThreadLocal对象之后,手动调用ThreadLocal 的remove方法,手动清除entry对象
  4. ThreadLocal 经典的应用场景就是连接管理(一个先后才能持有一个连接,该连接对象可以在不同的方法之间进行传递,线程之间不共享一个连接)。
ThreadLocal内存泄露原因

内存泄露为程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光,

不再会被使用的对象或者变量占用的内存不能被回收,就是内存泄露。

ThreadLocal正确的使用方法
  • 每次使用完ThreadLocal都调用它的remove()方法清除数据

  • 将ThreadLocal变量定义成private static,这样就一直存在ThreadLocal的强引用,也就能保证任何时候都能通过ThreadLocal的弱引用访问到Entry的value值,进而清除掉 。

并发、并行、串行的区别

串行在时间上不可能发生重叠,前一个任务没搞定,下一个任务就只能等着

并行在时间上是重叠的,两个任务在同一时刻互不干扰的同时执行。

并发允许两个任务彼此干扰。统一时间点、只有一个任务运行,交替执行

锁的类型有哪些?

基于锁的属性分类:共享锁、排他锁

基于锁的粒度分类:行级锁(INNODB)、表级锁(INNODB、MYISAM)、页级锁(BDB引擎 )、记录锁、间隙锁、临键锁

基于锁的状态分类:意向共享锁、意向排它锁

共享锁(Share Lock)
共享锁又称读锁,简称S锁;当一个事务为数据加上读锁之后,其他事务只能对该数据加读锁,而不能对 数据加写锁,直到所有的读锁释放之后其他事务才能对其进行加持写锁。共享锁的特性主要是为了支持 并发的读取数据,读取数据的时候不支持修改,避免出现重复读的问题。
排他锁(eXclusive Lock)
排他锁又称写锁,简称X锁;当一个事务为数据加上写锁时,其他请求将不能再为数据加任何锁,直到该 锁释放之后,其他事务才能对数据进行加锁。排他锁的目的是在数据修改时候,不允许其他人同时修 改,也不允许其他人读取。避免了出现脏数据和脏读的问题。
表锁
表锁是指上锁的时候锁住的是整个表,当下一个事务访问该表的时候,必须等前一个事务释放了锁才能 进行对表进行访问; 
特点: 粒度大,加锁简单,容易冲突;
行锁
行锁是指上锁的时候锁住的是表的某一行或多行记录,其他事务访问同一张表时,只有被锁住的记录不 能访问,其他的记录可正常访问。
特点:粒度小,加锁比表锁麻烦,不容易冲突,相比表锁支持的并发要高;
记录锁(Record Lock)
记录锁也属于行锁中的一种,只不过记录锁的范围只是表中的某一条记录,记录锁是说事务在加锁后锁 住的只是表的某一条记录。
精准条件命中,并且命中的条件字段是唯一索引 
加了记录锁之后数据可以避免数据在查询的时候被修改的重复读问题,也避免了在修改的事务未提交前 被其他事务读取的脏读问题。	
间隙锁(Gap Lock)
属于行锁中的一种,间隙锁是在事务加锁后其锁住的是表记录的某一个区间,当表的相邻ID之间出现空 隙则会形成一个区间,遵循左开右闭原则。 
范围查询并且查询未命中记录,查询条件必须命中索引、间隙锁只会出现在REPEATABLE_READ(重复 读)的事务级别中。
触发条件:防止幻读问题,事务并发的时候,如果没有间隙锁,就会发生如下图的问题,在同一个事务 里,A事务的两次查询出的结果会不一样。
比如表里面的数据ID 为 1,4,5,7,10 ,那么会形成以下几个间隙区间,-n-1区间,1-4区间,7-10 区间,10-n区间 (-n代表负无穷大,n代表正无穷大)
临建锁(Next-Key Lock)
也属于行锁的一种,并且它是INNODB的行锁默认算法,总结来说它就是记录锁和间隙锁的组合,临键锁。 
会把查询出来的记录锁住,同时也会把该范围查询内的所有间隙空间也会锁住,再之它会把相邻的下一 个区间也会锁住。 
触发条件:范围查询并命中,查询命中了索引。
结合记录锁和间隙锁的特性,临键锁避免了在范围查询时出现脏读、重复读、幻读问题。加了临键锁之 后,在范围区间内数据不允许被修改和插入。

如果当事务A加锁成功之后就设置一个状态告诉后面的人,已经有人对表里的行加了一个排他锁了,你们不能对整个表加共享锁或排它锁了,那么后面需要对整个表加锁的人只需要获取这个状态就知道自己是不是可以对表加锁,避免了对整个索引树的每个节点扫描是否加锁,而这个状态就是意向锁。

意向共享锁
当一个事务试图对整个表进行加共享锁之前,首先需要获得这个表的意向共享锁。
意向排他锁
当一个事务试图对整个表进行加排它锁之前,首先需要获得这个表的意向排它锁。

Java死锁如何避免?

造成死锁的几个原因:
  1. 一个资源每次只能被一个线程使用
  2. 一个线程在阻塞等待某个资源时,不释放已占有资源
  3. 一个线程已经获得的资源,在未使用完之前,不能被强行剥夺
  4. 若干个线程形成头尾相接的 循环等待资源关系

这个是造成死锁必须要达到的4个条件,如果要避免死锁,只要不满足其中某一个条件即可。而其中前3个条件是作为锁要符合的条件,所以要避免死锁就需要打破第4个条件,不出现循环等待锁的关系。

在开发过程中:
  1. 要注意加锁顺序,保证每个线程按同样的顺序进行加锁
  2. 要注意加锁时限,可以针对所设置一个超时时间
  3. 要注意死锁检查,这是一种预防机制,确保在第一时间发现死锁并进行解决

Sychronized和ReentrantLock的区别

  1. sychronized是一个关键字,ReentrantLock是一个类
  2. sychronized会自动的加锁与释放锁,ReentrantLock需要程序员手动加锁与释放锁
  3. sychronized的底层是JVM层面的锁, ReentrantLock是API层面的锁
  4. sychronized是非公平锁,ReentrantLock可以选择公平锁或非公平锁
  5. sychronized锁的是对象,锁信息保存在对象头中, ReentrantLock通过代码中int类型的state标识来标识锁的状态
  6. sychronized底层有一个锁升级的过程

MYSQL

什么是索引?什么场景使用?

索引是对数据库表中一列或多列的值进行排序的一种结构,使用索引可快速访问数据库表中的特定信息

使用索引目的是加快检索表中数据

使用场景:

  • 中到大数据量表适合使用索引
  • 小数据量表,大部分情况全表扫描效率更高
  • 特大数据量表,建立和使用索引的代价会随之增大,适合使用分区或分库

索引的种类有哪些?

  • 普通索引:最基本的索引,没有任何约束限制。
  • 唯一索引:和普通索引类似,但是具有唯一性约束,可以有 null
  • 主键索引:特殊的唯一索引,不允许有 null,一张表最多一个主键索引
  • 组合索引:多列值组成一个索引,用于组合搜索,效率大于索引合并
  • 全文索引:对文本的内容进行分词、搜索
  • 覆盖索引:查询列要被所建的索引覆盖,不必读取数据行(这个不是很了解)

char与varchar的区别

  • char 是一种固定长度的字符串类型
  • varchar 是一种可变长度的字符串类型

MySQL中有哪些时间字段?

占用空间

  • DATETIME:8 bytes
  • TIMESTAMP:4 bytes
  • DATE:4 bytes
  • TIME:3 bytes
  • YEAR:1 byte

日期格式

  • DATETIME:YYYY-MM-DD HH:MM:SS
  • TIMESTAMP:YYYY-MM-DD HH:MM:SS
  • DATE:YYYY-MM-DD
  • TIME:HH:MM:SS
  • YEAR:YYYY

事务的四大特性

事务具备ACID四种特性,ACID是Atomic(原子性)、Consistency(一致性)、Isolation(隔离性)和Durability(持久性)的英文缩写。

原子性(Atomicity)

事务最基本的操作单元,要么全部成功,要么全部失败,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚到事务开始前的状态,就像这个事务从来没有执行过一样。

一致性(Consistency)

事务的一致性指的是在一个事务执行之前和执行之后数据库都必须处于一致性状态。如果事务成功地完成,那么系统中所有变化将正确地应用,系统处于有效状态。如果在事务中出现错误,那么系统中的所有变化将自动地回滚,系统返回到原始状态。

隔离性(Isolation)

指的是在并发环境中,当不同的事务同时操纵相同的数据时,每个事务都有各自的完整数据空间。由并发事务所做的修改必须与任何其他并发事务所做的修改隔离。事务查看数据更新时,数据所处的状态要么是另一事务修改它之前的状态,要么是另一事务修改它之后的状态,事务不会查看到中间状态的数据。

持久性(Durability)

指的是只要事务成功结束,它对数据库所做的更新就必须永久保存下来。即使发生系统崩溃,重新启动数据库系统后,数据库还能恢复到事务成功结束时的状态。

脏读、幻读、不可重复读指什么?

  • 脏读:一个事务读取另外一个事务还没有提交的数据。

  • **不可重复读:**一个事务内,两次相同条件的查询返回了不同的结果。

  • **幻读:**同一个事务中,一条数据出现在这次查询的结果集里,却没有出现在之前的查询结果集中。例如,在一个事务中进行了同一个查询运行了两次,期间被另外一个事务提交插入一行或修改查询条件匹配的一行。它比不可重复读更难防范,因为锁定第一个查询结果集的所有行并不能阻止导致幻象出现的更改。

数据库的三范式是什么?有什么作用?

  • 列不可分,确保表的每一列都是不可分割的原子数据项。作用:方便字段的维护、查询效率高、易于统计。
  • 属性字段完全依赖(完全依赖指不能存在仅依赖主键的部分属性)于主键。作用:保证每行数据都是按主键划分的独立数据。
  • 任何非主属性字段不依赖于其它非主属性字段。作用:减少表字段与数据存储,让相互依赖的非主键字段单独成为一张关系表,记录被依赖字段即可。

三大范式只是一般设计数据库的基本理念,可以设计冗余较小、存储查询效率高的表结构。

但不能一味的去追求数据库设计范式,数据库设计应多关注需求和性能,重要程度:需求 - 性能 - 表结构。比如有时候添加一个冗余的字段可以大大提高查询性能。

左连接、右连接、内连接和全外连接的区别

  • 左连接(left join):返回包括左表中的所有记录和右表中连接字段相等的记录。
  • 右连接(right join):返回包括右表中的所有记录和左表中连接字段相等的记录。
  • 内连接(inner join):只返回两个表中连接字段相等的记录。
  • 全外连接(full join):返回左右表中连接字段相等的记录和剩余所有记录。

事务有哪些隔离级别?

  • 读未提交(Read Uncommitted):是最低的事务隔离级别,它允许另外一个事务可以看到这个事务未提交的数据。会出现脏读,幻读,不可重复读,所有并发问题都可能遇到。
  • 读已提交(Read Committed):保证一个事物提交后才能被另外一个事务读取。另外一个事务不能读取该事物未提交的数据。不会出现脏读现象,但是会出现幻读,不可重复读。Oracle等多数数据库默认都是该级别。
  • 可重复读(Repeatable Read):这种事务隔离级别可以防止脏读,不可重复读,但是可能会出现幻象读。它除了保证一个事务不能被另外一个事务读取未提交的数据之外还避免了不可重复读。
  • 串行化(Serializable):这是花费最高代价但最可靠的事务隔离级别。事务被处理为顺序执行。防止脏读、不可重复读、幻象读。

索引查询失效的几个情况

1、like 以%开头,索引无效;当like前缀没有%,后缀有%时,索引有效。

2、or语句前后没有同时使用索引

3、组合索引,不是使用第一列索引,索引失效。

4、如果列类型是字符串,那一定要在条件中将数据使用引号引用起来,否则不使用索引

5、在索引列上使用 IS NULL 或 IS NOT NULL操作。

总结:在索引列上使用 IS NULL 或 IS NOT NULL操作,索引不一定失效!!!

mybatis

#{}和${}的区别是什么?

#{}是预编译处理、是占位符, ${}是字符串替换、是拼接符。

Mybatis 在处理#{}时,会将 sql 中的#{}替换为?号,调用 PreparedStatement 来赋值;

Mybatis 在处理 时,就是把 {}时, 就是把 时,就是把{}替换成变量的值,调用 Statement 来赋值;

#{} 的变量替换是在DBMS 中、变量替换后,#{} 对应的变量自动加上单引号

的变量替换是在 D B M S 外、变量替换后, {} 的变量替换是在 DBMS 外、变量替换后, 的变量替换是在DBMS外、变量替换后,{} 对应的变量不会加上单引号

使用#{}可以有效的防止 SQL 注入, 提高系统安全性。

spring

谈谈你对AOP的理解

面向切面

AOP:将程序中的交叉业务逻辑(比如安全,日志,事务等),封装成一个切面,然后注入到目标对象(具体业务逻辑)中去。

AOP可以对某个对象或某些对象的功能进行增强,比如对象中的方法进行增强,可以在执行某个方法之前或之后额外的做一些事情。

谈谈你对IOC的理解

控制反转、依赖注入

ioc容器概念

实际上就是个map(key,value),里面存的是各种对象(在xml里配置的bean节点、@repository、@service、@controller、@component),在项目启动的时候会读取配置文件里面的bean节点,根据全限定名使用反射创建对象放到map里、扫描到打上上述注解的类还是通过反射创建对象放到map里。

这个时候map里就有各种对象了,接下来我们在代码里需要用到里面的对象时,再通过DI注入(autowired、resource等注解,xml里bean节点内的ref属性,项目启动的时候会读取xml节点ref属性根据id注入,也会扫描这些注解,根据类型或id注入;id就是对象名)。

控制反转

控制反转是一种设计思想,而不是一种具体的技术实现。

在 Spring 中,控制反转指的是将对象的控制权转移给 Spring 框架进行管理,由 Spring 帮我们创建对象,管理对象之间的依赖关系。

依赖注入

“获得依赖对象的过程被反转了”。控制被反转之后,获得依赖对象的过程由自身管理变为了由IOC容器主动注入。依赖注入是实现IOC的方法,就是由IOC容器在运行期间,动态地将某种依赖关系注入到对象之中。

使用 IOC 的优点
  • 降低对象之间的依赖程度和耦合度。
  • 便于资源的管理。在 IOC 容器中,所有对象默认都是单例的。

单例Bean和单例模式

单例模式表示JVM中某个类的对象只会存在唯一一个。

而单例Bean并不表示JVM中只能存在唯一的某个类的bean对象。

@Autowired 和 @Resource 的区别是什么?

  • @Autowired 是 Spring 提供的注解, @Resource 是 JDK 提供的注解。
  • Autowired 默认的注入方式为 byType (根据类型进行匹配), @Resource 默认注入方式为 byName (根据名称进行匹配)。
  • 当一个接口存在多个实现类的情况下, @Autowired@Resource 都需要通过名称才能正确匹配到对应的 Bean。 Autowired 可以通过 @Qualifier 注解来显示指定名称, @Resource 可以通过 name 属性来显示指定名称。

spring事务传播机制

多个事务方法相互调用时,事务如何在这些方法间传播。

方法A是一个事务的方法,方法A执行过程中调用了方法B,那么方法B有无事务以及方法B对事务的要求不同都 会对方法A的事务具体执行造成影响,同时方法A的事务对方法B的事务执行也有影响,这种影响具体是什么就 由两个方法所定义的事务传播类型所决定。
  1. REQUIRED(Spring默认的事务传播类型):如果当前没有事务,则自己新建一个事务,如果当前存在事务,则加入这个事务
  2. SUPPORTS:当前存在事务,则加入当前事务,如果当前没有事务,就以非事务方法执行
  3. MANDATORY:当前存在事务,则加入当前事务,如果当前事务不存在,则抛出异常。
  4. REQUIRES_NEW:创建一个新事务,如果存在当前事务,则挂起该事务。
  5. NOT_SUPPORTED:以非事务方式执行,如果当前存在事务,则挂起当前事务
  6. NEVER:不使用事务,如果当前事务存在,则抛出异常
  7. NESTED:如果当前事务存在,则在嵌套事务中执行,否则REQUIRED的操作一样(开启一个事务)

spring事务什么时候会失效?

spring事务的原理是AOP,进行了切面增强,那么失效的根本原因是这个AOP不起作用了!

常见情况有如下几种

1、发生自调用,类里面使用this调用本类的方法(this通常省略),此时这个this对象不是代理类,二十UserService对象本省!

2、方法不是public的:@Transaction只能用于public的方法上,否则事务不会失效,如果要用在飞public方法上,可以开启AspectJ 代理模式。

3、数据库不支持事务

4、没有被spring管理

5、异常被吃掉,事务不会回滚(或者抛出异常没有被定义,默认为RuntimeException)

spring中的Bean创建生命周期有步骤

spring中一个Bean的创建大概分为以下几个步骤:

1、推断构造方法

2、实例化

3、填充属性,也就是依赖注入

4、处理Aware回调

5、初始化前,处理@PostConstruct注解

6、初始化,处理lnitializingBean接口

7、初始化后,进行AOP

Spring容器启动流程是怎样的

1、在创建Spring容器,也就是启动Spring时:

2、首先会进行扫描,扫描得到所有的BeanDefinition对象,并存在一个Map中

3、然后筛选出非懒加载的单例BeanDfinition进行创建Bean,对于多例Bean不需要在启动过程中去创建,对于多例Bean会在每次获取Bean时利用Bean时利用BeanDefinition去创建

4、利用BeanDefinition创建Bean的创建生命周期,这期间包括了合并BeanDefinition、推断构造器、实例化、初始化前、初始化、初始化后等步骤,其中AOP就是发生在初始化后这一步骤中

5、单例Bean创建完了之后,Spring会发布一个容器启动事件

6、Spring启动结束

7、在源码中会更复杂,比如源码中会提供一些模板方法,让子类来实现,比如源码中还涉及到一些BeanFactory和BeanPostProcessor的注册,Spring的扫描就是通过BeanFactoryPostProcessor来实现的,依赖注入就是通过BeanPostProcessor来实现的

8、在Spring启动过程中还会去处理@Import等注解

在这里插入图片描述

未完结

  • 2
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HY丶浪潮

感谢支持,让自己变更好

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值