文章目录
- Spring
- 一、Spring介绍
- 二、IOC
- 1、基本概念
- 2、什么是IOC?什么是DI?
- 3、Spring IOC的实现机制?
- 4、BeanFactory和ApplicationContext?
- 5、Spring容器启动时做些什么?
- 6、Spring容器IOC的生命周期
- 7、Spring Bean的生命周期?
- 8、依赖注入的方法有?
- 9、Spring的自动装配方式?
- 10、Spring中bean的作用域有?
- 11、Spring中的单例bean会存在线程安全问题么?
- 12、循环依赖?
- 12、如何解决循环依赖问题?
- 13、@Autowired的实现原理?
- 14、bean实例化的4种方式?
- 15、创建容器的方式
- 16、获取bean的方式
- 17、如何实现一个IOC容器?
- 三、AOP
- 三、Spring事务
- 四、Spring注解
Spring
一、Spring介绍
1、Spring是什么?特性?
Spring 是一个轻量级、非入侵式的 控制反转 (IoC) 和 面向切面 (AOP) 的框架。
- IOC 和 DI 的支持
Spring 的核心就是容器,可以维护所有对象的创建和依赖关系,Spring 工厂用于生成 Bean,并且管理 Bean 的生命周期,实现 高内聚低耦合 的设计理念。 - AOP 编程的支持
Spring 提供了面向切面编程,可以方便的实现对程序进行 权限拦截、运行监控 等切面功能。 - 声明式事务的支持
支持通过配置就能完成对事务的管理,而不需要通过硬编码的方式,以前重复的一些事务提交、回滚的JDBC代码,都可以不用自己写了。 - 快捷测试的支持
Spring 对 Junit 提供支持,可以通过注解快捷地测试 Spring 程序。 - 快速集成功能
方便集成各种优秀框架,Spring 不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如:Struts、Hibernate、MyBatis、Quartz 等)的直接支持。 - 复杂 API 模板封装
Spring 对 JavaEE 开发中非常难用的一些 API(JDBC、JavaMail、远程调用等)都提供了模板化的封装,这些封装 API 的提供使得应用难度大大降低。
2、Spring有哪些模块?
- Spring Core:Spring核心容器,它是框架最基础的部分,提供IOC和依赖注入DI特性。
- Spring Context:Spring上下文容器,它是BeanFactory功能加强的一个子接口。
- Spring Web:它提供Web应用开发的支持。
- Spring MVC:它针对Web应用中MVC思想的实现。
- Spring DAO:Data Access Object,是用于 访问数据的对象 。提供对JDBC抽象层,简化了JDBC编码,同时,编码更具有健壮性。
- Spring ORM:它支持用于流行的ORM框架的整合,比如:Spring + Hibernate、Spring + MyBatis、Spring + JDO 的整合等。
- Spring AOP:即 面向切面编程,它提供了与AOP联盟兼容的编程实现。
3、Spring中的设计模式?
- 工厂模式:Spring 容器本质是一个大工厂,使用工厂模式通过 BeanFactory、ApplicationContext 创建 bean 对象。
- 代理模式:Spring AOP 功能就是通过代理模式来实现的,分为动态代理和静态代理。
- 单例模式:Spring 中的 Bean 默认都是单例的,这样有利于容器对 Bean 的管理。
- 模板模式:Spring 中 JdbcTemplate、RestTemplate 等以 Template 结尾的对数据库、网络等等进行操作的模板类,就使用到了模板模式。
- 观察者模式:Spring 事件驱动模型就是观察者模式很经典的一个应用。
- 适配器模式:Spring ==AOP 的增强或通知 (Advice) ==使用到了适配器模式、Spring MVC 中也是用到了适配器模式适配 Controller。
- 策略模式:Spring 中有一个 Resource 接口,它的不同实现类,会根据不同的策略去访问资源。
二、IOC
1、基本概念
IoC(Inversion of Control)控制反转: 对象的创建控制权由程序转移到外部,这种思想称之为控制反转(目的:解耦)。
IoC容器:“外部”创建对象的容器。
Bean: IoC容器中创建或管理的对象。
DI(Dependency Injection)依赖注入: 在容器中建立bean与bean之间的依赖关系的整个过程,称之为依赖注入。
自动装配: Ioc容器根据bean所依赖的资源在容器中自动查找并注入到bean中的过程称为自动装配。
AOP(Aspect Oriented Programming): 面向切面编程,用来功能增强。
连接点(JoinPoint): 方法执行的任意位置。
切入点(PointCut): 匹配连接点的式子。
通知(Advice): 切入点处执行的操作。
通知类: 定义通知的类;
切面(Aspect): 描述通知与切入点的对应关系。
2、什么是IOC?什么是DI?
Java 是面向对象的编程语言,一个个实例对象相互合作组成了业务逻辑,原来,我们都是在代码里创建对象和对象的依赖。
所谓的 IOC(控制反转): 就是==由容器来负责控制对象的生命周期和对象间的关系。==以前是我们想要什么,就自己创建什么,现在是我们需要什么,容器就给我们送来什么(将对象的创建控制权由程序转移到外部,这种思想称为控制反转)。
也就是说,控制对象生命周期的不再是引用它的对象,而是容器。对具体对象,以前是它控制其它对象,现在所有对象都被容器控制,所以这就叫控制反转。
DI(依赖注入): 指的是容器在实例化对象的时候把它依赖的类注入给它。
Q:为什么要使用 IOC 呢?
最主要的是两个字 解耦 ,硬编码会造成对象间的过度耦合,使用IOC之后,我们可以不用关心对象间的依赖,专心开发应用就行。
3、Spring IOC的实现机制?
Spring 的 IOC 本质就是一个大工厂 ,我们想想一个工厂是怎么运行的呢?
生产产品:一个工厂最核心的功能就是生产产品。在 Spring 里,不用 Bean 自己来实例化,而是交给 Spring,应该怎么实现呢?——答案毫无疑问,反射。
那么这个厂子的生产管理是怎么做的?你应该也知道——工厂模式。
库存产品:工厂一般都是有库房的,用来库存产品,毕竟生产的产品不能立马就拉走。Spring 我们都知道是一个容器,这个容器里存的就是对象,不能每次来取对象,都得现场来反射创建对象,得把创建出的对象存起来。
订单处理:还有最重要的一点,工厂根据什么来提供产品呢?订单。这些订单可能五花八门,有线上签签的、有到工厂签的、还有工厂销售上门签的……最后经过处理,指导工厂的出货。
在 Spring 里,也有这样的订单,它就是我们 bean 的定义和依赖关系,可以是 xml 形式,也可以是我们最熟悉的注解形式。
4、BeanFactory和ApplicationContext?
BeanFactory 是 Spring 的“心脏”,ApplicantContext 是完整的“身躯”。
BeanFactory (Bean工厂) 是 Spring 框架的基础设施,面向 Spring 本身。
ApplicationContext (应用上下文) 建立在 BeanFactoty 基础上,面向使用 Spring 框架的开发者。
-
BeanFactory 接口
BeanFactory 是类的通用工厂,可以 创建并管理各种类的对象。
BeanFactory 接口位于类结构树的顶端,它最主要的方法就是 getBean(String var1),这个方法 从容器中返回特定名称的 Bean。
BeanFactory 的功能通过其它的接口得到了不断的扩展,比如 AbstractAutowireCapableBeanFactory 定义了将容器中的 Bean 按照某种规则(比如按名字匹配、按类型匹配等)进行自动装配的方法。 -
ApplicationContext 接口
ApplicationContext 由 BeanFactory 派生而来,提供了更多面向实际应用的功能。可以这么说,使用 BeanFactory 就是手动档,使用 ApplicationContext 就是自动档。
ApplicationContext继承了HierachicalBeanFactory和ListableBeanFactory接口,在此基础上,还通过其他的接口扩展了BeanFactory的功能,包括:
Bean instantiation/wiring、Bean 的实例化/串联、自动的 BeanPostProcessor 注册、自动的 BeanFactoryPostProcessor 注册、方便的 MessageSource 访问(i18n)、ApplicationEvent 的发布与 BeanFactory 懒加载的方式不同,它是预加载,所以,每一个 bean 都在 ApplicationContext 启动之后实例化。
5、Spring容器启动时做些什么?
Spring 的 IOC 容器工作的过程,其实可以划分为两个阶段:容器启动阶段和Bean 实例化阶段。
其中容器启动阶段主要做的工作是 加载和解析配置文件 ,保存到对应的 Bean 定义中。
6、Spring容器IOC的生命周期
4个阶段: 刷新阶段、启动阶段、关闭阶段、停止阶段。
- 刷新阶段是IoC生命周期中最为重要的一个阶段,里面包括 上下文准备、容器创建、各种生命周期回调以及内部Bean创建 等的操作。
7、Spring Bean的生命周期?
在 Spring 中,基本容器 BeanFactory 和扩展容器 ApplicationContext 的实例化时机不太一样,BeanFactory 采用的是 延迟初始化 的方式,也就是只有在第一次getBean()的时候,才会实例化Bean;ApplicationContext 启动之后会实例化所有的 Bean 定义。
Spring IOC 中 Bean 的生命周期大致分为四个阶段:实例化(Instantiation)、属性赋值(Populate)、初始化(Initialization)、销毁(Destruction)。
详细步骤如下:
实例化: 第 1 步,实例化一个 Bean 对象。主要包括:①读取spring配置文件相关标签封装生成BeanDefinition对象;②通过反射进行bean的实例化(此时的Bean是空对象信息)。
属性赋值: 第 2 步,为 Bean 设置相关属性和依赖。主要包括:①解析BeanDefinition的属性(beanName、beanType等)并赋值;②如果Bean对象里的属性需要引用容器内部的对象,那么需要调用aware接口的子类方法进行统一设置;③循环依赖。
初始化: 初始化阶段的步骤比较多,5、6 步是真正的初始化,第 3、4 步为在初始化前执行,第 7 步在初始化后执行,初始化完成之后,Bean 就可以被使用了。主要包括:①调用XXXAware回调方法;②调用初始化生命周期回调;③如果bean实现aop创建动态代理。
销毁: 第 8~10 步,第 8 步其实也可以算到销毁阶段,但不是真正意义上的销毁,而是先在使用前注册了销毁的相关调用接口,为了后面第 9、10 步真正销毁 Bean 时再执行相应的方法。主要包括:①在spring容器关闭的时候进行调用;②调用初始化生命周期回调。
具体的描述时可以概括为如下步骤:
8、依赖注入的方法有?
Spring 支持 构造方法注入、属性注入(Setter)、工厂方法注入,其中工厂方法注入,又可以分为 静态工厂方法注入和非静态工厂方法注入。
9、Spring的自动装配方式?
什么是自动装配?
IOC容器根据bean所依赖的资源在容器中自动查找并注入到bean中的过程称为自动装配。
Spring提供了哪几种自动装配类型?
- byName:根据名称进行自动匹配。
- byType:根据类型进行自动匹配。
- constructor:与 byType 类似, 只不过它是针对构造函数注入而言的。
- autodetect:根据 Bean 的自省机制决定采用 byType 还是 constructor 进行自动装配,如果 Bean 提供了默认的构造函数,则采用 byType,否则采用 constructor。
注意: 自动装配bean的优先级是最低的。
10、Spring中bean的作用域有?
- singleton : 在 Spring 容器仅存在一个 Bean 实例,Bean 以单实例的方式存在,是 Bean 默认的作用域。
- prototype : 每次从容器中重新调用 Bean 时,都会返回一个新的实例。
注意:以下三个作用域于只在 Web 应用中适用: - request : 每一次 HTTP 请求都会产生一个新的 Bean,该 Bean 仅在当前 HTTP Request 内有效。
- session : 同一个 HTTP Session 共享一个 Bean,不同的 HTTP Session 使用不同的 Bean。
- globalSession:同一个全局 Session 共享一个 Bean,只用于基于 Protlet 的 Web 应用,Spring5 中已经不存在了。
11、Spring中的单例bean会存在线程安全问题么?
Spring 中的单例 Bean不是线程安全的。
因为单例 Bean,是全局只有一个 Bean,所有线程共享。如果说单例 Bean,是一个 无状态的 ,也就是线程中的操作不会对 Bean 中的成员变量执行查询以外的操作,那么这个单例 Bean 是 线程安全 的。比如 Spring mvc 的 Controller、Service、Dao 等,这些 Bean 大多是无状态的,只关注于方法本身。
假如这个 Bean 是 有状态的 ,也就是会对 Bean 中的成员变量进行写操作,那么可能就 存在线程安全的问题 。
Q:单例 Bean 线程安全问题怎么解决呢?
①将 Bean 定义为多例
这样每一个线程请求过来都会创建一个新的 Bean,但是不利于容器管理 Bean。
②在 Bean 对象中尽量避免定义可变的成员变量
③将 Bean 中的 成员变量保存在 ThreadLocal 中 ★。
我们知道 ThredLocal 能保证多线程下变量的隔离,可以在类中定义一个 ThreadLocal 成员变量,将需要的可变成员变量保存在 ThreadLocal 里,这是推荐的一种方式。
Q:Spring中bean创建完成后执行指定代码有哪几种方式?
①实现ApplicationListener接口;
②实现InitializingBean接口;
③使用@PostConstruct注解。
12、循环依赖?
注意: Spring不支持基于构造器注入的循环依赖。
12、如何解决循环依赖问题?
单例bean初始化要经过3步:实例化、属性赋值、初始化。 注入就发生在属性赋值过程中,Spring通过 三级缓存解决循环依赖 问题。
- 一级缓存:Map<String,Object> singletonObjects,单例池,用于保存实例化、属性赋值(注入)、初始化完成的 bean 实例。
- 二级缓存:Map<String,Object> earlySingletonObjects,早期曝光对象,用于保存实例化完成的 bean 实例。
- 三级缓存:Map<String,ObjectFactory<?>> singletonFactories,早期曝光对象工厂,用于保存 bean 创建工厂,以便于后面扩展有机会创建代理对象。
Q:为什么要用三级缓存?二级缓存行不行?
不行,主要是为了生成代理对象。如果是没有代理的情况下,使用二级缓存解决循环依赖也是 OK 的。但是如果存在代理,三级没有问题,二级就不行了。
因为三级缓存中放的是生成具体对象的匿名内部类,获取 Object 的时候,它可以生成代理对象,也可以返回普通对象。使用三级缓存主要是为了保证不管什么时候使用的都是一个对象。
假设只有二级缓存的情况,往二级缓存中放的显示一个普通的 Bean 对象,Bean 初始化过程中,通过 BeanPostProcessor 去生成代理对象之后,覆盖掉二级缓存中的普通 Bean 对象,那么可能就导致取到的 Bean 对象不一致了。
13、@Autowired的实现原理?
实现@Autowired 的关键是: AutowiredAnnotationBeanPostProcessor
在 Bean 的初始化阶段,会通过 Bean 后置处理器BeanPostProcessor来进行一些前置和后置的处理。
实现 @Autowired 的功能,也是通过 后置处理器 来完成的。这个后置处理器就是 AutowiredAnnotationBeanPostProcessor。
Spring 在创建 bean 的过程中,最终会调用到 doCreateBean()方法,在 doCreateBean()方法中会调用 populateBean()方法,来为 bean 进行属性填充,完成自动装配等工作。
在 populateBean()方法中一共调用了两次后置处理器,第一次是为了判断是否需要属性填充,如果不需要进行属性填充,那么就会直接进行 return,如果需要进行属性填充,那么方法就会继续向下执行,后面会进行第二次后置处理器的调用,这个时候,就会调用到 AutowiredAnnotationBeanPostProcessor 的 postProcessPropertyValues()方法,在该方法中就会进行@Autowired 注解的解析,然后实现自动装配。
AutowiredAnnotationBeanPostProcessor实现了BeanPostProcessor接口,当 Spring 容器启动时,AutowiredAnnotationBeanPostProcessor将扫描 Spring 容器中所有 Bean,当发现 Bean 中拥有@Autowired 注解时就找到和其匹配(默认按类型匹配)的 Bean,并注入到对应的地方中去。
14、bean实例化的4种方式?
①构造函数(默认,使用反射机制)
②静态工厂
③实例工厂
④工厂对象(实现FactoryBean接口,指定泛型)
15、创建容器的方式
①类路径加载配置文件(ClassPathXmlApplicationContext)
②文件路径加载配置文件(FileSystemXmlApplicationContext)
16、获取bean的方式
①使用bean名称
②使用bean名称,同时指定类型
③使用bean类型
17、如何实现一个IOC容器?
三、AOP
1、什么是AOP?
AOP: 面向切面编程。简单说,就是 把一些业务逻辑中的相同的代码抽取到一个独立的模块中 ,让业务逻辑更加清爽,如常见的日志记录和数据校验等。
AOP 的核心其实就是 动态代理,如果是 实现了接口的话就会使用 JDK 动态代理,否则使用 CGLIB 代理 ,主要应用于处理一些具有横切性质的系统级服务,如日志收集、事务管理、安全检查、缓存、对象池管理等。
2、AOP的核心概念
- 切面(Aspect):类是对物体特征的抽象,切面就是对横切关注点的抽象。
- 连接点(Joinpoint):被拦截到的点,因为 Spring 只支持方法类型的连接点,所以在 Spring 中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器。
- 切点(Pointcut):对连接点进行拦截的定位。
- 通知(Advice):所谓通知指的就是拦截到连接点之后要执行的代码,也可以称作增强。
- 目标对象(Target):代理的目标对象。
- 织入(Weabing):织入是将增强添加到目标类的具体连接点上的过程。
- 编译期织入:切面在目标类编译时被织入。
- 类加载期织入:切面在目标类加载到 JVM 时被织入。需要特殊的类加载 器,它可以在目标类被引入应用之前增强该目标类的字节码。
- 运行期织入:切面在应用运行的某个时刻被织入。一般情况下,在织入切 面时,AOP 容器会为目标对象动态地创建一个代理对象。Spring AOP 就 是以这种方式织入切面。
- Spring 采用运行期织入,而 AspectJ 采用编译期织入和类加载器织入。
- 引介(introduction):引介是一种特殊的增强,可以动态地为类添加一些属性和方法。
3、AOP的环绕方式有?
AOP一般有5种环绕方式:前置通知(@Before)、返回通知 (@AfterReturning)、异常通知(@AfterThrowing)、后置通知(@After)、环绕通知(@Around)。
注意: 多个切面的情况下,可以通过 @Order 指定先后顺序,数字越小,优先级越高。
4、AOP的使用?
SpringBoot 项目中,常使用 AOP 打印接口的入参和出参日志,以及执行时间等。
使用步骤: ①引入依赖 --> ②自定义一个注解作为切点 --> ③配置AOP切面类 --> ④需要使用AOP时直接加上注解即可。
5、Spring代理模式有?
JDK 动态代理 和 CGLIB 动态代理。
6、JDK动态代理和CGLIB动态代理的区别?
- JDK 动态代理
① Interface:对于 JDK 动态代理,目标类需要实现一个接口Interface。
② InvocationHandler:InvocationHandler是一个接口,可以通过实现这个接口,定义横切逻辑,再通过反射机制(invoke)调用目标类的代码,在此过程,可以包装逻辑,对目标方法进行前置后置处理。
③ Proxy:Proxy利用InvocationHandler动态创建一个符合目标类实现的接口的实例,生成目标类的代理对象。 - CGLIB 动态代理
① 使用 JDK创建代理有一大限制,它只能为接口创建代理实例 ,而 CGLIB 动态代理就没有这个限制。
② CGLIB 动态代理是 使用字节码处理框架 ASM,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。
③ CGLIB 创建的动态代理对象性能比 JDK 创建的动态代理对象的性能高不少,但是 CGLIB 在创建代理对象时所花费的时间却比 JDK 多得多,所以对于单例的对象,因为无需频繁创建对象,用 CGLIB 合适,反之,使用 JDK 方式要更为合适一些。同时,由于 CGLIB 由于是采用动态创建子类的方法,对于 final 方法,无法进行代理。
7、Spring AOP 和 AspectJ AOP 区别?
-
Spring AOP
Spring AOP 属于 运行时增强,主要具有如下特点:
① 基于 动态代理 来实现,默认如果使用接口的,用 JDK 提供的动态代理实现,如果是方法则使用 CGLIB 实现。
② Spring AOP 需要 依赖 IOC 容器来管理,并且只能作用于 Spring 容器,使用纯 Java 代码实现。
③ 在性能上,由于 Spring AOP 是基于动态代理来实现的,在容器启动时需要生成代理实例,在方法调用上也会增加栈的深度,使得 Spring AOP 的性能不如 AspectJ 的那么好。
④ Spring AOP 致力于解决 企业级 开发中最普遍的 AOP(方法织入)。 -
AspectJ
① AspectJ 是一个易用的功能强大的 AOP 框架,属于 编译时增强, 可以单独使用,也可以整合到其它框架中,是 AOP 编程的完全解决方案。AspectJ 需要用到单独的编译器 ajc。
② AspectJ 属于静态织入,通过修改代码来实现,在实际运行之前就完成了织入,所以说它生成的类是没有额外运行时开销的,一般有如下几个织入的时机:- 编译期织入(Compile-time weaving):如类 A 使用 AspectJ 添加 了一个属性,类 B 引用了它,这个场景就需要编译期的时候就进行织入, 否则没法编译类 B。
- 编译后织入(Post-compile weaving):也就是已经生成了 .class 文件, 或已经打成 jar 包了,这种情况我们需要增强处理的话,就要用到编译后织 入。
- 类加载后织入(Load-time weaving):指的是在加载类的时候进行织 入,要实现这个时期的织入,有几种常见的方法。
8、面向切面编程和面向对象编程的区别?
1、面向过程(POP)和面向对象(OOP)的区别:
两者的主要区别在于解决问题的方式不同:
- 面向过程把解决问题的 过程拆成一个个方法,通过一个个方法的执行 解决问题。
- 面向对象会先 抽象出对象,然后用对象执行方法 的方式解决问题。
另外,面向对象开发的程序一般更易维护、易复用、易扩展。
2、面向对象(OOP)和面向切面(AOP)的区别:
- 面向目标不同:简单来说OOP是面向名词领域,AOP面向动词领域。
- 思想结构不同:OOP是纵向结构,AOP是横向结构。
- 注重方面不同:OOP注重业务逻辑单元的划分,AOP偏重业务处理过程的某个步骤或阶段。
OOP和AOP联系: 两者之间是一个相互补充和完善的关系。
总的来说:
AOP是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性。 主要的功能是:日志记录,性能统计,安全控制,事务处理,异常处理等等。
OOP针对业务处理过程的实体及其属性和行为进行抽象封装,以获得更加清晰高效的逻辑单元划分。
三、Spring事务
Spring 事务的本质其实就是 数据库对事务的支持 ,没有数据库的事务支持,Spring 是无法提供事务功能的。Spring 只提供 统一事务管理接口 ,具体实现都是由各数据库自己实现,数据库事务的提交和回滚是通过数据库自己的事务机制实现。
1、Spring事务的种类?
Spring支持 编程式事务管理 和 声明式事务管理 。
编程式事务: 编程式事务管理使用 TransactionTemplate,需要 显式执行事务。
声明式事务: 声明式事务管理建立在 AOP 之上的。其本质是通过 AOP 功能,对方法前后进行拦截,将事务处理的功能编织到拦截的方法中,也就是在目标方法开始之前启动一个事务 ,在执行完目标方法之后根据执行情况提交或者回滚事务。优点是不需要在业务逻辑代码中掺杂事务管理的代码,只需在 配置文件中做相关的事务规则声明或通过 @Transactional 注解的方式,便可以将事务规则应用到业务逻辑中,减少业务代码的污染。唯一不足地方是,声明式事务最细粒度 只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。
2、声明式事务实现原理
就是通过 AOP/动态代理 。
- 在Bean初始化阶段创建代理对象: Spring容器在初始化每个单例bean的时候,会遍历容器中的所有BeanPostProcessor实现类,并执行其 postProcessAfterInitialization方法,在执行AbstractAutoProxyCreator类的postProcessAfterInitialization方法时会遍历容器中所有的切面,查找与当前实例化bean匹配的切面,这里会获取事务属性切面,查找@Transactional注解及其属性值,然后根据得到的切面创建一个代理对象,默认是使用JDK动态代理创建代理,如果目标类是接口,则使用JDK动态代理,否则使用CGLIB。
- 在执行目标方法时进行事务增强操作: 当通过代理对象调用bean方法的时候,会触发对应的AOP增强拦截器,声明式事务是一种环绕增强,对应接口为MethodInterceptor,事务增强对该接口的实现为TransactionInterceptor,类图如下:
事务拦截器TransactionInterceptor在invoke方法中,通过调用父类TransactionAspectSupport的invokeWithinTransaction方法进行事务处理,包括开启事务、事务提交、异常回滚。
3、声明式事务什么时候失效?
4、Spring事务隔离级别?
Spring事务的隔离级别对应数据库的事务隔离级别,以MySQL数据库为例,主要包括:读未提交、读已提交、可重复读、串行化。
5、Spring事务传播机制?
Spring 事务的传播机制说的是,当多个事务同时存在的时候,一般指的是 多个事务方法相互调用时,Spring 如何处理这些事务的行为。
事务传播机制是使用简单的 ThreadLocal 实现的,所以,如果调用的方法是在新线程调用的,事务传播实际上是会失效的。
Spring 默认的事务传播行为是 PROPAFATION_REQUIRED 。
四、Spring注解
Spring的注解主要分为两类:
- 类注解:@Component、@Repository、@Controller、@Service
- 类内部注解(字段和方法上):@Bean、@Autowired、@Value、@Resource
1、Web
@Controller:组合注解(组合了@Component注解),应用在MVC层(控制层)。
@RestController:该注解为一个组合注解,相当于@Controller和@ResponseBody的组合,注解在类上,意味着,该Controller的所有方法都默认加上了@ResponseBody。
@RequestMapping:用于映射Web请求,包括访问路径和参数。如果是Restful风格接口,还可以根据请求类型使用不同的注解:@GetMapping、@PostMapping、@PutMapping、@DeleteMapping。
@ResponseBody:支持将返回值放在response内,而不是一个页面,通常用户返回 json 数据。
@RequestBody:允许request的参数在request体中,而不是在直接连接在地址后面。
@PathVariable:用于接收路径参数,比如@RequestMapping(“/hello/{name}”)申明的路径,将注解放在参数中前,即可获取该值,通常作为Restful的接口实现方法。
@RestController:该注解为一个组合注解,相当于@Controller和@ResponseBody的组合,注解在类上,意味着,该Controller的所有方法都默认加上了@ResponseBody。
2、容器
@Component:表示一个带注释的类是一个“组件”,成为Spring管理的 Bean。当使用基于注解的配置和类路径扫描时,这些类被视为自动检测的候选对象。同时@Component还是一个元注解。
@Service:组合注解(组合了@Component注解),应用在service层(业务逻辑层)。
@Repository:组合注解(组合了@Component注解),应用在dao层(数据访问层)。
@Autowired:Spring 提供的工具(由Spring的依赖注入工具(BeanPostProcessor、BeanFactoryPostProcessor)自动注入)。
@Qualifier:该注解通常跟@Autowired一起使用,当想对注入的过程做更多的控制,@Qualifier可帮助配置,比如两个以上相同类型的Bean时Spring无法抉择,用到此注解。
@Configuration:声明当前类是一个配置类(相当于一个 Spring 配置的 xml 文件)
@Value:可用在字段,构造器参数跟方法参数,指定一个默认值,支持 #{} 跟 ${} 两个方式。一般将 SpringBoot 中的 application.properties 配置的属性值赋值给变量。
@Bean:注解在方法上,声明当前方法的返回值为一个Bean。返回的Bean对应的类中可以定义init()方法和destroy()方法,然后在@Bean(initMethod=”init”,destroyMethod=”destroy”)定义,在构造之后执行init,在销毁之前执行destroy。
@Scope:定义我们采用什么模式去创建Bean(方法上,得有@Bean) 其设置类型包括:Singleton 、Prototype、Request 、 Session、GlobalSession。
3、AOP
@Aspect:声明一个切面(类上) 使用@After、@Before、@Around定义建言(advice),可直接将拦截规则(切点)作为参数。
@After:在方法执行之后执行(方法上)。
@Before:在方法执行之前执行(方法上)。
@Around:在方法执行之前与之后执行(方法上)。
@PointCut:声明切点 在 java 配置类中使用@EnableAspectJAutoProxy 注解开启 Spring 对 AspectJ 代理的支持(类上)。
4、事务
@Transactional:在要开启事务的方法上使用@Transactional 注解,即可声明式开启事务。
声明bean的注解:@Component(通用注解方式)、@Service(业务层注解)、@Repository(数据层注解)、@Controller(表现层注解)。
注入bean的注解: @Autowired(Spring提供)、@Qualifier(配和@Autowired使用)、@Inject、@Resource 。
配置类相关注解:@Configuration(声明当前类为配置类,相当于xml形式的Spring配置(类上))、@Bean(注解,方法上,声明当前方法的返回值为一个bean,替代xml中的方式(方法上))、@Configuration(声明当前类为配置类,其中内部组合了@Component注解,表明这个类是一个bean(类上))、@ComponentScan(用于对Component进行扫描,相当于xml中的(类上))。
AOP相关注解:@Aspect(声明当前类为切面类(类上))、使用@After、@Before、@Around定义通知(advice),可直接将拦截规则(切点)作为参数。(@After 在方法执行之后执行(方法上)、@Before 在方法执行之前执行(方法上)、@Around 在方法执行之前与之后执行(方法上))、@PointCut(声明切点)、在java配置类中使用@EnableAspectJAutoProxy注解开启Spring对AspectJ代理的支持(类上)。
Bean属性设置注解:@Scope(bean的生命周期)、@PostConstruct(在构造方法和init方法(如果有的话)之间得到调用,且只会执行一次。等价于xml配置文件中bean的initMethod)、@PreDestory(在Bean销毁之前执行,等价于xml配置文件中bean的destroyMethod)
Q:@Resource 和 @Autowired 的区别
- @Resource是 Java自己的注解,有两个属性比较重要,分别是name和type;Spring将@Resource注解的 name属性 解析为 bean的名字,而 type属性 则解析为 bean的类型。所以如果i) 使用name属性,则使用byName的自动注入策略,而ii)使用type属性时则使用byType自动注入策略。默认按name进行注入。
- @AutoWired是 Spring的注解,@Autowired只根据type进行注入,不会去匹配name。如果涉及到type无法辨别注入对象时,那需要依赖@Qualifier或@Primary注解一起来修饰。使用@Autowired方式最好使用构造函数的方式注入。
- @Resource默认 按名称 方式进行bean匹配,@Autowired默认 按类型 方式进行bean匹配。
Q:@bean 和 @Component 的区别
@Component 和 @Bean是两种使用注解来 定义bean的方式,都可以使用@Autowired或者@Resource注解注入。
- @Component(和@Service和@Repository)用于 自动检测和使用类路径扫描自动配置bean 。注释类和bean之间存在隐式的一对一映射(即每个类一个bean)。@Component注解表明一个类会作为组件类,并告知Spring要为这个类创建bean。
- @Bean用于 显式声明单个bean ,而不是让Spring像上面那样自动执行它。它将bean的声明与类定义分离,并允许您精确地创建和配置bean。@Bean常和@Configuration注解搭配使用。@Bean注解告诉Spring这个方法将会返回一个对象,这个对象要注册为Spring应用上下文中的bean。
Q:为什么有了@Component,还需要@Bean?
如果想将 第三方的类变成组件,你又没有没有源代码,也就没办法使用@Component进行自动配置,这种时候 使用@Bean 就比较合适了。不过同样的也可以通过xml方式来定义。
另外 @Bean注解的方法返回值是对象 ,可以在方法中为对象设置属性。
此外Spring的Starter机制,就是通过@Bean注解来定义bean。
可以搭配@ConditionalOnMissingBean注解 @ConditionalOnMissingClass注解,如果本项目中没有定义该类型的bean则会生效。避免在某个项目中定义或者通过congfig注解来声明大量重复的bean。