java常见面试问题

1谈谈你对面向对象的理解:

面向对象和面向过程经常拿来一起比较,面向过程 就是根据功能的实现逻辑,然后一步一步的设计方法完成。面向对象就是把里面每个对象抽象出来,定义它独属的方法,有点类似于模块化开发,对象只负责自己的方法。然后功能实现的时候就去调用对应对象的方法,这样相对于面向过程来说,它更容易复用维护和扩展,而面向过程的性能要高一些。

它有三个重要的性质:封装继承和多态。封装是指将具体的实现逻辑不对外开放,对外暴露方法名能让外部调用即可。继承,继承父类的方法然后子类可以进行拓展。多态:父类的引用指向子类的实例,这样可以调用子类实例的方法,更加灵活。

2 jdk,jre,jvm的区别和联系

jdk:java的开发工具,它是给java程序员用来开发程序用的。

jre:java运行时环境,程序运行时就需要用到jre。

jvm:java虚拟机,java代码能够一次编译,到处运行就是因为有jvm

Jdk包含jre包含jvm

程序员写的.java文件,然后javac编译为.class文件,然后把.class文件放到jvm上运行,jvm会把它变成机器语言。

3简述final的作用

修饰类:不可以被继承

修饰方法:不可以被子类覆盖,但是可用重载

修饰变量:表示常量,一旦被赋值就不能再被修改

​ 成员变量:必须直接赋值或者构造器中赋值 或者代码块中赋值。

​ 修饰局部变量:可以先声明,但是在使用之前一定要赋值

​ 修饰引用类型:相当于绑定了地址值了,但是内容可用修改

4 重载和重写的区别

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

重写:发生在父子类中,方法名参数列表必须相同,返回值类型小于等于父类,抛出的异常范围小于等于父类,访问修饰符大于等于父类,父类方法如果修饰符为private,子类不能重写该方法

重载和返回值类型没有关系

//会报错 方法名相同 参数列表相同 返回值类型不同
public int a(){
        return 5;
    }
    public  String a(){
        return "ahf";
    }

5接口和抽象类的区别

1 抽象类可以存在普通成员函数,而接口只能存在public abstract方法

2抽象类的成员变量可用是各种类型 ,而接口的成员变量是public static final修饰

3抽象类只能继承一个,而接口可用实现多个

接口的设计目的:对外提供访问的,封装了内部的实现细节。,它规定了类的行为,但是没有规定具体的实现细节。

抽象类:本质是为了代码复用,当一些类具体有相同的行为时,我们就可以把相同的行为抽象为一个抽象类,不同的部分交由子类取实现,对类的通性的抽象。

所以,当我们关注事务的本质时,使用抽象类,关注具体的行为时使用接口。

6 List和set的区别

list:有序,可重复,按对象进入的顺序保存对象,允许出现多个Null对象,可以使用iterator取出所有元素,也可以通过下标取出元素

set:无序,不可重复 ,最多一个null对象,只能通过iterator取得所有元素。

7 hashcode和equals方法

hashcode和equals都是javaobject类的基本方法,在不重写的情况下:hashcode默认的值是堆中的内存地址 equal和==相同。

在使用时,也分情况,主要是两种情况:是否创建散列表

没有:比如New 两个相同的modle,如果传入相同的参数,那么它们的hashcode不同但是equals(重写过,比较内容)是相同的

如果创建了,那么hashcode也必须要重写,这样才能达到equals相同,然后hashcode相同的目的。可以用hashmap来举例,hashcode相同,分配到同一个位置,然后调equal比较内容是否相同。

8 arrylist和linkedList的区别

arrylist:动态数组,需要连续的内存存储空间,适合下标访问。扩容机制;默认是10 超过后会按1.5倍扩容,具体是新建一个数组,然后将老数组的内容拷贝到新数组中,插入数据时,如果不是尾插法,那么元素会往后移,然后再插入。如果尾插法,效率会非常好

linkedlist:基于链表实现的,可以使用分散的内存空间,适合数据

的修改,不适合查询,因为查询需要逐一的去遍历(迭代器或者for循环),而且不能使用indexof对其进行遍历,因为结构为空会遍历这个链表。

9 concureentHashMap原理

jdk7和8的区别

数据结构:reentrantLock+segment+hashentry 一个segment中包含一个hashenry数组,每个hashentry又是一个链表结构

元素查询时:二次hash。第一次定位到segment片段,第二次定位到元素所在链表的头部。

锁:segment分段锁继承了reentrantLock,锁定的时操作的segment,对其他的segemnt不影响,并发度就是segment的个数,可以通过构造函数知道,数组扩容不影响其他segment

get方法无需加锁,volate保证安全

jdk8:

数据结构:synchronized(扩容 hash冲突时用)+cas+node+红黑树 node的val和next都用volatie修饰,保证可见性,查找替换,赋值操作都使用cas

锁:锁链表的head节点,不影响其他元素的读写,锁粒度更细,扩容时,阻塞所有的读写操作,并发扩容。

读操作无锁:node的val和next使用volatile修晒,读写线程对该变量的互相可见,数组用volatile修饰,保证扩容时被线程感知。

总结相比于1.7 1.8锁的粒度更细。1.7分段锁锁住sehment片段,1.8是用synchronized锁链表的头部,volatile锁next和val

10如何实现一个ioc容器

1配置文件配置包扫描路径

2 递归包扫描获取class文件

3反射,确定需要交给ioc管理的表

4对需要注入的类进行依赖注入

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

我们写的源代码—》编译后(java.c编译)生成.class文件(字节码)—》在jvm上运行,然后被解释为机器可用执行的二进制代码—》机器运行该二进制代码

好处:编译一次就可以在多种不同的计算机上面运行

12 java的类加载器有哪些

jdk自带三个类加载器:bootstrap classloader(爷) extclassloader(父) appclassloader(当前)并不是直接继承:中间通过一个变量来维护,继承classloader可以自定义类加载器

13 双亲委托模型

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6JuE0kWG-1615612234831)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210306130600642.png)]

解释:向上委派和向下查找,拿到一个类的时候向上委派,查找缓存,找到了就返回,没有找到就继续往上找,到顶了(boostrap)还没有,那就向下查找,找到了就返回,没有找到就继续向下,到了app一般就可以找到了。

14 java中的异常体系

顶级父类:throwable有两个子类:exception和error

error:程序无法处理的错误,一旦出现该错误,程序被迫停止运行

exception不会导致程序停止,又分为运行时异常和检查异常

运行时异常发生在程序运行中,会导致程序当前线程执行失败,检查异常常常发生在程序编译过程中,会导致程序编译不通过。

15 gc如何判断对象可以被回收

引用技术法:每个对象都有一个引用技术属性,新增一个引用时计数+1,引用释放时计数减一,计数为0时,表示可回收

可达性分析法(java采用 要标记两次):从gc root开始向下搜索,搜索所走过的路径为引用链,当一个对象到gc roots没有任何引用链相连时,则证明此对象是不可用的,那么虚拟机就判断是可回收对象。

gcroot对象:栈中引用对象 方法区静态属性引用对象 常量引用的对象 本地方法栈引用的对象

16线程的生命周期

线程有五种状态:创建 就绪(start方法) 运行 阻塞 和死亡

阻塞有三种:

等待阻塞:线程执行wait方法,然后释放占用的所有资源,线程被放入等待池中,只能通过notify唤醒

同步阻塞:线程在获取对象的锁的时候,锁被其他线程占用,那么jvm会把线程放入锁池

其他阻塞:执行sleep jion的方法 或发出io请求时等待io处理完毕。

17 sleep() wait() join() yield()的区别

锁池:所有竞争同步锁的线程都会放在锁池中

等待池:调用wait方法后会把线程放进等待池中,等待池中的线程不回去竞争同步锁

sleep:thread类的本地静态方法,wait是object类的本地方法

sleep不会释放锁,wait会释放锁,然后会把线程放入等待池中。

join:线程进入阻塞状态

yield():让当前线程进入就绪状态

18 说说你对线程安全的理解

我们说的线程安全其实是指内存安全,堆是共享内存,可以被所有线程访问。

堆:是进程和线程共有的空间,分为全局堆和局部堆,全局堆就是所有没有分配的空间,局部堆就是分配给用户的空间,堆在操作系统对进程初始化的时候分配,运行过程中也可以向系统要额外的堆,但是用完了要还给系统,否则就是内存泄漏。

栈是每个线程独有的,保存其运行状态和局部自动变量的,栈在线程开始的时候初始化,每个线程的栈互相独立,因此,栈是线程安全的,操作系统在切换线程的时候自动切换栈,栈空间不需要再高级序言里面显示的分配和释放。

19 说说你对守护线程的理解

守护线程是对非守护线程提供服务的线程,守护的是jvm所有的非 守护线程。
守护线程的终止自身是无法控制的,所以不要把io,file等重要操作的逻辑分配给它
应用场景:为其他线程提供服务支持 ,如果要求程序结束时,线程必须正常的关闭就可以把该线程作为守护线程来使用,
守护线程中产生的新线程也是守护线程。

20 threadLocal的原理和使用场景

每一个对象均含有一个threadlocalmap类型的成员变量,它存储本线程所有threadLocal对象及其对应的值。
threadlocalmap由一个个entry对象构成,entry有threadlocal对象和object构成。entry的key就是threadlocal对象,并且是一个弱引用。当没有指向key的强引用后,可以就会被垃圾回收器回收。
执行set方法时,threadlocal会先获取当前线程对象,然后获取当前线程的threadlocalmap对象,然后以threadlocalmap为key,将值存进threadlocalmap中。
get方法类似。threadlocal首先会获取当前线程对象,然后获取当前线程的threadloaclmap对象,再以threadlocal对象为key,获取对应的value。
由于每一条线程均含有各自私有的threadlocalmap容器,所以是线程安全的。
使用场景:
1 在进行对象跨层传递的时候,使用threadlocal可以避免多次传递,打破层次间的约束。
2 线程间的数据隔离
3 进行事物操作,用于存储线程事务信息
4数据库链接,sessio会话管理
在这里插入图片描述

21 threadlocal内存泄露的原因,如何避免?

不会再被使用的对象或者变量占用的内存不能被回收,就是内存泄漏。
强引用:最常使用的引用(new),一个对象如果具有强引用,那么就不会被垃圾回收器回收,当内存空间不足,宁愿跑出oom也不会回收这个对象。如果想取消这个强引用,那可以把对象赋值为null,那么合适的时候jvm就会回收这个对象
弱引用:jvm在进行垃圾回收时,弱引用的对象就会被回收。
内存泄露:以threadlocal为弱引用key的entry,如果外部threadloca不存在强引用,那么gc会回收这个key,但是不能回收这个object值,所以就会出现内存泄露,
解决方法:手动调用remove方法清除value值

22 并发 并行 串行的区别

并发:一段时间内有多个线程在执行(单核)
并行:一个时间点有多个线程在执行(多核)
串行:按照顺序来运行,前一个任务没有运行完毕,下一个任务就只能等着。

23 并发的三大特性

原子性:一个操作中cpu不可以中途暂停,然后调度,即不被中断操作,要不全部执行完成,要不都不执行(比如转账,a给b转1000,那a必须减少1000 b必须增加1000)

//分析i++为什么线程不安全 执行i++ 假设i=0 原子+可见就可以实现线程安全 atomicInteger可以让i++安全:volatie(可见+有序)+cas(原子性)
1 将i从主存读到工作内存的副本中
2 将i+1
3 将结果写入工作内存du
 //分析:如果不能保证原子性,即123可以分步来操作,那么线程1 2 读取结果都是0,然后完成i+1,写入工作内存后 i的值都是1 这个很明显不对,我们需要让+2次的结果变为2.  
  
    //如果保证了原子性,但是不保证可见性。那么线程1即使完成原子操作,但是结果还没有刷进主存中的时候,线程2读取i的结果任然为0,那么它操作后的结果任然为1,这显然时不对的。
4 将工作内存的值刷回主存(这一步是由操作系统来完成的,不确定)

可见性:当多个线程访问一个变量时,一个线程修改了这个变量的值,其他线程立刻看得到修改的值 涉及到两个协议:总线和mesi(缓存一致性协议)这两个协议使得1234步都为原子性操作

有序性:不允许出现指令重排(单线程操作结果不变),如果下面的代码的业务逻辑要依赖上面的代码,那么多线程情况下指令重排就会不安全

补充一下:volatie经常用来修饰对象(new 对象时如果发生指令重排是可能出现返回一个空对象的情况的)

//new一个对象
1 分配一个空间
2 给对象赋值
3 栈中的变量指向对象//指令重排 1 3就返回了  没有给对象赋值。

24 为什么要使用线程池 ,解释一下线程池参数

1 降低资源消耗,提高线程利用率,降低创建和销毁线程的消耗

2提高响应速度:直接就有线程在等待执行,而不需要我们去创建线程

3提高线程的可管理性:线程是稀缺资源,使用线程池可以统一分配调优监控。

参数:核心线程数 最大线程数 超时等待时间(回收非核心线程用的) 时间的单位 任务队列 线程工厂 和拒绝策略

如果来任务了,线程还没有到核心线程数,那就创建线程直到核心线程被创建,如果核心线程数满了,那就把新来的任务放进任务队列中,当任务队列满了,就创建线程直到最大线程数。当最大线程数任务队列也满了,那就使用拒绝策略。

25 线程池中阻塞队列的作用?为什么先添加到队列,而不是先创建最大线程?

1 阻塞队列和一般队列不同,如果一般队列(有限长度的缓冲区)满了,新来的任务是加不进队列中的,那么数据就无法保存,而阻塞可以把任务阻塞,然后保留要入队的任务。

阻塞队列可以使得当任务队列没有任务时,核心线程进入等待池中等待,释放cpu资源。它还自带唤醒功能,在有任务入队的时候就会唤醒线程进行处理。

2 在创建新线程的时候,是要获取全局锁的,这时候其他的线程就得阻塞,影响了整体效果。核心线程可以看作正式员工,非核心可以看成外包。当新任务来了,是可以压一压让正式员工干的,如果超过正式工的极限才会请外包。

26 线程池线程复用原理

线程池是将线程和任务解耦(分开),在线程池中,同一个线程可以不断得获取任务来执行,原理就是 线程池对thread进行了封装,并不是每次执行任务都会调优Thread.start()来启动线程,而是让每个线程去执行一个循环任务,在这个循环任务中不停检查是否有任务需要执行,如果有则直接执行,也就是调优任务中的run方法,将run方法当成一个普通的方法执行,通过这种方式只使用固定线程就将所有的任务的run方法串联起来。

27 什么是spring?

spring是一个容器框架,用来装javabean(java对象),中间层框架可以起一个连接作用,可以让企业开发更快更简洁:

spring是一个控制反转和面向切面的容器框架

1 从大小和开销上来说都是轻量级的

2通过控制反转的技术达到松耦合

3提供了面向切面的丰富支持,允许通过分离应用的业务逻辑和系统级服务进行内聚性开发

4包含并管理应用对象(bean)的配置和生命周期,说明是个容器

5将简单的组件配置,组合成为一个复杂的应用,这个意义上来说是一个框架。

28 谈谈你对aop的理解

系统是由许多不同的组件组成的,每一个组件负责一块特定功能,除了实现自身核心功能之外,这些组件还要实现一些额外的职责:比如日志,事务管理和安全 这样核心服务经常要融入到自身具有核心业务逻辑的组件中去,这些系统服务经常被称为横切关注点,因为它们会跨越系统的多个组件。

所以像我们需要为分散的对象引入公共行为的时候,使用面向对象编程的话就会使得代码重复度很高。比如实现日志功能

日志代码往往水平的分散在所有对象层次中,而与它锁散步到的对象的核心功能毫无关系。

aop:将程序的交叉业务逻辑(比如安全 日志 事务等),封装成一个切面,然后注入到目标对象(具体业务逻辑)中去,aop可以对某个对象或某些对象的功能进行增强,比如对象中的方法进行增强,可以执行某个方法之前额外的做一些事情,在某个方法执行之后额外的做一些事情。

29谈谈你对ioc的理解

容器概念 控制反转 依赖注入

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

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

控制反转:没有引入ioc容器之前,对象a依赖于对象b,那么对象a在初始化或者运行到某一点的时候,自己必须注定去创建b或者使用已经创建的对象,无论是创建还是使用对象b,控制权都在自己手上。

引入ioc容器之后,对象a与对象b之间失去了直接联系,当对象a运行到需要的对象b的时候,ioc容器会主动创建一个对象b注入到对象a需要的地方。

通过前后的对比,不难看出对象a获得对象b的过程由主动行为变为了被动,控制权颠倒过来了,这就是控制反转这个名称的由来。

全部的对象控制权全部上缴给第三方ioc容器,所以ioc容器成了整个系统的关键核心。它起到一种类似“粘合剂”的作用,把系统中的所有对象粘合在一起发挥作用,如果没有这个粘合剂,对象与对象之间会彼此失去联系,这就是有人把ioc容器比喻为粘合剂的由来。

30 beanfactory和applicationcontext有什么区别

applicationcontext是beanfactory的子接口

applicationcontext提供了更完整的功能

1 继承messagesource 因此支持国际化

2统一资源文件访问方式

3提供在监听器中注册bean的事件

4同时加载多个配置文件

5载入多个(有继承关系)上下文,使得每一个上下文都专注于一个特定的层次,比如web层。

beanfactory采用的是延迟加载形式来注入bean,即只有在使用到某个bean时,才对该bean进行加载实例化。这样我们就不能发现一些存在的spring的配置问题。如果bean的某一个属性没有注入,beanfactory加载后,直到第一次使用调用getbean方法才会抛出异常。

applicationcontext:它是在容器启动时,一次性创建了所有的bean。这样,在容器启动时,我们就可以发i西安spring中存在的配置错误,这样有利于检查所依赖的属性是否注入。applicationcontext启动后预加载入所有的单实例bean,通过与载入单实例bean,确保当你需要的时候,你就不用等待,因为它们已经创建好了。

相对于基本的beanfactory,applicationcontext唯一的不足是占用内存空间。当应用程序配置bean较多时。程序启动较慢。

beanfactory通常以编程的方式被创建,applicationcontext还能以声明的方式创建,如使用contextloader

beanFactory和applicationcontext都支持beanPostprocessor的使用,但两者的区别是:beanfactory需要手动注册,而application则是自动注册。

31 描述一下springbean的生命周期

1解析类得到beanDefinition

2如果有多个构造方法,则要推断构造方法

3确定好构造方法后,进行实例化得到一个对象

4对对象中的加了@autowired注解的属性进行属性填充(依赖注入)

5回调aware方法,比如beanNameAware,beanfactoryaware

6调用beanpostprocessor的初始化前的方法

7调用初始化方法

8调用beanpostprocessor的初始化后的方法,在这里会进行aop

9如果当前创建的bean是单例的则会把bean放入单例池中。

10使用bean

11 spring 容器关闭时调用disposablebean中的distory方法。

32 spring支持的几种bean的作用域

singleton:默认 每个容器只有一个bean的实例,单例的模式由beanfactory自身来维护,该对象的对象周期是与spring ioc容器一致的(单在第一次被注入时创建)。

prototype为每一个bean请求提供一个实例,在每次注入时都会创建一个新的对象。

request:bean被定义为在每个http请求中创建一个单列对象,也就是说在单个请求都会复用这一个单例对象

seesion:与request范围类似,确保每个session中有一个bean实例,在session中有一个bean实例,在session过期后,bean会随之失效。

application:bean被定义为servletContext的生命周期中复用一个单例对象

websocket:bean被定义为websocket的生命周期中复用一个单例对象。

gloal-session:全局作用域,global-session和portle应用相关。当你的应用部署在protket容器中工作时,它包含很多portlet,如果你想要声明让所有的portlet共有全局的存储变量的话,那么这全局变量需要存储在global-session。全局作用域与servlet中的session作用域效果相同。

33spring框架中的单例bean是线程安全的吗?

不是线程安全的,框架并没有对bean进行多线程处理。

如果bean是有状态的,那就需要开发人员自己来进行线程安全的保证,最简单的办法就是改变bean作用域,把singleton改为property这样每次请求bean就相当于new bean(),这样就可以保证线程安全

有状态就是有数据存储功能,无数据就是不会保存数据 controller service 和dao层本身不是线程安全的,如果只是调用里面的方法,而且多线程调用一个实例的方法,会在内存中复制彬良,这是自己的线程的工作内存,是安全的。

dao会操作数据库connection,connection是带有状态的,比如数据库事务.spring事务管理使用threadlocal为不同线程维护了一套独立的connection副本,保证线程之间不会互相影响(spring 是如何保证事务获取同一个connection的)

不要在bean声明任何有状态的实例变量或类变量,如果必须如此,那么就是要threadlocal把变量变为线程私有的,如果bean的实例变量或类变量需要在多个线程之间共享,那么就只能使用synchronized lock cas等这些实现线程同步的方法了。

34 谈谈你对进程线程的理解

我对进程和线程的理解是循序渐进的,在刚学多线程的时候了解到了这个进程和线程。这里的解释是“进程是资源执行的基本单位,线程是轻量级进程。一个进程可以有多个线程,它们都周期基本相同:创建 就绪 运行 阻塞 销毁这几个状态,进程和线程之间都可以进行对应的通信。后来学习了操作系统,才算真的了解了进线程。进程的解释是:程序动态的执行的一次过程,这里把进程与程序做了一个对比。程序是静态的一段代码,进程则是动态的,它包括了程序运行时的所有信息,包括代码,操作的数据,寄存器等内容。进程在cpu的数据结构叫做pcb,进程控制块。它有三大部分组成:标识符 状态信息,和控制信息。至于线程,我们要理解线程是怎么来的,来干什么。这里与进程作比较,进程是一块一块的内存空间,最初的时候,一个服务的进程只有一个(性能不好),含有许多的功能,后来为了把这些功能分开,又创建了多个进程(开销就很大)。这时候我们想要一种可以并发执行,而且共享相同地址空间的策略,这时候线程就出来了。进程=线程+资源控制,线程是一条执行的流程,但是它也有缺陷,就是一个线程如果崩了,那么进程所属的线程都会崩。

35 spring框架中都用到了哪些设计模式

1简单工厂:由一个工厂类根据传入的参数,动态决定应该创建哪一类产品,spring中的beanfactory就是简单工厂模式的体现,根据传入一个唯一的标识来获得bean对象,但是否再传入参数前创建对象还是要根据具体的情况来决定。

工厂方法:实现了factorybean接口的bean是一类叫做factory的bean,其特点是spring会在使用getbean()调用获得该bean时,会自动调用该bean的getobject()方法,所以返回的不是factory这个bean,而是这个bean.getObject()方法的返回值

单例模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

spring对单列的实现,spring中的单例模式完成了后半句话,即提供全局的访问点beanfactory,单没有从构造器级别去控制单例,这是因为spring管理的是任意java对象。

适配器模式:spring定义了一个适配接口,使得每一个controller有一个对应的适配器实现类,让适配器代替controller执行相应的方法,这样在拓展controller时,只需要增加一个适配器类就完成了springmvc的拓展了。

装饰器模式:动态地给一个对象添加一些额外的职责,就增加功能来说,decorator模式相比生产子类更为灵活。spring中用到的包装器模式在类名有两种表现,一种时类名中含义wrapper,另一种时类名中含义decorator

动态代理模式:切面在应用运行的时候织入,一般情况下,在织入漆面时,aop容器会为目标对象动态的创建一个代理对象,springaop就是以这种方式织入切面的。

织入:把切面应用到目标对象并创建新的代理对象的过程

观察者模式:spring的事件驱动模型使用的是 观察者模式,spring中observer模式常用的地方是listener的实现

策略模式:spring框架的资源访问resource接口,该接口提供了更强的资源访问能力,spring跨级本身大量使用了resource接口来访问底层资源。

36 spirng事务的实现方式

在使用spring框架时,可以有两种使用事务的方式,一种时编程Ⅹ的,一种时声明式的。@transactional注解就是声明式的。

首先,事务的这个概念式数据库层面的,spring只是基于数据库中的事务进行了拓展,以及提供了一些能让程序员更加方便操作事务的方式。

比如我们可以通过在某个方法上添加@transactional注解,就可以开启事务,这个方法中所有的sql都会在一个事务中执行,统一失败或成功。

在一个方法上加了@transactional注解后,spring会基于这个类生成一个代理对象,会将这个代理对象作为bean,当在使用这个代理对象的方法时,如果这个放啊上存在这个注解,那么代理逻辑会把事务自动提交设置为false,然后再去执行原本的业务逻辑方法,如果执行业务逻辑方法没有出现异常,那么代理逻辑中就会把事务进行提交,如果执行业务逻辑方法出现了异常,那么就会将事务进行回滚。当然,针对哪些回滚的事务是可以配置的,可以利用@transactional注解中的rollbackfor属性进行配置,默认情况下回对runtimeexception和error进行回滚

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值