Spring面经


1、Spring是什么?

Java的Spring是一个开源的、轻量级的Java应用程序框架,它可以帮助开发人员构建企业级应用程序。Spring框架提供了一个综合性的编程和配置模型,使得开发人员可以使用简单的Java对象(POJO)进行开发,并且能够集成其他Java框架。Spring框架的核心特性包括依赖注入(Dependency Injection)、面向切面编程(Aspect-Oriented Programming)、控制反转(Inversion of Control)等。Spring框架的目标是简化企业应用程序开发,并提供一致性、高效性和可维护性。

Spring是一个开源的轻量级的Java开发框架。

目的:解决企业应用开发的复杂性
功能:使用基本的JavaBean代替EJB,并提供了更多的企业应用功能
范围:任何Java应用
简单来说,Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。
在这里插入图片描述

2、Spring的特点有哪些?或者优点有哪些?

  1. IOC(控制反转) 和 DI(依赖注入) 的支持
    Spring 的核心就是一个大的工厂容器,可以维护所有对象的创建和依赖关系,Spring 工厂用于生成 Bean,并且管理 Bean 的生命周期,实现高内聚低耦合的设计理念。
  2. AOP (面向切面编程)编程的支持
    Spring 提供了面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等切面功能。
  3. 声明式事务的支持
    支持通过配置就来完成对事务的管理,而不需要通过硬编码的方式,以前重复的一些事务提交、回滚的JDBC代码,都可以不用自己写了。
  4. 快捷测试的支持
    Spring 对 Junit 提供支持,可以通过注解快捷地测试 Spring 程序。
  5. 快速集成功能
    方便集成各种优秀框架,Spring 不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如:Struts、Hibernate、MyBatis、Quartz 等)的直接支持。
  6. 复杂API模板封装
    Spring 对 JavaEE 开发中非常难用的一些 API(JDBC、JavaMail、远程调用等)都提供了模板化的封装,这些封装 API 的提供使得应用难度大大降低。

3、详细介绍Spring的IOC和AOP(重点、核心!!!!)

Spring框架的两个核心特性是IOC(Inversion of Control,控制反转)和AOP(Aspect-Oriented Programming,面向切面编程)。

IOC(Inversion of Control,控制反转)
IOC是Spring框架的核心思想,也是Spring框架的最大特色之一。IOC的基本思想是将对象的创建和依赖关系的管理交给Spring容器来完成,开发人员只需要关注对象的使用,而不用关注对象的创建和管理。具体实现方式是通过依赖注入(Dependency Injection,DI)来完成,Spring容器会自动将需要依赖的对象注入到需要使用的对象中。
例如,我们创建一个UserService对象,它需要依赖UserDao对象,我们可以使用注解或者配置文件来告诉Spring容器需要注入哪个UserDao对象,Spring容器会自动将UserDao对象注入到UserService对象中。

AOP(Aspect-Oriented Programming,面向切面编程)
AOP是Spring框架的另一个核心特性,它提供了一种在应用程序中进行横向切面关注点分离的方法。AOP能够将应用程序中的横切关注点(例如事务管理、安全检查等)与业务逻辑代码分离开来,从而使得代码更加清晰、简洁、易于维护。
具体实现方式是通过定义切面(Aspect)和切点(Pointcut)来完成。切点表示在哪些地方插入切面,而切面则表示要执行的操作。例如,我们可以定义一个事务切面,用来在执行数据库操作时添加事务管理功能,这样就能够将事务管理与业务逻辑分离开来。

综上所述,IOC和AOP是Spring框架的核心特性,它们能够大大简化开发人员的工作,提高应用程序的可维护性和可扩展性。

4、 Spring有哪些模块?

Spring 框架是分模块存在,除了最核心的Spring Core Container是必要模块之外,其他模块都是可选,大约有 20 多个模块。
在这里插入图片描述
最主要的七大模块:

  1. Spring Core:Spring 核心,它是框架最基础的部分,提供 IOC 和依赖注入 DI 特性。
  2. Spring Context:Spring 上下文容器,它是 BeanFactory 功能加强的一个子接口。
  3. Spring Web:它提供 Web 应用开发的支持。
  4. Spring MVC:它针对 Web 应用中 MVC 思想的实现。
  5. Spring DAO:提供对 JDBC 抽象层,简化了 JDBC 编码,同时,编码更具有健壮性。
  6. Spring ORM:它支持用于流行的 ORM 框架的整合,比如:Spring + Hibernate、Spring + iBatis、Spring + JDO 的整合等。对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。
  7. Spring AOP:即面向切面编程,它提供了与 AOP 联盟兼容的编程实现。

5、说一说你对Spring容器的了解

Spring框架的核心是一个轻量级的IoC容器(Inversion of Control,控制反转容器)。Spring容器可以管理对象的生命周期、创建和管理对象、对象之间的依赖关系等等,使得开发人员可以更加专注于应用程序的业务逻辑实现,而不必关注底层的对象管理和依赖注入等问题。

Spring容器包括两种类型:BeanFactory和ApplicationContext

BeanFactory
BeanFactory是Spring框架最基本的容器,提供了最简单的对象工厂实现,通过XML配置文件或Java注解方式定义Bean的属性,它负责管理对象的创建、销毁、依赖关系等,但是它不支持AOP功能。

ApplicationContext
ApplicationContext是BeanFactory的子接口,也是Spring框架中最常用的容器。ApplicationContext除了BeanFactory的所有功能外,还提供了以下功能:

  • 支持AOP功能
  • 国际化支持
  • 事件传播和处理
  • Web应用程序支持
  • 安全性支持
  • 数据访问支持等等。

ApplicationContext有多种实现方式,包括:

ClassPathXmlApplicationContext:从类路径下加载XML配置文件。
FileSystemXmlApplicationContext:从文件系统加载XML配置文件。
AnnotationConfigApplicationContext:从Java配置类中加载Bean。

Spring容器的工作流程大致如下:

  1. 配置Spring容器,将需要管理的对象和其依赖关系定义在配置文件或Java注解中。
  2. 在程序中获取Spring容器实例。
  3. 从Spring容器中获取需要的Bean实例,Spring容器会自动管理Bean的生命周期,包括创建、依赖注入、初始化、调用业务逻辑等。
  4. 最后,在程序结束时,Spring容器会销毁所有被管理的Bean实例。

总之,Spring容器是Spring框架的核心,它能够帮助开发人员更加高效和便捷地管理对象和依赖关系。

6、说一说你对BeanFactory的了解

BeanFactory是一个类工厂,与传统类工厂不同的是,BeanFactory是类的通用工厂,它可以创建并管理各种类的对象。这些可被创建和管理的对象本身没有什么特别之处,仅是一个POJO,Spring称这些被创建和管理的Java对象为Bean。并且,Spring中所说的Bean比JavaBean更为宽泛一些,所有可以被Spring容器实例化并管理的Java类都可以成为Bean。

BeanFactory是Spring容器的顶层接口,Spring为BeanFactory提供了多种实现,最常用的是XmlBeanFactory。但它在Spring 3.2中已被废弃,建议使用XmlBeanDefinitionReader、DefaultListableBeanFactory替代。BeanFactory最主要的方法就是 getBean(String beanName),该方法从容器中返回特定名称的Bean。

Bean Factory与Factory Bean有什么区别?

相同点:都是用来创建对象的
不同点:使用BeanFactory创建对象的时候,必须遵循严格的生命周期,太复杂了,如果想要简单的自定义某个对象的创建,同时创建完成的对象想交给spring来管理,那么就必须实现FactoryBean接口了

  • isSingleton:是否是单例对象
  • getObjectType:获取返回对象的类型
  • getObject:自定义创建对象的过程(new,反射,动态代理)

7、说一说你对Spring IOC的理解

所谓的IOC(控制反转):就是由容器来负责控制对象的生命周期和对象间的关系。以前是我们想要什么,就自己创建什么,现在是我们需要什么,容器就给我们送来什么。

IoC(Inversion of Control)是控制反转的意思,这是一种面向对象编程的设计思想。在不采用这种思想的情况下,我们需要自己维护对象与对象之间的依赖关系,很容易造成对象之间的耦合度过高,在一个大型的项目中这十分的不利于代码的维护。IoC则可以解决这种问题,它可以帮我们维护对象与对象之间的依赖关系,降低对象之间的耦合度。

DI(Dependency Injection),DI是依赖注入的意思,它是IoC实现的实现方式,就是说IoC是通过DI来实现的。由于IoC这个词汇比较抽象而DI却更直观,所以很多时候我们就用DI来代替它,在很多时候我们简单地将IoC和DI划等号,这是一种习惯。而实现依赖注入的关键是IoC容器,它的本质就是一个工厂。

DI(依赖注入):指的是容器在实例化对象的时候把它依赖的类注入给它。有的说法IOC和DI是一回事,有的说法是IOC是思想,DI是IOC的实现。

使用IOC最主要的原因即使解耦,硬编码会造成对象间的过度耦合,使用IOC之后,我们可以不用关心对象间的依赖,专心开发应用就行。

在具体的实现中,主要由三种注入方式:

  1. 构造方法注入:就是被注入对象可以在它的构造方法中声明依赖对象的参数列表,让外部知道它需要哪些依赖对象。然后,IoC Service Provider会检查被注入的对象的构造方法,取得它所需要的依赖对象列表,进而为其注入相应的对象。构造方法注入方式比较直观,对象被构造完成后,即进入就绪状态,可以马上使用。
  2. setter方法注入:通过setter方法,可以更改相应的对象属性。所以,当前对象只要为其依赖对象所对应的属性添加setter方法,就可以通过setter方法将相应的依赖对象设置到被注入对象中。setter方法注入虽不像构造方法注入那样,让对象构造完成后即可使用,但相对来说更宽松一些, 可以在对象构造完成后再注入。
  3. 接口注入:相对于前两种注入方式来说,接口注入没有那么简单明了。被注入对象如果想要IoC Service Provider为其注入依赖对象,就必须实现某个接口。这个接口提供一个方法,用来为其注入依赖对象。IoC Service Provider最终通过这些接口来了解应该为被注入对象注入什么依赖对象。相对于前两种依赖注入方式,接口注入比较死板和烦琐。

总体来说,构造方法注入和setter方法注入因为其侵入性较弱,且易于理解和使用,所以是现在使用最多的注入方式。而接口注入因为侵入性较强,近年来已经不流行了。

8、Spring是如何管理Bean的?

什么是Bean管理?

  1. Spring创建对象
  2. Spring注入属性

Bean管理操作的两种方式

  1. 基于xml配置文件方式实现
    在这里插入图片描述
  2. 基于注解方式实现

Spring通过IoC容器来管理Bean,我们可以通过XML配置或者注解配置,来指导IoC容器对Bean的管理。因为注解配置比XML配置方便很多,所以现在大多时候会使用注解配置的方式。

在这里插入图片描述
使用set方法注入属性:
在这里插入图片描述
使用有参构造注入属性
在这里插入图片描述

以下是管理Bean时常用的一些注解:

  1. @ComponentScan用于声明扫描策略,通过它的声明,容器就知道要扫描哪些包下带有声明的类,也可以知道哪些特定的类是被排除在外的。
  2. @Component、@Repository、@Service、@Controller用于声明Bean,它们的作用一样,但是语义不同。@Component用于声明通用的Bean,@Repository用于声明DAO层的Bean,@Service用于声明业务层的Bean,@Controller用于声明视图层的控制器Bean,被这些注解声明的类就可以被容器扫描并创建。
  3. @Autowired、@Qualifier用于注入Bean,即告诉容器应该为当前属性注入哪个Bean。其中,@Autowired是按照Bean的类型进行匹配的,如果这个属性的类型具有多个Bean,就可以通过@Qualifier指定Bean的名称,以消除歧义。
  4. @Scope用于声明Bean的作用域,默认情况下Bean是单例的,即在整个容器中这个类型只有一个实例。可以通过@Scope注解指定prototype值将其声明为多例的,也可以将Bean声明为session级作用域、request级作用域等等,但最常用的还是默认的单例模式。
  5. @PostConstruct、@PreDestroy用于声明Bean的生命周期。其中,被@PostConstruct修饰的方法将在Bean实例化后被调用,@PreDestroy修饰的方法将在容器销毁前被调用。

9、介绍Bean的作用域有哪些?

默认情况下,Bean在Spring容器中是单例的,我们可以通过@Scope注解修改Bean的作用域。该注解有如下5个取值,它们代表了Bean的5种不同类型的作用域:

  1. singleton : 在Spring容器仅存在一个Bean实例,Bean以单实例的方式存在,是Bean默认的作用域。
  2. prototype : 每次从容器重调用Bean时,都会返回一个新的实例。
    在这里插入图片描述

以下三个作用域于只在Web应用中适用:

  1. request : 每一次HTTP请求都会产生一个新的Bean,该Bean仅在当前HTTP Request内有效。
  2. session : 同一个HTTP Session共享一个Bean,不同的HTTP Session使用不同的Bean。
  3. globalSession:同一个全局Session共享一个Bean,只用于基于Protlet的Web应用,Spring5中已经不存在了。

10、说一说Bean的生命周期

生命周期: 从对象创建到对象销毁的过程

Bean的生命周期

Bean的生命周期可以分为以下几个阶段:

实例化:当容器实例化Bean时,它会调用Bean的构造方法来创建Bean的实例。

属性注入:容器使用依赖注入将Bean所需的所有属性设置到Bean实例中。

BeanPostProcessor前置处理器的前置处理:在Bean实例化和初始化之间,Spring容器会执行所有注册的BeanPostProcessor前置处理器的before方法,可以在该方法中对Bean进行额外的处理,比如校验属性是否合法。

初始化:容器调用Bean的init-method方法或InitializingBean接口的afterPropertiesSet方法,完成Bean的初始化。

BeanPostProcessor后置处理器的后置处理:在Bean初始化之后,Spring容器会执行所有注册的BeanPostProcessor后置处理器的after方法,可以在该方法中对Bean进行额外的处理,比如包装Bean实例,生成代理等。

使用Bean:Bean实例化、属性注入和初始化完成之后,Bean可以被应用程序使用。

销毁:当容器关闭时,它会调用Bean的destroy-method方法或实现DisposableBean接口的destroy方法,完成Bean的销毁。

在以上的生命周期中,用户可以自定义的扩展点包括BeanPostProcessor前置处理器和后置处理器,以及定义init-method和destroy-method方法来完成Bean的初始化和销毁。

Spring容器管理Bean,涉及对Bean的创建、初始化、调用、销毁等一系列的流程,这个流程就是Bean的生命周期。整个流程参考下图:
在这里插入图片描述
这个过程是由Spring容器自动管理的,其中有两个环节我们可以进行干预。

  • 我们可以自定义初始化方法,并在该方法前增加@PostConstruct注解,届时Spring容器将在调用SetBeanFactory方法之后调用该方法。
  • 我们可以自定义销毁方法,并在该方法前增加@PreDestroy注解,届时Spring容器将在自身销毁前,调用这个方法。

11、Spring是怎么解决循环依赖的?

什么是循环依赖? 简单说就是自己依赖自己,或者和别的Bean相互依赖。

循环依赖是指两个或多个Bean之间互相依赖,形成了一个环形依赖关系。例如,Bean A依赖Bean B,Bean B又依赖Bean A。如果没有正确的处理循环依赖,会导致Bean的创建失败,从而影响整个应用程序的正常运行。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
只有单例的Bean才存在循环依赖的情况,原型(Prototype)情况下,Spring会直接抛出异常。原因很简单,AB循环依赖,A实例化的时候,发现依赖B,创建B实例,创建B的时候发现需要A,创建A实例……无限套娃,系统就会崩溃。

假如我们有两个bean,A 和 B。他们的代码简单如下:

@Bean
public class A {
    @Autowire
    private B b;
}
 
 
@Bean
public class B {
    @Autowire
    private A a;
}

也就是需要在A中注入B,在B中注入A,那么Spring在创建A的时候会出现这种现象:创建A实例后,在依赖注入时需要B,然后就去创建B,这时候发现又需要依赖注入 A ,这样就导致了循环依赖。(类似于死锁)在这里插入图片描述
Spring对循环依赖的解决方法可以概括为 用三级缓存方式达到Bean提前曝光的目的。
在这里插入图片描述
Spring创建的过程简单的可以简单概括为实例化——>依赖注入(属性赋值)——>初始化。而Spring解决循环依赖的方法就是在实例化之后,依赖注入之前,将实例化的对象放到缓存中进行提前曝光,后边的对象则在实例化前,先到缓存中查找有无对应的实例化对象即可。

具体分析:
Spring创建的过程简单的可以简单概括为实例化——>依赖注入(属性赋值)——>初始化。

注入就发生在第二步,属性赋值,结合这个过程,Spring 通过三级缓存解决了循环依赖:

  1. 一级缓存 : Map<String,Object> singletonObjects,单例池,用于保存实例化、属性赋值(注入)、初始化完成的 bean 实例。
  2. 二级缓存 : Map<String,Object> earlySingletonObjects,早期曝光对象,用于保存实例化完成的 bean 实例。
  3. 三级缓存 : Map<String,ObjectFactory<?>> singletonFactories,早期曝光对象工厂,用于保存 bean 创建工厂,以便于后面扩展有机会创建代理对象。
    在这里

三级缓存解决循环依赖的过程:

A实例的初始化过程:

  1. 创建A实例,实例化的时候把A对象⼯⼚放⼊三级缓存,表示A开始实例化了,虽然我这个对象还不完整,但是先曝光出来让大家知道。
  2. A注⼊属性时,发现依赖B,此时B还没有被创建出来,所以去实例化B。
  3. 同样,B注⼊属性时发现依赖A,它就会从缓存里找A对象。依次从⼀级到三级缓存查询A,从三级缓存通过对象⼯⼚拿到A,发现A虽然不太完善,但是存在,把A放⼊⼆级缓存,同时删除三级缓存中的A,此时,B已经实例化并且初始化完成,把B放入⼀级缓存。
  4. 接着A继续属性赋值,顺利从⼀级缓存拿到实例化且初始化完成的B对象,A对象创建也完成,删除⼆级缓存中的A,同时把A放⼊⼀级缓存。
  5. 最后,⼀级缓存中保存着实例化、初始化都完成的A、B对象。

为什么需要三级缓存,二级不行吗?

在Spring中,使用两级缓存是无法完全解决循环依赖问题的。因为,在循环依赖场景中,如果只有两级缓存,无法准确地判断哪个Bean应该被先创建,哪个Bean应该后创建。这样就有可能导致Bean创建过程中出现错误,或者出现死锁等问题。

因此,Spring使用了三级缓存来解决循环依赖问题。其中,第一级缓存singletonObjects保存已经创建好的Bean实例,第二级缓存earlySingletonObjects保存尚未完全创建好的Bean实例,第三级缓存singletonFactories保存Bean实例的创建工厂。通过这种方式,Spring能够在创建Bean实例的过程中正确地处理循环依赖关系,保证Bean实例的创建过程顺利进行。

具体来说,如果两级缓存无法解决循环依赖问题,那么三级缓存是更加完善的解决方案。通过三级缓存,Spring能够在Bean实例创建的各个阶段进行适当的处理,保证Bean实例创建过程的正确性和完整性,避免出现死锁等问题。

12、@Autowired和@Resource注解有什么区别?

  1. @Autowired是Spring提供的注解,@Resource是JDK提供的注解。
  2. @Autowired是只能按类型注入,@Resource默认按名称注入,也支持按类型注入。
  3. @Autowired按类型装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它required属性为false,如果我们想使用按名称装配,可以结合@Qualifier注解一起使用。@Resource有两个中重要的属性:name和type。name属性指定byName,如果没有指定name属性,当注解标注在字段上,即默认取字段的名称作为bean名称寻找依赖对象,当注解标注在属性的setter方法上,即默认取属性名作为bean名称寻找依赖对象。需要注意的是,@Resource如果没有指定name属性,并且按照默认的名称仍然找不到依赖对象时, @Resource注解会回退到按类型装配。但一旦指定了name属性,就只能按名称装配了。

13、Spring中默认提供的单例是线程安全的吗?

首先结论在这:Spring中的单例Bean不是线程安全的

因为单例Bean,是全局只有一个Bean,所有线程共享。如果说单例Bean,是一个无状态的,也就是线程中的操作不会对Bean中的成员变量执行查询以外的操作,那么这个单例Bean是线程安全的。比如Spring mvc 的 Controller、Service、Dao等,这些Bean大多是无状态的,只关注于方法本身。

假如这个Bean是有状态的,也就是会对Bean中的成员变量进行写操作,那么可能就存在线程安全的问题。

单例Bean线程安全问题怎么解决呢?

  1. 将Bean定义为多例,这样每一个线程请求过来都会创建一个新的Bean,但是这样容器就不好管理Bean,不能这么办。
  2. 在Bean对象中尽量避免定义可变的成员变量,削足适履了属于是,也不能这么干。
  3. 将Bean中的成员变量保存在ThreadLocal中,我们知道ThredLoca能保证多线程下变量的隔离,可以在类中定义一个ThreadLocal成员变量,将需要的可变成员变量保存在ThreadLocal里,这是推荐的一种方式。(采用这种方式)

14、说一说你对Spring AOP的理解

Spring AOP(Aspect-Oriented Programming,面向切面编程)是Spring框架中的一个重要模块,它提供了一种面向切面编程的方式,用来增强程序的功能和模块的可重用性。Spring AOP主要是通过动态代理来实现,可以在程序运行期间动态地将代码织入到指定的类或方法中,从而实现对其进行增强的目的。

在Spring AOP中,我们可以通过定义切面(Aspect)来实现对某个方法或一组方法的增强,切面可以看作是一组横切逻辑的集合,它包含了一些通知(Advice),如前置通知、后置通知、环绕通知、异常通知等。这些通知会在特定的切点(Join Point)处被执行,以达到对目标方法进行增强的效果。

Spring AOP的核心是切面、切点和通知。切面(Aspect)定义了横切逻辑,可以看作是一种类似于拦截器的机制。切点(Join Point)定义了哪些方法会被增强,通常是指定一个方法的执行位置。通知(Advice)则定义了在切点处执行的操作,如前置通知、后置通知、环绕通知、异常通知等。这些通知可以控制切点处的行为,从而实现对目标方法的增强。

Spring AOP提供了三种通知的方式:

1.前置通知(Before Advice):在目标方法执行之前执行。

2.后置通知(After Advice):在目标方法执行之后执行。

3.环绕通知(Around Advice):在目标方法执行前后都可以执行,可以控制目标方法的执行流程,如决定是否执行目标方法,或者在执行完目标方法后继续执行其他操作。

除此之外,还有异常通知和最终通知。异常通知(After Throwing Advice)在目标方法抛出异常时执行,最终通知(After Returning Advice)在目标方法执行结束后执行,无论目标方法是否抛出异常都会执行。

Spring AOP的优点在于它可以很好地实现横切逻辑的复用,避免了代码的重复编写,并且能够使程序结构更加清晰,易于维护。在Spring中,我们可以通过注解或XML配置的方式来定义切面、切点和通知,使得AOP的使用变得非常简单和方便。

15、请你说说AOP的应用场景与实现一个AOP场景

Spring AOP为IoC的使用提供了更多的便利,一方面,应用可以直接使用AOP的功能,设计应用的横切关注点,把跨越应用程序多个模块的功能抽象出来,并通过简单的AOP的使用,灵活地编制到模块中,比如可以通过AOP实现应用程序中的日志功能。另一方面,在Spring内部,一些支持模块也是通过Spring AOP来实现的,比如事务处理。从这两个角度就已经可以看到Spring AOP的核心地位了。
视频:狂神说
在这里插入图片描述

16、Spring AOP不能对哪些类进行增强?

  1. Spring AOP只能对IoC容器中的Bean进行增强,对于不受容器管理的对象不能增强。
  2. 由于CGLib采用动态创建子类的方式生成代理对象,所以不能对final修饰的类进行代理。

17、说说JDK 动态代理和 CGLIB 代理 ?为什么Spring还要使用JDK动态代理?

JDK动态代理和CGLIB代理是两种常见的Java代理模式,它们都可以在运行时生成代理类来实现对目标对象的代理。

JDK动态代理是利用Java反射机制实现的。它要求目标类必须实现一个或多个接口,代理对象会实现这些接口,并把所有方法的调用委托给一个InvocationHandler对象来处理。JDK动态代理的优点是实现简单,代理类不需要额外的依赖,缺点是要求目标类必须实现接口。

CGLIB代理则是使用字节码生成技术实现的。它可以代理没有实现接口的目标类,代理对象会继承目标类,并重写所有非final方法,把方法调用委托给一个MethodInterceptor对象来处理。CGLIB代理的优点是可以代理没有实现接口的目标类,缺点是实现相对复杂,代理类需要依赖CGLIB库。

在Spring中,通常会优先选择JDK动态代理,因为它要求目标类实现接口,可以更好地遵循面向接口编程的原则,也可以保证代理对象的类型是接口类型,不会出现类型不一致的问题。同时,JDK动态代理也比CGLIB代理更高效,因为它生成的代理类是基于接口的,调用方法时会直接调用目标对象的方法,不需要像CGLIB代理一样调用父类方法。

然而,在目标类没有实现接口或者只需要对一些特定方法进行代理的情况下,Spring也提供了CGLIB代理的支持,可以使用@EnableAspectJAutoProxy注解开启基于CGLIB代理的AOP功能。

18、Spring如何管理事务?

Spring为事务管理提供了一致的编程模板,在高层次上建立了统一的事务抽象。也就是说,不管是选择MyBatis、Hibernate、JPA还是Spring JDBC,Spring都可以让用户以统一的编程模型进行事务管理。

Spring支持两种事务编程模型:

1、编程式事务管理

Spring提供了TransactionTemplate模板,利用该模板我们可以通过编程的方式实现事务管理,而无需关注资源获取、复用、释放、事务同步及异常处理等操作。相对于声明式事务来说,这种方式相对麻烦一些,但是好在更为灵活,我们可以将事务管理的范围控制的更为精确。

2、声明式事务管理

Spring事务管理的亮点在于声明式事务管理,它允许我们通过声明的方式,在IoC配置中指定事务的边界和事务属性,Spring会自动在指定的事务边界上应用事务属性。相对于编程式事务来说,这种方式十分的方便,只需要在需要做事务管理的方法上,增加@Transactional注解,以声明事务特征即可。

1 基于注解方式。2 基于XML方式

在这里插入图片描述

19、Spring的事务传播方式有哪些?

在Spring中,事务传播(Transaction Propagation)是指在多个事务嵌套调用的过程中,事务如何进行传播和处理的机制。Spring提供了多种不同的事务传播方式,开发者可以根据实际需求选择合适的方式。

常用的Spring事务传播方式包括:

  1. REQUIRED:默认的事务传播方式,如果当前存在事务,则加入该事务,否则创建新的事务。这是最常用的一种传播方式,它保证了多个操作在同一事务中执行,具有完整性和一致性。
  2. REQUIRES_NEW:如果当前存在事务,则挂起该事务,创建新的事务,执行完毕后再恢复之前的事务。使用该传播方式可以避免事务嵌套的问题,但是会增加系统的开销。
  3. SUPPORTS:如果当前存在事务,则加入该事务,否则不使用事务。该传播方式适合于只读操作,可以提高性能。
  4. MANDATORY:如果当前存在事务,则加入该事务,否则抛出异常。该传播方式用于强制使用事务,如果调用方没有事务,则会抛出异常。
  5. NOT_SUPPORTED:如果当前存在事务,则挂起该事务,不使用事务,执行完毕后再恢复之前的事务。该传播方式适合于非事务性操作,可以提高性能。
  6. NEVER:如果当前存在事务,则抛出异常,否则不使用事务。该传播方式用于禁止使用事务,如果调用方有事务,则会抛出异常。
  7. NESTED:如果当前存在事务,则在该事务中创建一个嵌套事务,否则创建新的事务。该传播方式用于支持嵌套事务,但是只有在某些特定的数据库中才能使用。

在使用Spring事务管理时,需要根据实际业务场景选择合适的事务传播方式,以保证数据的一致性和完整性。

20、Spring的事务如何配置,常用注解有哪些?

事务的打开、回滚和提交是由事务管理器来完成的,我们使用不同的数据库访问框架,就要使用与之对应的事务管理器。在Spring Boot中,当你添加了数据库访问框架的起步依赖时,它就会进行自动配置,即自动实例化正确的事务管理器。

对于声明式事务,是使用@Transactional进行标注的。这个注解可以标注在类或者方法上。

  • 当它标注在类上时,代表这个类所有公共(public)非静态的方法都将启用事务功能。
  • 当它标注在方法上时,代表这个方法将启用事务功能。

另外,在@Transactional注解上,我们可以使用isolation属性声明事务的隔离级别,使用propagation属性声明事务的传播机制。

21、Spring事务的实现原理是什么?

在使用Spring框架的时候,可以有两种事务的实现方式,一种是编程式事务,有用户自己通过代码来控制事务的处理逻辑,还有一种是声明式事务,通过@Transactional注解来实现。

其实事务的操作本来应该是由数据库来进行控制,但是为了方便用户进行业务逻辑的操作,spring对事务功能进行了扩展实现,一般我们很少会用编程式事务,更多的是通过添加@Transactional注解来进行实现,当添加此注解之后事务的自动功能就会关闭,有spring框架来帮助进行控制。

其实事务操作是AOP的一个核心体现,当一个方法添加@Transactional注解之后,spring会基于这个类生成一个代理对象,会将这个代理对象作为bean,当使用这个代理对象的方法的时候,如果有事务处理,那么会先把事务的自动提交给关系,然后去执行具体的业务逻辑,如果执行逻辑没有出现异常,那么代理逻辑就会直接提交,如果出现任何异常情况,那么直接进行回滚操作,当然用户可以控制对哪些异常进行回滚操作。

22、Spring中有哪些常用的注解?

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。

容器:

  • @Component:表示一个带注释的类是一个“组件”,成为Spring管理的Bean。当使用基于注解的配置和类路径扫描时,这些类被视为自动检测的候选对象。同时@Component还是一个元注解。
  • @Service:组合注解(组合了@Component注解),应用在service层(业务逻辑层)。
  • @Repository:组合注解(组合了@Component注解),应用在dao层(数据访问层)。
  • @Autowired:Spring提供的工具(由Spring的依赖注入工具(BeanPostProcessor、BeanFactoryPostProcessor)自动注入)。
  • @Qualifier:该注解通常跟 @Autowired 一起使用,当想对注入的过程做更多的控制,@Qualifier 可帮助配置,比如两个以上相同类型的 Bean 时 Spring 无法抉择,用到此注解。
  • @Configuration:声明当前类是一个配置类(相当于一个Spring配置的xml文件)。
  • @Value:可用在字段,构造器参数跟方法参数,指定一个默认值,支持 #{} 跟 ${} 两个方式。一般将 SpringbBoot 中的 application.properties 配置的属性值赋值给变量。
  • @Bean:注解在方法上,声明当前方法的返回值为一个Bean。返回的Bean对应的类中可以定义init()方法和destroy()方法,然后在@Bean(initMethod=”init”,destroyMethod=”destroy”)定义,在构造之后执行init,在销毁之前执行destroy。
  • @Scope:定义我们采用什么模式去创建Bean(方法上,得有@Bean)其设置类型包括:Singleton 、Prototype、Request 、 Session、GlobalSession。

AOP:

  • @Aspect:声明一个切面(类上) 使用@After、@Before、@Around定义建言(advice),可直接将拦截规则(切点)作为参数。@After :在方法执行之后执行(方法上)。@Before: 在方法执行之前执行(方法上)。@Around: 在方法执行之前与之后执行(方法上)。@PointCut: 声明切点 在java配置类中使用@EnableAspectJ***注解开启Spring对AspectJ代理的支持(类上)。

事务:

@Transactional:在要开启事务的方法上使用@Transactional注解,即可声明式开启事务。

23、Spring中使用了什么设计模式?(重要)

参考博客(掌握)

  • 工厂模式 : Spring 容器本质是一个大工厂,使用工厂模式通过 BeanFactory、ApplicationContext 创建 bean 对象。
  • 代理模式 : Spring AOP 功能功能就是通过代理模式来实现的,分为动态代理和静态代理。
  • 单例模式 : Spring 中的 Bean 默认都是单例的,这样有利于容器对Bean的管理。
  • 模板模式 : Spring 中 JdbcTemplate、RestTemplate 等以 Template结尾的对数据库、网络等等进行操作的模板类,就使用到模板模式。
  • 观察者模式: Spring 事件驱动模型就是观察者模式很经典的一个应用。
  • 适配器模式 :Spring AOP 的增强或通知 (Advice) 使用到了适配器模式、Spring MVC 中也是用到了适配器模式适配 Controller。
  • 策略模式:Spring中有一个Resource接口,它的不同实现类,会根据不同的策略去访问资源。

24、Spring有哪些自动装配的方式?

什么是自动装配?

Spring IOC容器知道所有Bean的配置信息,此外,通过Java反射机制还可以获知实现类的结构信息,如构造方法的结构、属性等信息。掌握所有Bean的这些信息后,Spring IOC容器就可以按照某种规则对容器中的Bean进行自动装配,而无须通过显式的方式进行依赖配置。

Spring提供的这种方式,可以按照某些规则进行Bean的自动装配,元素提供了一个指定自动装配类型的属性:autowire=“<自动装配类型>”。

Spring中有以下几种自动装配的方式:

  • byName自动装配:Spring会根据bean的属性名称和配置文件中的bean的名称进行自动匹配,如果匹配成功,则将该属性注入到bean中。
  • byType自动装配:Spring会根据bean的属性类型和配置文件中的bean的类型进行自动匹配,如果匹配成功,则将该属性注入到bean中。如果有多个bean符合类型,则会抛出异常。
  • constructor自动装配:Spring会根据构造函数的参数类型和配置文件中的bean的类型进行自动匹配,如果匹配成功,则将该构造函数注入到bean中。
  • autowire注解自动装配:通过使用@Autowired注解,Spring会根据bean的类型进行自动匹配,并将匹配成功的bean注入到属性中。如果有多个bean符合类型,则可以使用@Qualifier注解指定具体的bean。
  • Java配置类中的自动装配:通过@Configuration注解的Java配置类中使用@Bean注解声明bean,Spring会根据bean的类型自动匹配并注入到其他bean中。

需要注意的是,自动装配只能在同一个ApplicationContext中的bean之间进行,如果要在不同的ApplicationContext中进行自动装配,则需要使用Spring的集成技术,如RMI或JMS等。

25、@Autowired的实现原理是什么?

实现@Autowired的关键是:AutowiredAnnotationBeanPostProcessor

在Bean的初始化阶段,会通过Bean后置处理器来进行一些前置和后置的处理。实现@Autowired的功能,也是通过后置处理器来完成的。这个后置处理器就是AutowiredAnnotationBeanPostProcessor。

Spring在创建bean的过程中,最终会调用到doCreateBean()方法,在doCreateBean()方法中会调用populateBean()方法,来为bean进行属性填充,完成自动装配等工作。

在populateBean()方法中一共调用了两次后置处理器,第一次是为了判断是否需要属性填充,如果不需要进行属性填充,那么就会直接进行return,如果需要进行属性填充,那么方法就会继续向下执行,后面会进行第二次后置处理器的调用,这个时候,就会调用到AutowiredAnnotationBeanPostProcessor的postProcessPropertyValues()方法,在该方法中就会进行@Autowired注解的解析,然后实现自动装配。

26、Spring AOP和AspectJ AOP有什么区别?

Spring AOP是属于运行时增强,而AspectJ是编译时增强。Spring AOP基于代理(Proxying),而AspectJ基于字节码操作(Bytecode Manipulation)。

Spring AOP已经集成了AspectJ,AspectJ应该算得上是Java生态系统中最完整的AOP框架了。AspectJ相比于Spring AOP功能更加强大,但是Spring AOP相对来说更简单。

如果我们的切面比较少,那么两者性能差异不大。但是,当切面太多的话,最好选择AspectJ,它比SpringAOP快很多。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值