Spring使用细节

https://www.yuque.com/matteo-irbtp/qqmkoc/yn2ybg

Spring原理详解

说说你对Spring的理解或者是简单说一下Spring的原理

首先从什么是Spring,它的优缺点是什么出发:

Spring是用于简化企业应用程序开发的一个轻量级IoC与AOP容器框架,为Java应用程序提供基础性服务,使得开发者只需要关心业务需求。
Spring的优缺点简单来说有以下这些:
(优点)
(方便集成优秀框架)在实际开发中,通常服务器端采用表现层(web)、业务逻辑层(service)、持久层(dao)这种三层体系架构进行项目搭建,而Spring 对每一层都提供了集成支持,在表现层提供了与 SpringMVC框架的整合,在业务逻辑层可以使用AOP框架进行管理事务和记录日志等,在持久层可以整合 Hibemate 和 JdbcTemplate 、Mybatis 等技术。
(降低组件间的耦合) DI机制将对象之间的依赖关系交由Spring框架处理,减低组件的耦合性;
(提供AOP技术进行切面编程)支持将一些通用任务,如安全、事务、日志、权限等进行集中式管理,从而提供更好的复用;
(声明式事务的支持)只需要通过配置就可以完成对事务的管理,而无须手动编程。
(缺点)
(使用门槛升高)入门学习Spring需要较长时间;以及对过时技术兼容,导致复杂度升高
集成第三方优秀工具时需要考虑兼容性;集成过多框架会导致系统启动慢,不具备热部署功能,完全依赖虚拟机或Web服务器的热部署。

然后谈谈对IOC 与AOP的理解?

IOC与AOP是Spring中的两大核心要点
什么是IOC 控制反转?
简单来说 IOC(Inversion of Control)控制反转,指的是将对象的控制权转移给Spring框架,由 Spring 来负责控制对象的生命周期(比如创建、销毁)和对象间的依赖关系。
通俗点来说,以前我们创建对象的时机和主动权都是由自己把控的,如果在一个对象中使用另外的对象,就必须主动通过new指令去创建所依赖对象,有的依赖在使用完后还需要销毁(比如Connection等),并且在销毁对象之前对象始终会和其他接口或类耦合起来,这样会给程序带来很大的耦合度,增大程序的维护成本。
而 IOC 控制反转则是有专门的容器来帮忙创建对象,将所有的类信息都在 Spring 容器中登记,当需要某个对象时,不需要自己主动去 new ,只需告诉 Spring 容器要创建哪个对象,然后 在Spring 系统运行到适当的时机,就会把你想要的对象主动给你。
也就是说,对于某个具体的对象而言,以前是由自己控制它所引用对象的生命周期,而在IOC中,所有的对象都被 Spring 控制,控制对象生命周期的不再是引用它的对象,而是Spring容器,由 Spring 容器帮我们创建、查找及注入依赖对象的属性,而引用对象只是被动的接受依赖对象,这就叫控制反转。
什么是依赖注入?
IoC 的一个核心要点就是创建bean的过程中,动态的向该个对象提供它所需要的其他对象(即提供该对象需要的属性对象),这一点是通过DI(Dependency Injection,依赖注入)来实现的,即应用程序在运行时依赖 IoC 容器来动态注入对象所需要的外部依赖属性。
而 Spring 的 DI 具体又是通过反射实现注入的,因为反射允许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性。

IOC 的大概原理?
Spring 的 IoC 的实现原理就是工厂模式加反射机制,而在 Spring 容器中,Bean 对象如何注册到 IoC 容器,以及Bean对象的加载、实例化、初始化等过程是一个复杂的过程,在下面的题目中有详细的讲解;

什么是AOP?
Java是一门OOP面向对象的语言,允许开发者定义纵向的关系,但并不适用于定义横向的关系,会导致大量代码的重复,而不利于各个模块的重用
而AOP是面向切面编程的,可以作为面向对象的一种补充,用来将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect),减少系统中的重复代码,降低了模块间的耦合度,提高系统的可维护性。一般可用于权限认证、日志、事务处理。

Spring AOP实现的大概原理?
一般的AOP框架的实现关键在于代理模式,而代理模式有又静态代理和动态代理。
静态代理的代表为AspectJ;动态代理则以Spring AOP为典型代表。
(1)AspectJ静态代理,也称为编译时增强,这种实现方式的AOP框架会在编译阶段生成AOP代理类,并将AspectJ(切面)织入到Java字节码中,这样在运行的时候就是增强之后的AOP对象。
(2)Spring中 AOP框架的实现使用的是动态代理模式,所谓的动态代理就是说AOP框架不会去修改字节码,而是每次运行时在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,而且还能回调原对象的方法。
Spring AOP框架中的动态代理主要有两种实现方式,JDK动态代理和CGLIB动态代理:
① JDK动态代理只提供接口的代理,不支持类的代理,要求被代理类实现接口。
② 如果被代理类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。
同理关于这两种代理实现的具体原理,下面的题目中会有详细介绍;

到这里Spring的大致原理就无了…..

BeanFactory和ApplicationContext有什么区别?

是什么?

这两个东西都是Spring的两大核心容器接口。在Spring的主程序中有一个SpringApplication.run方法,它的返回值就是一个ApplicationContext接口的实现类,通过按住CTRL+ALT+U查看这个ApplicationContext接口的继承关系可以发现最终ApplicationContext这个接口最终也会继承于BeanFactory接口;
也就是说,BeanFactory是Spring里面最底层的接口,是IoC的核心,给具体的IOC容器的实现类提供了规范,包含了各种Bean的定义、加载、实例化,依赖注入和生命周期管理。实际上控制反转、基本的依赖注入、Bean生命周期的各种功能等都可以由BeanFactory的实现类来提供。
ApplicationContext 的实现都[组合] 了BeanFactory的功能。比如说我们常使用的获取容器中注册对象的方法,context.getBean方法;该方法最终其实就是调用BeanFactory的getBean方法。换句话来说就是ApplicationContext扩展BeanFactory功能的方式是内部组合了一个BeanFactory对象(成员变量),通过调用该对象来使用BeanFactory的功能来达到扩展目的。

ApplicationContext相比BeanFactory扩展了什么功能

ApplicationContext在BeanFactory的基础上多实现了四个接口:
MessageSource: 支持国际化功能,支持多种语言
ResourcePatternResolver: 通配符匹配资源路径
EnvironmentCapable: 读取环境信息,系统环境变量,.properties、.application.yml等配置文件中的值
ApplicationEventPublisher: 可以用来发布事件对象

其他区别

何时加载bean实例

①BeanFactroy采用的是延迟加载形式(比较抽象)来注入Bean的,只有在使用到某个Bean(调用getBean()方法)时,才对该Bean进行加载实例化。但是这样一来,我们就有可能不能提前发现一些存在的Spring的配置问题。如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常。
②ApplicationContext,它是在容器启动时就一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误,这样有利于检查所依赖属性是否注入。
③ApplicationContext启动后预载入所有的单实例Bean,所以在运行的时候速度比较快,因为它们已经创建好了。相对于BeanFactory,ApplicationContext 唯一的不足是占用内存空间,当应用程序配置Bean较多时,程序启动较慢。

是否支持扩展

BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册

创建方式

BeanFactory通常以编程的方式被创建,ApplicationContext还能以声明的方式创建,如使用ContextLoader。

BeanFactory和FactoryBean有什么区别?

BeanFactory是接口,提供了IOC容器最基本的形式,给具体的IOC容器的实现提供了规范;
而FactoryBean也是接口,它为IOC容器中Bean的实例化提供了更加灵活的方式,FactoryBean在IOC容器的基础上给Bean的实现加上了一个简单工厂模式和装饰模式,让我们可以在getObject()方法中灵活配置对象实现的具体逻辑。
但是BeanFactory是个Factory,也就是IOC容器或对象工厂,FactoryBean是个Bean。在Spring中,所有的Bean都是由BeanFactory(也就是IOC容器)来进行管理的。但对FactoryBean而言,这个Bean不是简单的Bean,而是一个能生产或者修饰对象生成的工厂Bean,它的实现与设计模式中的工厂模式和修饰器模式类似 。

在整个IOC 创建对象的过程中,一般创建对象的时候都是用BeanFactory接口来创建的,但是在Spring容器中还提供了一堆的FactoryBean接口实现来达到创建对象的目的。

比如说,一开始我们创建对象的时候可能会有一个模板(BeanFactory),这就意味着我们创建出来的对象都是一样的;当我们想要创建一些不一样的对象时,不能通过再次创建模板的方式去创建不一样的对象,这样做的话,当我们不一样的对象有点多或者每种对象只创建一个的时候,你这么多的模板就失去了意义。这个时候就可以使用FactoryBean接口来创建对象,而使用这种方式在创建对象的时候都是调用getObject方法,在该方法内部就可以实现各种各样不同逻辑的对象,所以最终调用getObject方法返回的对象就是我们想要的“不一样的对象”,而不是通过BeanFactory创建出来的标准的对象;
也就是说不管是BeanFactory还是FactoryBean,都是用来创建对象的,但是BeanFactory更多是像一个模板的工厂,里面的对象都是按照模板创建出来的,而FactoryBean是可以按照自己不一样的需求来创建对象的,这也是它们两的一个区别。

什么是Spring的内部bean?什么是Spring inner beans?

在Spring框架中,当一个bean仅被用作另一个bean的属性时,它能被声明为一个内部bean。
内部bean可以用setter注入“属性”和构造方法注入“构造参数”的方式来实现,内部bean通常是匿名的,它们的Scope一般是prototype。

Spring基于xml注入bean的几种方式

参考文档:https://blog.csdn.net/a745233700/article/details/89307518
平时我们编写Java代码时,当某个类中需要调用(或依赖)其它类的方法(或者该类中有一个属性是其他类的对象)时,通常是通过new一个依赖类的实例对象再调用该实例的方法,这种开发存在的问题是new出来的的类实例不好进行统一的bean管理。
所以Spring就提出了依赖注入的思想,即依赖类不由程序员实例化,而是通过Spring容器帮我们new指定实例并且将实例注入到需要该对象的类中。
依赖注入的另一种说法是”控制反转”,通俗的理解是:平常我们new一个实例,这个实例的控制权是我们程序员。而控制反转是指new实例工作不由我们程序员来做而是交给Spring容器来做。
Spring中有很多种依赖注入的方式,主要分为通过xml进行IOC配置的方式或者通过配置类的方式;
这里主要介绍以下几种关于Spring通过xml进行IOC配置注入的方式,

  • set()方法注入,需要被注入的类中声明set方法;
  • 构造器注入:①通过index设置参数的位置;②通过type设置参数类型;
  • 静态工厂注入,就是通过调用静态工厂的方法来获取自己需要的对象。
  • 实例工厂,实例工厂的意思是获取对象实例的方法不是静态的,所以需要首先new工厂类,再调用普通的实例方法。

在 Spring 中如何注入一个 java 集合?

  1. list类型用于注入一列值,允许有相同的值。
  2. set 类型用于注入一组值,不允许有相同的值。
  3. map类型用于注入一组键值对,键和值都可以为任意类型。
  4. 类型用于注入一组键值对,键和值都只能为 String 类型。

image.png
image.png

Spring的自动装配

Spring的自动装配其实就是给bean对象的属性自动赋值的意思。

(1)在Spring框架xml配置中共有几种自动装配:

如果我们的bean对象是通过spring的xml配置文件进行注册的花, 标签的 autowire 参数可以控制 bean 自动装配的方式

  1. <bean id="person" class="*.Person" autowire="no">
  2. ...
  3. </bean>
  • default - 默认的方式和 “no” 方式一样
  • no - 默认的方式是不进行自动装配的,通过手动设置标签的ref参数来进行装配bean的属性。
  • byName - 通过bean的名称进行自动装配,如果一个bean的 property标签 与另一bean 的name 相同,就进行自动装配。
  • byType - 通过参数的类型进行自动装配。
  • constructor - 利用构造函数进行装配,并且构造函数的参数通过byType进行装配。
  • autodetect:自动探测,如果有构造方法,通过 construct的方式自动装配,否则使用 byType的方式自动装配。


2)基于注解的自动装配方式

使用@Autowired、@Resource等注解来自动装配指定的bean。
在使用@Autowired注解之前需要在Spring配置文件中配置相应的属性对象。在启动spring IoC容器时,容器自动装载了一个AutowiredAnnotationBeanPostProcessor后置处理器,该后置处理器的作用是当IOC 容器扫描到@Autowied、@Resource或@Inject的bean对象或方法时,就会在IoC容器自动查找属性需要的bean,并装配给该对象的属性。

@Autowired的查找规则
在使用@Autowired时,首先在容器中查询对应类型的bean:
如果查询结果刚好为一个,就将该bean装配给@Autowired指定的数据;
如果查询的结果不止一个,那么@Autowired会根据名称来查找;
如果上述查找的结果为空,那么会抛出异常。解决方法时,使用required=false。

@Autowired和@Resource之间的区别
(1) @Autowired默认是按照类型装配注入的,默认情况下它要求依赖对象必须存在(可以设置它required属性为false)。
(2) @Resource默认是按照名称来装配注入的,只有当找不到与名称匹配的bean才会按照类型来装配注入。

自动装配有哪些局限性?

  1. 仍需用和配置来定义依赖,这意味着总要重写自动装配。
  2. 不能自动装配简单的属性,如基本数据类型,String 字符串和类。
  3. 模糊特性 :自动装配不如显式装配精确,如果有可能,建议使用显式装配。

什么是Spring的依赖注入?

IoC控制反转可以用不同的方式来实现,其主要实现方式有两种:依赖注入和依赖查找;
相对于IoC而言,依赖注入(DI)更加准确地描述了IoC的设计理念。所谓依赖注入(Dependency Injection),即组件之间的依赖关系由容器在应用系统运行期来决定,也就是由容器动态地将某种依赖关系的目标对象实例注入到应用系统中的各个关联的组件之中。组件不做定位查询,只提供普通的Java方法让容器去决定依赖关系。

依赖注入的基本原则

应用组件不应该负责查找资源或者其他依赖的协作对象
配置对象的工作应该由IoC容器负责,“查找资源”的逻辑应该从应用组件的代码中抽取出来,交给IoC容器负责。
容器全权负责组件的装配,它会把符合依赖关系的对象通过属性(JavaBean中的setter)或者是构造器传递给需要的对象。

依赖注入有什么优势

依赖注入之所以更流行是因为它是一种更可取的方式:让容器全权负责依赖查询,受管组件只需要暴露JavaBean的setter方法或者带参数的构造器或者接口,使容器可以在初始化时组装对象的依赖关系。其与依赖查找方式相比,主要优势为:
查找定位操作与应用代码完全无关。
不依赖于容器的API,可以很容易地在任何容器以外使用应用对象。
不需要特殊的接口,绝大多数对象可以做到完全不必依赖容器。

有哪些不同类型的依赖注入实现方式?

依赖注入是时下最流行的IoC实现方式,依赖注入分为接口注入(Interface Injection),Setter方法注入(Setter Injection)和构造器注入(Constructor Injection)三种方式。
其中接口注入由于在灵活性和易用性比较差,现在从Spring4开始已被废弃。

构造器依赖注入:构造器依赖注入通过容器触发一个类的构造器来实现的,该类有一系列参数,每个参数代表一个对其他类的依赖。构造方法注入需要在配置文件中加入bean标签配置,其中的constructor-arg 代表的是构造函数的参数,一般我们常用name来指定参数名,当然,我们要得到参数也有其他方法,比如:
type 根据类型来赋值,但是有相同的类型就不行了;
index 根据下标来赋值,但是参数多的话,你不一定会记得每个下标的值是什么;
name 用name的话spring会有参数提示,这样更适合我们的习惯;
value来指定该参数的值,当然,当你的值是对象时,你就需要重新在spring容器里面拥有这个对象再去用它,譬如我们的Date类,你就需要先创建bean对象再使用!
image.png

Setter方法注入:Setter方法注入是容器通过调用无参构造器或无参static工厂 方法实例化bean之后,调用该bean的setter方法,即实现了基于setter的依赖注入。
image.png
两种依赖方式都可以使用,构造器注入和Setter方法注入。最好的解决方案是用构造器参数实现强制依赖,setter方法实现可选依赖。

Spring中bean的Scope作用域

(1)singleton:默认作用域,单例bean,每个容器中只有一个bean的实例。
(2)prototype:为每一个bean请求创建一个实例。
(3)request:为每一个request请求创建一个实例,在请求完成以后,bean会失效并被垃圾回收器回收。
(4)session:与request范围类似,同一个session会话共享一个实例,不同会话使用不同的实例。
(5)global-session:全局作用域,所有会话共享一个实例。如果想要声明让所有会话共享的存储变量的话,那么这全局变量需要存储在global-session中。

Spring框架中的Bean是线程安全的么?如果线程不安全,那么如何处理?

Spring容器本身并没有提供Bean的线程安全策略,因此可以说Spring容器中的Bean本身不具备线程安全的特性,但是是否会引发线程安全问需要要结合Bean的作用域来讨论:
(1)对于prototype作用域的多例Bean,每次都创建一个新对象,线程之间不存在Bean对象的共享,因此不会有线程安全问题。
(2)对于singleton作用域的单例Bean,所有的线程都共享一个单例的Bean对象,因此是有可能存在线程安全问题的。
如果单例Bean是一个无状态Bean,也就是线程中的操作不会对Bean的成员执行查询以外的操作(比如不存在修改对象内部属性值),那么这个单例Bean是线程安全的。
比如Controller类、Service类和Dao等,这些Bean大多是无状态的,只关注于方法本身。
对于有状态的bean(比如Model和View),就需要自行保证线程安全,最简单的解决办法就是将有状态的bean的作用域由“singleton”改为“prototype”。
也可以采用ThreadLocal(“空间换时间”)解决线程安全问题,为每个线程提供一个独立的变量副本,不同线程只操作自己线程的副本变量。
所以 spring 管理的 bean 的线程安全跟 bean 的创建的作用域和 bean 所在的使用环境是否存在竞态条件有关,spring 并不能保证 bean 的线程安全。

Spring如何处理线程并发问题?

一般情况下,只有无状态的Bean才可以在多线程的环境下共享。在Spring中,绝大部分Bean都可以声明为singleton作用域。因为Spring对一些Bean中非线程安全状态采用ThreadLocal进行处理,解决线程安全问题。
ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。同步机制采用了“时间换空间”的方式,仅提供一份变量,不同的线程在访问前需要获取锁,没获得锁的线程则需要排队。而ThreadLocal采用了“空间换时间”的方式。
ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。

Spring容器的启动流程?

https://www.yuque.com/matteo-irbtp/xniix0/trp1kb

Spring Bean的生命周期?

参考:https://blog.csdn.net/w_linux/article/details/80086950?
一般来说,Spring Bean的生命周期有四个阶段:
实例化 Instantiation —> 属性赋值 Populate —> 初始化 Initialization —> 销毁 Destruction
具体来说,一个bean的生命周期主要经过以下几个阶段:

image.png
1、实例化bean
对于在BeanFactory容器中的对象,当用户向容器请求获取一个尚未初始化的bean时,或容器初始化bean的时候需要注入另一个尚未初始化的bean属性时,容器就会调用createBean方法进行bean的实例化。
对于在ApplicationContext容器中的对象,当容器启动结束后,通过获取BeanDefinition对象中的信息,实例化所有的bean。
2、bean的属性赋值(依赖注入)
实例化后的bean对象会被封装在一个BeanWrapper对象中,紧接着,Spring根据BeanDefinition中的信息 以及 通过BeanWrapper提供的设置属性的接口完成属性设置与依赖注入。
3、查看bean是否实现xxxAware接口
相应的属性注入之后,Spring会检测该对象是否实现了xxxAware接口,通过Aware类型的接口,可以让我们拿到Spring容器的一些资源:
①如果这个Bean实现了BeanNameAware接口,会调用它实现的setBeanName(String beanId)方法,传入Bean的名字;
②如果这个Bean实现了BeanClassLoaderAware接口,调用setBeanClassLoader()方法,传入ClassLoader对象的实例。
②如果这个Bean实现了BeanFactoryAware接口,会调用它实现的setBeanFactory()方法,传递的是Spring工厂自身。
③如果这个Bean实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文容器对象;
4、BeanPostProcessor的前置处理方法
调用完xxxAware接口中的方法之后,如果还想对Bean进行一些自定义的前置处理,那么可以让Bean实现了BeanPostProcessor接口,之后将会调用postProcessBeforeInitialization(Object obj, String s)方法。
5、InitializingBean
如果Bean实现了InitializingBean接口,那么之后将紧接着执行afeterPropertiesSet()方法。
6、init-method
如果Bean在Spring配置文件中配置了@Bean注解的 init-method 属性(或者是配置文件中为该bean配置了该属性),之后则会自动调用其配置的初始化方法。
7、BeanPostProcessor的后置处理方法
如果这个Bean实现了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法;由于这个方法是在Bean初始化结束时调用的,所以可以被应用于内存或缓存技术;

以上几个步骤完成后,Bean就已经被正确创建了,之后就可以使用这个Bean了。

8、DisposableBean
当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用其实现的destroy()方法;
9、destroy-method
最后,如果这个Bean的Spring配置中配置了@Bean注解的destroy-method属性(或者是配置文件中为该bean配置了该属性),会自动调用其配置的销毁方法。

一个程序例子:
xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xmlns="http://www.springframework.org/schema/beans"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans.xsd
  7. http://www.springframework.org/schema/context
  8. http://www.springframework.org/schema/context/spring-context.xsd">
  9. <!-- init-method:指定初始化的方法
  10. destroy-method:指定销毁的方法 -->
  11. <bean id="myTestBean" class="com.mdy.test.MyTestBean" init-method="initMyTestBean" destroy-method="destroyMyTestBean">
  12. <property name="name" value="li-san"></property>
  13. </bean>
  14. </beans>
  1. package com.mdy.test;
  2. import com.mdy.service.UserService;
  3. import org.springframework.beans.BeansException;
  4. import org.springframework.beans.factory.BeanNameAware;
  5. import org.springframework.beans.factory.DisposableBean;
  6. import org.springframework.beans.factory.InitializingBean;
  7. import org.springframework.beans.factory.config.BeanPostProcessor;
  8. import org.springframework.context.ApplicationContext;
  9. import org.springframework.context.support.AbstractApplicationContext;
  10. import org.springframework.context.support.ClassPathXmlApplicationContext;
  11. public class MyTest {
  12. public static void main(String[] args) {
  13. ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
  14. MyTestBean myTestBean = (MyTestBean) context.getBean("myTestBean");
  15. //Bean的使用
  16. myTestBean.test();
  17. System.out.println("bean对象...."+myTestBean);
  18. //关闭容器
  19. ((AbstractApplicationContext) context).close();
  20. }
  21. }
  22. class MyTestBean implements BeanPostProcessor, BeanNameAware , DisposableBean ,InitializingBean {
  23. private String name;
  24. public void test(){
  25. System.out.println("MyTestBean的使用,,,test方法的执行...");
  26. }
  27. public void initMyTestBean(){
  28. System.out.println("自己定义的initMyTestBean方法执行....");
  29. }
  30. public void destroyMyTestBean(){
  31. System.out.println("自己定义的destroyMyTestBean方法执行....");
  32. }
  33. public String getName() {
  34. return name;
  35. }
  36. public void setName(String name) {
  37. System.out.println("name属性赋值.....name:"+name);
  38. this.name = name;
  39. }
  40. public MyTestBean() {
  41. System.out.println("MyTestBean构造方法执行.....");
  42. }
  43. @Override
  44. public void setBeanName(String name) {
  45. System.out.println("BeanNameAware接口的setBeanName方法执行.....name: "+name);
  46. }
  47. @Override
  48. public void destroy() throws Exception {
  49. System.out.println("DisposableBean接口的destroy方法...");
  50. }
  51. @Override
  52. public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
  53. System.out.println(beanName+"BeanPostProcessor接口的postProcess...Before...Initialization方法");
  54. return null;
  55. }
  56. @Override
  57. public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
  58. System.out.println(beanName+"BeanPostProcessor接口的postProcess...After...Initialization方法");
  59. return null;
  60. }
  61. @Override
  62. public void afterPropertiesSet() throws Exception {
  63. System.out.println("InitializingBean接口的afterPropertiesSet方法执行.....");
  64. }
  65. }

image.png

Spring Bean的加载过程?容器启动、获取Bean、创建Bean

https://www.yuque.com/matteo-irbtp/xniix0/ahumu7

Spring如何解决循环依赖问题

分析过程:

1、什么是循环依赖

类与类之间的依赖关系形成了闭环,就会导致循环依赖问题的产生。
比如ObjectA类依赖了ObjectB类,而ObjectB类又依赖了ObjectA类,这样就形成了循环依赖问题。image.png
Spring中要解决循环依赖有两个问题:死循环创建以及AOP代理;
要知道当我们去bean工厂获取一个bean的时候,这里是获取ObjectA类对象a,bean工厂首先会尝试去缓存中获取这ObjectA类对象a,如果缓存中没有的话就去创建一个ObjectA类对象a,;
即是说,调用getBean方法获取一个ObjectA类对象a的时候是先去单例池中获取,单例池中有该对象就直接用这个对象,如果没有的话就去创建这个对象,创建对象的时候会包括三大步骤:实例化、注入属性、初始化(执行bean的相关初始化方法);
首先调用createBeanInstance(“a”);方法将对象实例化,再调用populateBean(“a”);方法填充属性,在填充该属性b的时候发现同样需要调用getBean方法获取,同样是先去单例池中查看能否获取到该b对象,没有获取到就调用createBeanInstance(“b”);进行创建,然后就是填充属性,初始化;
三大步骤执行完之后就会将该对象放在单例池中,然后再返回该对象;
image.png
循环依赖
假设ObjectB对象中有一个ObjectA的引用a,那么接下来我们进入循环依赖的分析
image.png
在ObjectA对象填充ObjectB类属性b的时候,b对象在创建的时候也会去填充属性,此时在填充属性的时候发现ObjectB中有一个ObjectA的属性a需要去填充,也是首先调用getBean方法去单例池中获取,获取不到;这就意味着它又得去创建a,这就进入了循环创建
image.png
要解决这个创建循环的问题,需要加入一个半成品的池子;当我们的bean对象在完成实例化创建好对象这一个步骤之后但是并没有到填充属性之前就直接将其放入半成品池子中;
这样在ObjectA对象实例化之后就将a放入半成品池子中,填充属性时由于有属性b,需要去创建b对象,然后在b对象填充属性a时,调用getBean方法发现半成品池子中有一个a对象,直接返回了这个a对象,这样b对象完全走完了三大步骤,就将其放入单例池中;之后a对象也完成了三大步骤,也放在了单例池中;半成品池中的a对象就被删除了;
这也就避免了循环创建的问题;这里的关键是对象完成实例化这个步骤之后就将其放入了半成品池中;
这里只用到了二级缓存,但是Spring中却有三级缓存,这是为什么呢?这是因为二级缓存只能解决循环创建的问题,而不能解决AOP代理的问题;

什么是AOP代理问题?当我们给ObjectA对象a与ObjectB对象b添加上AOP代理时,这样变成了代理对象proxy$a跟代理对象proxy$b之间的循环依赖了( 红线 )
image.png
由于半成品池中存放的是a对象本身,这样在进行属性填充的时候调用getBean方法去获取半成品池中的对象时就获取不到代理之后的proxy$a,二级缓存解决不了这个问题,所以才有了三级缓存;
那三级缓存是如何解决这个问题的呢?
首先我们要搞清楚这个AOP代理是如何创建AOP代理对象的,学习Spring理论的时候我们可以知道代理对象是在初始化时候创建的,在初始化的时候要去执行bean的后置处理并在里面执行构造代理对象的代码;
这个相关的后置处理器就是AspectJAwareAdvisorAutoProxyCreator,在该后置处理器中的一个实现方法postProcessAfterlnitialization方法中调用createProxy方法创建代理对象;而后置处理器中还有一个能够解决循环依赖的关键方法
image.png
也就是说在AspectJAwareAdvisorAutoProxyCreator后置处理器中有两个入口去创建代理对象;

了解了这些前置知识,我们再来看看结合了AOP代理的循环依赖是如何被三级缓存解决的:
首先,代理对象创建的其他流程都是差不多的,不一样的地方在于代理对象在完成初始化之后需要执行后置处理,基于普通的a对象创建出一个proxy$a代理对象,然后将代理对象proxy$a放入单例池中;也就是说,此时单例池中存放的不再是对象a而是proxy$a代理对象本身;原来半成品池中存放的是对象a,这个时候b通过原来的流程解决循环依赖的时候引用的还是半成品池中的对象a而不是proxy$a代理对象,而我们正确的方式应该是引用这个代理对象proxy$a。
现在为了解决这个问题,在单例池以及半成品池之上多加了一个工厂池,即所谓的三级缓存;有了这个三级缓存,在我们创建完这个a对象的时候,就会给它在工厂池中添加一个factory(a),这个factory(a)干的事情就是调用前面讲到过的在创建动态代理的时候走的另一个方法,提前处理;
image.png
为什么不在实例化完这个a对象之后就调用这个提前的处理方法(提前引用)呢?这是因为这个提前处理的方法主要是为了在a在创建过程中如果有其他对象引用这个a,才会去执行的factory(a),要不然不会执行factory(a);
在这个循环引用的例子中,在b填充a属性的时候就会提前引用这个a对象,然后就会去调用它的factory(a)方法提前去处理,创建动态代理;这个时候a的动态代理proxy$a代理对象就有了,然后将proxy$a代理对象放在半成品池中去,然后冲半成品池中将其填充到bean的属性中;填充完之后b的属性就填充完成,然后到b的初始化步骤,初始化完就是调用后置处理器执行后置处理,这里同样会创建一个动态代理的bean,这个时候这个bean也会放到单例池中,同时在工厂池中也有一个factory(b);只不过在创建这个b的过程中没有其他对象在它创建的过程中提前引用它,所以factory(b);没有执行的机会,故最终工场池中的factory(b);被删除,对象b也就被创建完成;
然后回到a对象去填充它的属性b,由于这个时候单例池中已经有了对象b,所以直接返回即可;填充完属性且初始化完成,a也会去调用动态代理对象,但是由于半成品池中已经有了一个动态代理对象a,所以就没有必要再去创建一个代理对象a了,直接将半成品池中的代理对象移到单例池中即可;
最后工厂池中的factory(a)就被移除了。整个流程就走完了 ;循环依赖也被解决了;
一些细节:
如果仅仅是解决循环依赖问题,使用二级缓存就可以了,但是如果对象实现了AOP,那么注入到其他bean的时候,并不是最终的代理对象,而是原始的。这时就需要通过三级缓存的ObjectFactory才能提前产生最终的需要代理的对象。
什么时候将Bean的引用提前暴露给第三级缓存的ObjectFactory持有?时机就是在第一步实例化之后,第二步依赖注入之前,完成此操作。

循环依赖问题的类型以及解决进展

循环依赖问题在Spring中主要有三种情况:

  • (1)通过构造方法进行依赖注入时产生的循环依赖问题。
  • (2)通过setter方法进行依赖注入且是在多例(原型)模式下产生的循环依赖问题。
  • (3)通过setter方法进行依赖注入且是在单例模式下产生的循环依赖问题。

在Spring中,只有第(3)种方式的循环依赖问题被解决了,其他两种方式在遇到循环依赖问题时都会产生异常。其实也很好解释,上面分析的循环依赖的解决是借助于对象创建的三大步骤:实例化、属性注入、初始化(还有一个后置处理器的执行);

  • 第(1)种构造方法注入的情况下,在new对象进行实例化的时候就被堵塞住了,其实也就是”先有鸡还是先有蛋“的历史难题。
  • 第(2)种setter方法(多例)的情况下,每一次getBean()时,都会产生一个新的Bean而不是原来的bean,这样如此反复循环创建下去就会有无穷无尽的Bean产生了,最终就会导致OOM问题的出现。

Spring在单例模式下的setter方法依赖注入引起的循环依赖问题,主要是通过二级缓存和三级缓存来解决的,其中三级缓存是主要功臣。解决的核心原理就是:在对象实例化之后,依赖注入之前,Spring提前暴露的Bean实例的引用在第三级缓存中进行存储。

解决构造函数相互注入造成的循环依赖

前面说Spring可以自动解决单例模式下通过setter()方法进行依赖注入产生的循环依赖问题。而对于通过构造方法进行依赖注入时产生的循环依赖问题没办法自动解决,那针对这种情况,我们可以使用@Lazy注解来解决。
也就是说,对于类A和类B都是通过构造器注入的情况,可以在A或者B的构造函数的形参上加个@Lazy注解实现延迟加载。@Lazy实现原理是,当实例化对象时,如果发现参数或者属性有@Lazy注解修饰,那么就不直接创建所依赖的对象了,而是使用动态代理创建一个代理类。
比如,类A的创建:A a=new A(B),需要依赖对象B,发现构造函数的形参上有@Lazy注解,那么就不直接创建B了,而是使用动态代理创建了一个代理类B1,此时A跟B就不是相互依赖了,变成了A依赖一个代理类B1,B依赖A。但因为在注入依赖时,类A并没有完全的初始化完,实际上注入的是一个代理对象,只有当他首次被使用的时候才会被完全的初始化。

Spring的三大缓存

参考文档
https://blog.csdn.net/f641385712/article/details/92801300?
https://blog.csdn.net/chinawangfei/article/details/122963121?

AOP的应用场景

一般在做某些功能的时候都会进行有效性的校验(是否开始、是否结束等等)、以及这个接口是不是需要用户登录、日志记录啊之类的。
当然我们可以把这些校验代码直接放在业务实现的代码中,这样虽然能达到效果,但是这样总显得不够专业,耦合度也很高,而且我们有可能不止一个功能需要用到校验的代码,若是每个功能都copy一份校验的代码就太麻烦了;
这个时候我们用AOP就轻松多了,只需要写一份代码,然后在合适的地方切入进入就行了;

AOP中几个名词概念的理解?

Advice(通知):要对切面添加的功能代码,比如 安全,事物,日志等功能代码,它通过 before、after 和 around 来区别是在每个 连接点之前、之后还是代替执行的代码。
Joint point(连接点):目标对象,所有可以(只是可以)增强的方法。

Pointcut(切点):在连接点的基础上,来定义切入点,比如你的一个类里,有15个方法,那就有几十个连接点了对把,但是你并不想在所有方法附近都使用通知(使用叫织入,以后再说),你只想让其中的几个,在调用这几个方法之前,之后或者抛出异常时干点什么,那么就用切点来定义这几个方法,让切点来筛选连接点,选中那几个你想要的方法。
Aspect(切面): 用@Aspect注解声明的类,切面是通知和切入点的结合。现在发现了吧,没连接点什么事情,连接点就是为了让你好理解切点,搞出来的,明白这个概念就行了。通知说明了干什么和什么时候干(什么时候通过方法名中的before,after,around等就能知道),而切入点说明了在哪干(指定到底是哪个方法),这就是一个完整的切面定义。

Target(目标对象):织入 Advice 的目标对象.。
Weaving(织入):将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程

通知(Advice)的类型以及执行的顺序是什么?

类型

1、前置通知
在目标方法执行之前执行执行的通知。
2、环绕通知
在目标方法执行之前和之后都可以执行额外代码的通知。
3、后置通知
在目标方法执行之后执行的通知。
4、异常通知
在目标方法抛出异常时执行的通知。
5、最终通知
是在目标方法执行之后执行的通知。
以上5种都可以额外接收一个JoinPoint参数,来获取目标对象和目标方法相关信息,但一定要保证必须是第一个参数。


顺序

五种通知的执行顺序:
1、在目标方法没有抛出异常的情况下
前置通知
环绕通知的调用目标方法之前的代码
目标方法
环绕通知的调用目标方法之后的代码
后置通知
最终通知

2、在目标方法抛出异常的情况下
前置通知
环绕通知的调用目标方法之前的代码
目标方法
抛出异常
异常通知
最终通知

3、如果存在多个切面
多切面执行时,采用了责任链设计模式,在切面类之间,切面的配置顺序决定了切面的执行顺序,order越小越是最先执行,但更重要的是最先执行的最后结束,多个切面执行的过程,类似于方法调用的过程,在环绕通知的proceed()执行时,去执行下一个切面或如果没有下一个切面执行目标方法,从而达成了如下的执行过程:
image.png

有异常情况下:
image.png

可以把Spring AOP的执行顺序想象为一个同心圆,要执行的目标方法为圆心,最外层的order最小,而且@Order注解只能作用于类上,标记在方法上无效,同一个切面中,两个通知作用于同一个目标方法是无序(先按注解排序,再按方法名排序,没有优先级,这点可以从源码中分析得到)。

Spring的AOP的实现有哪些?

jdk接口代理跟cglib增强;
proxy代理增强是jdk自带的,需要目标类实现接口,故只能针对接口代理。
利用Proxy类的newProxyInstance静态方法去创建一个目标类的代理类,由于这个代理类是运行时动态生成的,所以需要一个类加载器来加载在运行时动态生成的代理类字节码文件;利用jdk自带的方式实现增强需要目标类实现一个或多个接口,所以第二个参数就是一个接口或者一个接口数组的Class对象;最后的参数表示给目标类的增强逻辑,都封装在一个InvocationHandler对象中。
小结:

  • jdk 动态代理要求目标必须实现接口,生成的代理类也实现相同接口,因此代理与目标之间是平级兄弟关系,不能进行强制类型转换
  • 目标类可以加final来修饰,可以没有子类。

cglib方式增强是利用Enhancer类的create方法创建代理对象;一个普通的create方法有两个参数,cglib跟jdk方式不一样的地方在于它是通过父子继承的关系来创建代理的,所以第一个参数是指定代理对象的父亲,即目标类;第二个参数是封装了增强逻辑的CallBack对象,但一般用是都是它的子接口MethodInterceptor;
小结:

  • cglib 不要求目标实现接口,它生成的代理类是目标的子类,因此代理与目标之间是子父关系
  • 限制⛔:1.根据上述分析 final 类无法被 cglib 增强 一旦目标类加上final来修饰,代理类就无法继承目标类 2.目标类方法定义的时候加上final来修饰,就会无法实现增强目标类

Spring的AOP的实现原理是什么?

AOP的实现方式除了代理,还有其他的实现方式吗?

AOP是指在程序的运行期间动态地将某段代码切入到指定方法、指定位置进行运行的编程方式。
它的实现原理除了代理这种方式,还有ajc实现、agent增强这两种实现方式;
ajc实现方式是用一个AspectJ的编译器来进行增强;这个编译器的原理是将我们的待增强类的class文件进行改写,既然class文件都改写了,那就不需要进行代理了。
注意,这里的切面类并没有加@Component注解 ,即它并不是由Spring管理、增强的,我们可以直接创建该对象就可以进行增强了,不需要在Spring容器中获取。
如果是使用javac的编译工具进行编译的话,是达不到增强的效果的;它只能使用插件才能编译增强的效果;
编译器增强能突破代理仅能通过方法重写增强的限制:可以对构造方法、静态方法等实现增强。
这种实现的方式并不常见,大多数的实现还是选择的代理方式实现;
ajc增强是在编译阶段对class文件进行改写,而agent增强是在类加载时可以通过 agent 修改 class 实现增强;

—Spring事务的实现方式和实现原理

(1)Spring事务的种类:

(2)spring的事务传播机制:

(3)Spring中的隔离级别:

—Spring 框架中都用到了哪些设计模式?

https://blog.csdn.net/a745233700/article/details/112598471

—Spring框架中有哪些不同类型的事件?

Spring 提供了以下5种标准的事件:

(1)上下文更新事件(ContextRefreshedEvent):在调用ConfigurableApplicationContext 接口中的refresh()方法时被触发。
(2)上下文开始事件(ContextStartedEvent):当容器调用ConfigurableApplicationContext的Start()方法开始/重新开始容器时触发该事件。
(3)上下文停止事件(ContextStoppedEvent):当容器调用ConfigurableApplicationContext的Stop()方法停止容器时触发该事件。
(4)上下文关闭事件(ContextClosedEvent):当ApplicationContext被关闭时触发该事件。容器被关闭时,其管理的所有单例Bean都被销毁。
(5)请求处理事件(RequestHandledEvent):在Web应用中,当一个http请求(request)结束触发该事件。

如果一个bean实现了ApplicationListener接口,当一个ApplicationEvent 被发布以后,bean会自动被通知。

SpringMVC

https://blog.csdn.net/a745233700/article/details/80963758?
https://blog.csdn.net/ThinkWon/article/details/104397427?

说说你对SpringMVC的理解?

是什么?

Spring MVC是一个基于Java实现MVC框架请求驱动类型的轻量级Web框架,通过把模型M-视图V-控制器C分离,将web层进行职责解耦,把复杂的web应用分成逻辑清晰的几部分,简化开发,减少出错,方便组内开发人员之间的配合。

优缺点

(1)可以支持各种视图技术,而不仅仅局限于JSP;
(2)与Spring框架集成(如IoC容器、AOP等);
(3)清晰的角色分配:前端控制器(dispatcherServlet) , 请求到处理器映射(handlerMapping), 处理器适配器(HandlerAdapter), 视图解析器(ViewResolver)。
(4) 支持各种请求资源的映射策略。

关于MVC模式与三层架构:https://www.yuque.com/matteo-irbtp/qqmkoc/oyoy2m

开发中怎么搭建SpringMvc来使用的?

1、导入相应的jar包或者引入相应的依赖坐标
2、在web.xml文件中添加前端控制器的配置
3、编写Controller控制器接收前端发起的请求;

SpringMVC有哪些核心组件

串起来?
(1)前端控制器 DispatcherServlet(不需要程序员开发)
作用:接收请求、响应结果,相当于转发器,有了DispatcherServlet 就减少了其它组件之间的耦合度。
(2)处理器映射器HandlerMapping(不需要程序员开发)
作用:根据请求的URL来查找Handler
(3)处理器适配器HandlerAdapter
注意:在编写Handler的时候要按照HandlerAdapter要求的规则去编写,这样适配器HandlerAdapter才可以正确的去执行Handler。
(4)处理器Handler(需要程序员开发)
(5)视图解析器 ViewResolver(不需要程序员开发)
作用:进行视图的解析,根据视图逻辑名解析成真正的视图(view)
(6)视图View(需要程序员开发jsp)
View是一个接口, 它的实现类支持不同的视图类型(jsp,freemarker,pdf等等)

简单描述SpringMVC的工作流程?

(1)用户发送请求至前端控制器DispatcherServlet;
(2) DispatcherServlet收到请求后,调用HandlerMapping处理器映射器,请求获取Handle;
(3)处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet;
(4)DispatcherServlet 调用 HandlerAdapter处理器适配器;
(5)HandlerAdapter 经过适配调用 具体处理器(Handler,也叫后端控制器);
(6)Handler执行完成返回ModelAndView;
(7)HandlerAdapter将Handler执行结果ModelAndView返回给DispatcherServlet;
(8)DispatcherServlet将ModelAndView传给ViewResolver视图解析器进行解析;
(9)ViewResolver解析后返回具体View;
(10)DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)
(11)DispatcherServlet响应用户。
image.png

注解的原理以及SpringMVC中常用的注解是什么?

https://blog.csdn.net/One_L_Star/article/details/102485860

MyBatis

https://www.bilibili.com/video/BV1Z44y1b775?p=49

SSM整合

SpringBoot

概述

谈谈你对SpringBoot的理解

在学SpringBoot之前我们肯定已经学过了spring、SpringMvc、Mybatis等框架了。使用整合这三个框架的方式去开发我们的代码比我们直接使用原生的Servlet去做Web开发更方便,在体验到SSM框架带来的好处的同时,我们也在为使用SSM框架开发之前要写一大堆的配置文件而苦恼,毕竟将这些东西都整合在一起不容易。也就是说我们做Spring Web项目开发之前要做的步骤包括但不限于以下步骤:

  • 配置web.xml去加载Spring、SpringMvc等
  • 配置数据库连接、配置数据源
  • 读取配置文件
  • ……

要是一不小心配错了其中的某个步骤,或者打错了某个东西,相信经验丰富的人都有体验,排bug就能排到你发癫。
而且Spring这个大熔炉能容纳的东西可不仅仅是这些,还有很多其他的框架。但仅仅是整合SSM要写的配置文件就足以让人发狂,若是再加上其他框架,那才真正是配置地狱了。
为了解决每次进行Web应用开发之前都要自己手动写配置文件,也为在程序员整合其他框架的时候不再重复那些固定的配置步骤,浪费时间,为了提高程序员的开发效率,SpringBoot出现了。以前要手动写一大堆配置文件才能启动的项目,现在SpringBoot完全自动的帮我们配置好了,我们只需要创建一个SpringBoot项目,就可以启动了,再写一个请求方法,就能够直接访问该请求了。
所以说,SpringBoot主要用来简化使用Spring的难度和繁重的XML配置,它是Spring组件的一站式解决方案,采取了习惯优于配置的方法。通过.properties或者.yml文件替代了Spring繁杂的XML配置文件,同时支持@ImportResource注解加载XML配置。Spring Boot还提供了嵌入式HTTP服务器、命令行接口工具、多种插件等等,使得应用程序的测试和开发简单起来。
1635593645208.png
如上图所示,都是spring生态中的技术,比如Microservices(微服务开发),我们实际开发中随便一个web应用因为功能模块众多,它随时可能成长为一个大型应用;故我们不能将其功能模块都写在一个项目中,我们应该将其中的功能模块拆分为一个一个微小的功能模块,每一个小模块就被成为一个微服务。
Reactive响应式开发、Cloud分布式云开发、serverless无服务开发、EventDriven事件驱动、Batch批处理等。spring的生态圈不仅仅局限于图中所示模块,还有很多很多。 要程序员每次开发应用的时候都自己写一遍,显然是不太理智的。

Starter启动器

什么是Spring Boot Starter?

Starters可以理解为启动器,它包含了一系列可以集成到应用里面的依赖包,可以一站式集成 Spring 和其他技术,而不需要到处找示例代码和依赖包。Spring Boot Starter的工作原理是:Spring Boot 在启动时扫描项目所依赖的JAR包,寻找包含spring.factories文件的JAR包,根据spring.factories配置加载AutoConfigure类,根据 @Conditional注解的条件,进行自动配置并将Bean注入Spring Context

pringBoot 常用的 Starter 有哪些?

1、 spring-boot-starter-web :提供 Spring MVC + 内嵌的 Tomcat 。

2、 spring-boot-starter-data-jpa :提供 Spring JPA + Hibernate 。

3、 spring-boot-starter-data-Redis :提供 Redis 。

4、 mybatis-spring-boot-starter :提供 MyBatis 。

SpringBoot Starter 的工作原理是什么?

SpringBoot 在启动的时候会干这几件事情:
1、 SpringBoot 在启动时会去依赖的 Starter 包中寻找 resources/META-INF/spring.factories 文件,然后根据文件中配置的 Jar 包去扫描项目所依赖的 Jar 包。
2、 根据 spring.factories 配置文件去加载 AutoConfigure 类
3、 根据 @Conditional 注解的条件,进行自动配置将 Bean 注入 Spring Context容器

总结一下,其实就是 SpringBoot 在启动的时候,按照约定去读取 SpringBoot Starter 的配置信息,再根据配置信息对资源进行初始化,并注入到 Spring 容器中。这样 SpringBoot 启动完毕后,就已经准备好了一切资源,使用过程中直接注入对应 Bean 资源即可。

配置文件

SpringBoot 的核心配置文件有哪几个?它们的区别是什么?

SpringBoot 的核心配置文件是 application 和 bootstrap 配置文件
application 配置文件这个容易理解,主要用于 SpringBoot 项目的自动化配置。
bootstrap比 applicaton 优先加载,配置在应用程序上下文的引导阶段生效, 而且boostrap 里面的属性不能被覆盖;bootstrap 配置文件有以下几个应用场景:
使用 Spring Cloud Config 配置中心时,这时需要在 bootstrap 配置文件中添加连接到配置中心的配置属性来加载外部配置中心的配置信息;
一些固定的不能被覆盖的属性;
一些加密/解密的场景;

Spring Boot 的配置文件有哪几种格式?它们有什么区别?

YAML 是一种可读的数据序列化语言,它通常用于配置文件。
主要有.properties 和 .yml格式,它们的区别主要是书写格式不同。另外,.yml 格式不支持 @PropertySource 注解导入配置。

YAML 配置的优势在哪里 ?

配置有序
支持数组,数组中的元素可以是基本数据类型或者对象
简洁方便

Spring Boot 是否可以使用 XML 配置 ?

Spring Boot 推荐使用 Java 配置同时支持 XML 配置,通过 @ImportResource 注解加载 XML 配置。

什么是 Spring Profiles?

Spring Profiles 允许用户根据配置文件(dev,prod,test等等)来注册 bean。当应用程序在开发环境中运行时,只有某些 bean 可以加载,而在生产环境中,某些其他 bean 也可以加载。比如要求 Swagger 文档仅适用于测试环境,并且禁用所有其他文档,可以使用配置文件来完成。

SpringBoot 配置文件的加载顺序是什么

由jar包外向jar包内进行寻找;

优先加载带profile

jar包外部的application-{profile}.properties或application.yml(带spring.profile配置文件

jar包内部的application-{profile}.properties或application.yml(带spring.profile配置文件

再来加载不带profile

jar包外部的application.properties或application.yml(不带spring.profile配置文件

jar包内部的application.properties或application.yml(不带spring.profile配置文件

Spring Boot配置加载顺序优先级是:propertiese文件、YAML文件、系统环境变量、命令行参数。

Spring Boot 有哪几种读取配置的方式?

使用@Value注解加载单个属性值
使用@ConfigurationProperties注解可以加载一组属性的值,针对于要加载的属性过多的情况,比@Value注解更加简洁

自动配置

什么是自动配置?

Spring 和 SpringMVC 的问题在于需要配置大量的参数以及配置文件。
我们能否带来更多的智能?当一个 MVC JAR 添加到应用程序中的时候,我们能否自动配置一些 beans?
Spring 查看(CLASSPATH 上可用的框架)已存在的应用程序的配置。在此基础上,SpringBoot 提供了配置应用程序和框架所需要的基本配置。这就是自动配置。

Spring Boot 自动配置原理是什么?

@EnableAutoConfiguration注解、 @Configuration注解和 @ConditionalOnClass注解组成了Spring Boot自动配置的核心,SpringBoot启动的时候通过@EnableAutoConfiguration注解找到META-INF/spring.factories配置文件中所有的自动配置类,并对其进行加载,而这些自动配置类的类名都是以AutoConfiguration结尾来命名的,它实际上就是一个javaConfig形式的Spring容器配置类,它们都有一个@EnableConfigurationPerperties的注解,通过这个注解启动XXXProperties命名的类去加载全局配置中的属性,如server.port,而XXXProperties通过@ConfigurationProperties注解将全局配置文件中的属性与自己的属性进行绑定。

SpringBoot核心注解

(Spring Boot 的核心注解是哪个?它主要由哪几个注解组成的)介绍一下

@SpringBootApplication 注解

Spring Boot 的核心注解是@SpringBootApplication,它也是启动类使用的注解,主要包含了 3 个注解:
@SpringBootConfiguration:它组合了 @Configuration 注解,实现配置文件的功能。
@EnableAutoConfiguration:具有打开自动配置的功能,也可以关闭某个自动配置的选项。
@ComponentScan:用于Spring组件扫描。

嵌入式Web容器

Spring Boot支持哪些嵌入式Web容器?

Spring Boot支持的嵌入式servlet容器有: Tomcat、Jetty、Undertow。

其他

Spring Boot 支持哪些日志框架?推荐和默认的日志框架是哪个?

Spring Boot 支持 Java Util Logging, Log4j2, Logback 作为日志框架,如果使用 Starters 启动器,Spring Boot 将使用 Logback 作为默认日志框架,推荐的日志框架是Log4j2。

SpringBoot如何配置log4j?

在引用log4j之前,需要先排除项目创建时候带的日志,因为那个是Logback,然后再引入log4j的依赖,引入依赖之后,去src/main/resources目录下的log4j-spring.properties配置文件,就可以开始对应用的日志进行配置使用。

Spring Boot 可以兼容老 Spring 项目吗?

可以兼容,使用 @ImportResource 注解导入老 Spring 项目配置文件。

Spring、SpringMVC、SpringBoot的区别?

1、 Spring框架就像一个家族,有众多衍生产品,例如boot、mvc、jpa等等。但他们的基础都是Spring的ioc、aop。ioc提供了依赖注入的容器,aop解决了面向横切面编程,然后在此两者的基础上实现了其它延伸产品的高级功能;
2、 springMVC是基于Servlet的一个MVC框架主要解决WEB开发的问题;
3、 为了简化开发的使用,从而创造性地推出了SpringBoot框架,默认优于配置

SpringBoot的核心注解是哪个?它主要由哪几个注解组成的?

启动类上面的注解是@SpringBootApplication,它也是SpringBoot的核心注解,主要组合包含了以下3个注解:
1、 @SpringBootConfiguration:组合了@Configuration注解,实现配置文件的功能。
2、 @EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项,如关闭数据源自动配置功能:SpringBootApplication(exclude={DataSourceAutoConfiguration.class})
3、 @ComponentScan:Spring组件扫描

如何在 SpringBoot 中添加通用的 JS 代码?

在源文件夹下,创建一个名为 static 的文件夹。然后,你可以把你的静态的内容放在这里面。
例如,myapp.js 的路径是 resources\static\js\myapp.js
你可以参考它在 jsp 中的使用方法:
错误:HAL browser gives me unauthorized error - Full authenticaition is required to access this resource.
该如何来修复这个错误呢?
两种方法:

方法 1:关闭安全验证
application.properties
management.security.enabled:FALSE
方法二:在日志中搜索密码并传递至请求标头中

SpringBoot 中如何实现定时任务 ?

在 SpringBoot 中使用定时任务主要有两种不同的方式,一个就是使用 Spring 中的 @Scheduled 注解,另一-个则是使用第三方框架 Quartz。

使用 Spring 中的 @Scheduled 的方式主要通过 @Scheduled 注解来实现。

如何在SpringBoot中禁用Actuator端点安全性?

默认情况下,所有敏感的HTTP端点都是安全的,只有具有ACTUATOR角色的用户才能访问它们。

安全性是使用标准的HttpServletRequest.isUserInRole方法实施的。 我们可以使用management.security.enabled = false 来禁用安全性。只有在执行机构端点在防火墙后访问时,才建议禁用安全性。

如何在自定义端口上运行SpringBoot应用程序? 为了在自定义端口上运行SpringBoot应用程序,您可以在application.properties中指定端口。 server.port = 8090

Async异步调用方法

在SpringBoot中使用异步调用是很简单的,只需要在方法上使用@Async注解即可实现方法的异步调用。 注意:需要在启动类加入@EnableAsync使异步调用@Async注解生效。

如何不通过任何配置来选择 Hibernate 作为 JPA 的默认实现?

因为 SpringBoot 是自动配置的。

下面是我们添加的依赖项:

spring-boot-stater-data-jpa 对于 Hibernate 和 JPA 有过渡依赖性。

当 SpringBoot 在类路径中检测到 Hibernate 中,将会自动配置它为默认的 JPA 实现。

12、SpringBoot 的核心注解是哪个?它主要由哪几个注解组成的?

启动类上面的注解是@SpringBootApplication,它也是 SpringBoot 的核心注解,主要组合包含了以下 3 个注解:

@SpringBootConfiguration:组合了 @Configuration 注解,实现配置文件的功能。

@EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项,如关闭数据源自动配置功能:

@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })。

@ComponentScan:Spring组件扫描。

13、什么是 WebSockets?

WebSocket 是一种计算机通信协议,通过单个 TCP 连接提供全双工通信信道。

1、 WebSocket 是双向的 -使用 WebSocket 客户端或服务器可以发起消息发送。

2、 WebSocket 是全双工的 -客户端和服务器通信是相互独立的。

3、 单个 TCP 连接 -初始连接使用 HTTP,然后将此连接升级到基于套接字的连接。然后这个单一连接用于所有未来的通信

4、 Light -与 http 相比,WebSocket 消息数据交换要轻得多。

14、运行 SpringBoot 有哪几种方式?

打包用命令或者放到容器中运行

用 Maven/ Gradle 插件运行

直接执行 main 方法运行

15、什么是执行器停机?

关机是允许应用程序正常关机的端点。默认情况下,此功能不启用。你可以在应用程序属性文件中使用management . endpoint . shut down . enabled = true来启用此选项。但是该方法请谨慎使用。

16、我们如何监视所有 SpringBoot 微服务?

SpringBoot 提供监视器端点以监控各个微服务的度量。这些端点对于获取有关应用程序的信息(如它们是否已启动)以及它们的组件(如数据库等)是否正常运行很有帮助。但是,使用监视器的一个主要缺点或困难是,我们必须单独打开应用程序的知识点以了解其状态或健康状况。想象一下涉及 50 个应用程序的微服务,管理员将不得不击中所有 50 个应用程序的执行终端。为了帮助我们处理这种情况,我们将使用位于的开源项目。它建立在 SpringBoot Actuator 之上,它提供了一个 Web UI,使我们能够可视化多个应用程序的度量。

17、如何使用SpringBoot实现分页和排序?

使用SpringBoot实现分页非常简单。使用Spring Data-JPA可以实现将可分页的

传递给存储库方法。

18、什么是 JavaConfig?

1、 面向对象的配置。由于配置被定义为 JavaConfig 中的类,因此用户可以充分利用 Java 中的面向对象功能。一个配置类可以继承另一个,重写它的@Bean 方法等。

2、 减少或消除 XML 配置。基于依赖注入原则的外化配置的好处已被证明。但是,许多开发人员不希望在 XML 和 Java 之间来回切换。JavaConfig 为开发人员提供了一种纯 Java 方法来配置与 XML 配置概念相似的 Spring 容器。从技术角度来讲,只使用 JavaConfig 配置类来配置容器是可行的,但实际上很多人认为将JavaConfig 与 XML 混合匹配是理想的。

3、 类型安全和重构友好。JavaConfig 提供了一种类型安全的方法来配置 Spring容器。由于 Java 5.0 对泛型的支持,现在可以按类型而不是按名称检索 bean,不需要任何强制转换或基于字符串的查找。

19、如何实现SpringBoot应用程序的安全性?

为了实现SpringBoot的安全性,我们使用 spring-boot-starter-security依赖项,并且必须添加安全配置。它只需要很少的代码。配置类将必须扩展WebSecurityConfigurerAdapter并覆盖其方法。

spring boot初始化环境变量流程?

1、 调用prepareEnvironment方法去设置环境变量

2、 接下来有三个方法getOrCreateEnvironment,configureEnvironment,environmentPrepared

3、 getOrCreateEnvironment去初始化系统环境变量

4、 configureEnvironment去初始化命令行参数

5、 environmentPrepared当广播到来的时候调用onApplicationEnvironmentPreparedEvent方法去使用postProcessEnvironment方法load yml和properties变量

20、比较一下 Spring Security 和 Shiro 各自的优缺点 ?

由于 SpringBoot 官方提供了大量的非常方便的开箱即用的 Starter ,包括 Spring Security 的 Starter ,使得在 SpringBoot 中使用 Spring Security 变得更加容易,甚至只需要添加一个依赖就可以保护所有的接口,所以,如果是 SpringBoot 项目,一般选择 Spring Security 。当然这只是一个建议的组合,单纯从技术上来说,无论怎么组合,都是没有问题的。Shiro 和 Spring Security 相比,主要有如下一些特点:

Spring Security 是一个重量级的安全管理框架;Shiro 则是一个轻量级的安全管理框架

Spring Security 概念复杂,配置繁琐;Shiro 概念简单、配置简单

Spring Security 功能强大;Shiro 功能简单

21、SpringBoot 中如何解决跨域问题 ?

跨域可以在前端通过 JSONP 来解决,但是 JSONP 只可以发送 GET 请求,无法发送其他类型的请求,在 RESTful 风格的应用中,就显得非常鸡肋,因此我们推荐在后端通过 (CORS,Cross-origin resource sharing) 来解决跨域问题。这种解决方案并非 SpringBoot 特有的,在传统的 SSM 框架中,就可以通过 CORS 来解决跨域问题,只不过之前我们是在 XML 文件中配置 CORS ,现在可以通过实现WebMvcConfigurer接口然后重写addCorsMappings方法解决跨域问题。

@Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry、addMapping(“/*”) 、allowedOrigins(““) 、allowCredentials(true) 、allowedMethods(“GET”, “POST”, “PUT”, “DELETE”, “OPTIONS”) 、maxAge(3600); } }

22、如何集成 SpringBoot 和 ActiveMQ?

对于集成 SpringBoot 和 ActiveMQ,我们使用依赖关系。它只需要很少的配置,并且不需要样板代码。

23、什么是 Apache Kafka?

Apache Kafka 是一个分布式发布 - 订阅消息系统。它是一个可扩展的,容错的发布 - 订阅消息系统,它使我们能够构建分布式应用程序。这是一个 Apache 顶级项目。Kafka 适合离线和在线消息消费。

24、spring-boot-starter-parent 有什么用 ?

我们都知道,新创建一个 SpringBoot 项目,默认都是有 parent 的,这个 parent 就是 spring-boot-starter-parent ,spring-boot-starter-parent 主要有如下作用:

1、 定义了 Java 编译版本为 1、8 。

2、 使用 UTF-8 格式编码。

3、 继承自 spring-boot-dependencies,这个里边定义了依赖的版本,也正是因为继承了这个依赖,所以我们在写依赖时才不需要写版本号。

4、 执行打包操作的配置。

5、 自动化的资源过滤。

6、 自动化的插件配置。

7、 针对 application、properties 和 application、yml 的资源过滤,包括通过 profile 定义的不同环境的配置文件,例如 application-dev、properties 和 application-dev、yml。

总结就是打包用的

25、SpringBoot与SpringCloud 区别

SpringBoot是快速开发的Spring框架,SpringCloud是完整的微服务框架,SpringCloud依赖于SpringBoot。

26、SpringBoot 中的 starter 到底是什么 ?

首先,这个 Starter 并非什么新的技术点,基本上还是基于 Spring 已有功能来实现的。首先它提供了一个自动化配置类,一般命名为 XXXAutoConfiguration ,在这个配置类中通过条件注解来决定一个配置是否生效(条件注解就是 Spring 中原本就有的),然后它还会提供一系列的默认配置,也允许开发者根据实际情况自定义相关配置,然后通过类型安全的属性注入将这些配置属性注入进来,新注入的属性会代替掉默认属性。正因为如此,很多第三方框架,我们只需要引入依赖就可以直接使用了。当然,开发者也可以自定义 Starter

27、spring boot监听器流程?

1、 通过app.addListeners注册进入 2、初始化一个SpringApplicationRunListeners进行处理

3、 从spring.factories中读取监听器处理类EventPublishingRunListener

4、 通过createSpringFactoriesInstances创建监听器处理类实例

5、 调用监听器listeners.starting()的方法来启动。

6、 底层把事件处理交给线程池去处理

28、SpringBoot 支持哪些日志框架?推荐和默认的日志框架是哪个?

SpringBoot 支持 Java Util Logging, Log4j2, Lockback 作为日志框架,如果你使用 Starters 启动器,SpringBoot 将使用 Logback 作为默认日志框架

29、什么是SpringBoot?

多年来,随着新功能的增加,spring变得越来越复杂。只需访问https://spring.io/projects 页面,我们就会看到可以在我们的应用程序中使用的所有Spring项目的不同功能。如果必须启动一个新的Spring项目,我们必须添加构建路径或添加Maven依赖关系,配置应用程序服务器,添加spring配置。因此,开始一个新的spring项目需要很多努力,因为我们现在必须从头开始做所有事情。

SpringBoot是解决这个问题的方法。SpringBoot已经建立在现有spring框架之上。使用spring启动,我们避免了之前我们必须做的所有样板代码和配置。因此,SpringBoot可以帮助我们以最少的工作量,更加健壮地使用现有的Spring功能。

30、SpringBoot常用的starter有哪些?

1、 spring-boot-starter-web 嵌入tomcat和web开发需要servlet与jsp支持

2、 spring-boot-starter-data-jpa 数据库支持

3、 spring-boot-starter-data-Redis Redis数据库支持

4、 spring-boot-starter-data-solr solr支持

5、 mybatis-spring-boot-starter 第三方的mybatis集成starter

SpringBoot 中如何解决跨域问题 ?

跨域可以在前端通过 JSONP 来解决,但是 JSONP 只可以发送 GET 请求,无法发送其他类型的请求,在 RESTful 风格的应用中,就显得非常鸡肋,因此我们推荐在后端通过 (CORS,Cross-origin resource sharing) 来解决跨域问题。这种解决方案并非 SpringBoot 特有的,在传统的 SSM 框架中,就可以通过 CORS 来解决跨域问题,只不过之前我们是在 XML 文件中配置 CORS ,现在可以通过实现WebMvcConfigurer接口然后重写addCorsMappings方法解决跨域问题。

  1. @Configuration
  2. public class CorsConfig implements WebMvcConfigurer {
  3. @Override
  4. public void addCorsMappings(CorsRegistry registry) {
  5. registry.addMapping("/**")
  6. .allowedOrigins("*")
  7. .allowCredentials(true)
  8. .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
  9. .maxAge(3600);
  10. }
  11. }

25. Spring Boot 中的监视器是什么?(什么是Spring Boot Actuator)?

Spring boot actuator 是 spring 启动框架中的重要功能之一,Spring boot 监视器可以访问生产环境中正在运行的应用程序的当前状态。监视器模块公开了一组可直接作为 HTTP URL 访问的 REST 端点来检查状态。

  1. 如何在 Spring Boot 中禁用 Actuator 端点安全性?
    默认情况下,所有敏感的 HTTP 端点都是安全的,只有具有 ACTUATOR 角色的用户才能访问它们。
    安全性是使用标准的 HttpServletRequest.isUserInRole 方法实施的,可以用来禁用安全性。
    只有在执行机构端点在防火墙后访问时,才建议禁用安全性。

  2. 什么是 CSRF 攻击?
    CSRF 代表跨站请求伪造,这是一种攻击,迫使最终用户在当前通过身份验证的Web 应用程序上执行不需要的操作。CSRF 攻击专门针对状态改变请求,而不是数据窃取,因为攻击者无法查看对伪造请求的响应。

  3. 如何使用 Spring Boot 实现异常处理?
    Spring 通过使用 @ControllerAdvice 注解处理异常,实现一个ControllerAdvice 类来处理控制器类抛出的所有异常。

  4. 如何监视所有 Spring Boot 微服务?
    Spring Boot 提供监视器端点监控各个微服务,这些端点对于获取有关应用程序的信息(如它们是否已启动)以及它们的组件(如数据库等)是否正常运行很有帮助。但是用监视器的一个主要缺点是,必须单独打开应用程序的知识点以了解其状态或健康状况。

  5. 运行 Spring Boot 有哪几种方式?
    用命令打包或者放到容器中运行
    用 Maven 插件运行
    直接执行 main 方法运行

SpringCloud