Spring相关的问题

一、Spring中的IOC、DI、AOP

1 、 I O C \color{green}{1、IOC} 1IOC

IOC:就是控制反转,通俗的说就是我们不用自己创建实例对象,这些都交给Spring的bean工厂帮我们创建管理,通过面向接口编程的方式来实现对业务组件的动态依赖。

优点:降低了代码的耦合度,将对象之间的相互依赖关系交给IOC容器来管理,并由IOC容器完成对象的注入,这样可以很大程度上简化应用的开发,把应用从复杂的依赖关系中解放出来。

2 、 D I \color{green}{2、DI} 2DI
DI:就是依赖注入,组件之间的依赖关系由容器在运行期决定形象的说即容器动态的将某个依赖关系注入到组件之中。我们只需要简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处。

有哪些不同类型的依赖注入实现方式?
依赖注入是时下最流行的IOC实现方式,依赖注入分为接口注入、Setter方法注入、构造器注入三种方式

Spring IOC的实现机制
Spring中的IOC的实现原理就是工厂模式加反射机制。

interface Fruit {
   public abstract void eat();
 }
class Apple implements Fruit {
    public void eat(){
        System.out.println("Apple");
    }
}
class Orange implements Fruit {
    public void eat(){
        System.out.println("Orange");
    }
}
class Factory {
    public static Fruit getInstance(String ClassName) {
        Fruit f=null;
        try {
            f=(Fruit)Class.forName(ClassName).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return f;
    }
}
class Client {
    public static void main(String[] a) {
        Fruit f=Factory.getInstance("io.github.dunwu.spring.Apple");
        if(f!=null){
            f.eat();
        }
    }
}

3 、 A O P \color{green}{3、AOP} 3AOP

AOP:面向切面编程,利用AOP可以对业务逻辑的各个部分进行隔离,从而使业务逻辑各部分之间的耦合度降低,提高程序的可重用行,同时提高了开发的效率。核心思想:**不修改源代码的前提下,对程序进行增强。**可用于权限认证、日志、事务处理。

AOP实现的关键在于代理模式,AOP代理主要分为静态代理和动态代理,静态代理的代表未AspectJ,动态代理则以Spring AOP为代表。

  • 静态: 由程序员创建代理类或特定工具自动生成源代码再对其编译,在程序运行前代理类的.class文件就已经存在了。
  • 动态:在程序运行时运用反射机制动态创建而成。(InvocationHandler的应用)

SpringAOP中的动态代理主要有两种方式:JDK动态代理和CGLIB动态代理。

  • JDK动态代理:通过反射来接受被代理的类,并且要求被代理的类必须实现一个接口。JDK动态dialing的核心是InvocationHandler接口和Proxy类。
  • CGLIB动态代理:CGLIB是一个代码生成的类库,可以在运行时动态的生成某个类的子类,注意,CGLIB是通过继承的方式做的动态代理,因此如果某个类表标记为final,那么它是无法使用CGLIB做动态代理的。

二、什么是Spring beans?

spring中的bean:我们一般获取对象的方法有两种,一种是手动直接new,另一种是直接交给spring来管理,交给spring来管理的就是bean容器先会实例化bean,然后自动注入,实例化的过程就需要依赖BeanDefinition。

Spring beans是那些形成Spring应用的主干的java对象,他们被Spring IOC容器初始化、装配、和管理,这些beans通过容器中配置的元数据创建,

三、BeanFatory和ApplicationContext有什么区别?

两者都是通过xml配置文件加载bean

1 、 B e a n F a c t o r y \color{green}{1、BeanFactory} 1BeanFactory

BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。

2 、 A p p l i c a t i o n C o n t e x t \color{green}{2、ApplicationContext} 2ApplicationContext

ApplicationContext它是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误。

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

1 、 S e t 方 法 注 入 \color{green}{1、Set方法注入} 1Set

2 、 构 造 器 注 入 \color{green}{2、构造器注入} 2

通过index设置参数的位置,通过type设置参数类型

3 、 静 态 工 厂 注 入 \color{green}{3、静态工厂注入} 3

就是通过调用静态工厂的方法来获取自己需要的对象,为了让spring管理所有对象,我们不能直接通过“工程类.静态方法()”来获取对象,而是依然通过spring注入的形式获取。

4 、 实 例 工 厂 \color{green}{4、实例工厂} 4

实例工厂的意思是获取对象实例的方法不是静态的,所以你需要首先new工厂类,再调用普通的实例方法。

五、spring中bean的生命周期?

1 、 实 例 化 B e a n \color{green}{1、实例化Bean} 1Bean

  • 对于BeanFactory容器,当客户向容器请求一个尚未初始化的bean时,或初始化bean的时候需要注入另一个尚未初始化的依赖时,容器就会调用createBean进行实例化。
  • 对于ApplicationContext容器,当容器启动结束后,通过获取BeanDefinition对象中的信息,实例化所有的bean。

BeanDefinition:用于保存bean的相关信息,包括属性、构造方法参数、依赖的bean实例名称以及是否延迟加载、单例等。它是实例化bean的原材料,spring就是根据BeanDefintion中的信息实例化bean。

( 2 ) 设 置 对 象 属 性 ( 依 赖 注 入 ) \color{green}{(2)设置对象属性(依赖注入)} 2

实例化后的对象被封装在BeanWrapper对象中,紧接着,Spring根据BeanDefinition中的信息 以及 通过BeanWrapper提供的设置属性的接口完成依赖注入。

( 3 ) 处 理 A w a r e 接 口 \color{green}{(3)处理Aware接口} 3Aware

xxxAware:xxxAware接口的出现就是为了让xxx感受到自身的一些属性。比如BeanFactoryAware接口就是为了能够获取到BeanFactory对象。

接着,Spring会检测该对象是否实现了xxxAware接口,并将相关的xxxAware实例注入给Bean:

  • ①如果这个Bean已经实现了BeanNameAware接口,会调用它实现的setBeanName(String beanId)方法,此处传递的就是Spring配置文件中Bean的id值

  • ②如果这个Bean已经实现了BeanFactoryAware接口,会调用它实现的setBeanFactory()方法,传递的是Spring工厂自身

  • ③如果这个Bean已经实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文;

( 4 ) B e a n P o s t P r o c e s s o r : \color{green}{(4)BeanPostProcessor:} 4BeanPostProcessor

如果想对Bean进行一些自定义的处理,那么可以让Bean实现了BeanPostProcessor接口,那将会调用postProcessBeforeInitialization(Object obj, String s)方法。

( 5 ) I n i t i a l i z i n g B e a n 与 i n i t − m e t h o d : \color{green}{(5)InitializingBean 与 init-method:} 5InitializingBeaninitmethod

如果Bean在Spring配置文件中配置了 init-method 属性,则会自动调用其配置的初始化方法。

( 6 ) 如 果 这 个 B e a n 实 现 了 B e a n P o s t P r o c e s s o r 接 口 , 将 会 调 用 p o s t P r o c e s s A f t e r I n i t i a l i z a t i o n ( O b j e c t o b j , S t r i n g s ) 方 法 ; \color{green}{(6)如果这个Bean实现了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法;} 6BeanBeanPostProcessorpostProcessAfterInitialization(Objectobj,Strings) 由于这个方法是在Bean初始化结束时调用的,所以可以被应用于内存或缓存技术;
以上几个步骤完成后,Bean就已经被正确创建了,之后就可以使用这个Bean了。

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

( 7 ) D i s p o s a b l e B e a n \color{green}{(7)DisposableBean} 7DisposableBean

当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用其实现的**destroy()**方法;

( 8 ) d e s t r o y − m e t h o d : \color{green}{(8)destroy-method:} 8destroymethod

最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。

六、 解释Spring支持的几种bean的作用域

( 1 ) s i n g l e t o n : \color{green}{(1)singleton:} 1singleton默认,每个容器中只有一个bean的实例,单例的模式由BeanFactory自身来维护。

( 2 ) p r o t o t y p e : \color{green}{(2)prototype:} 2prototype为每个bean请求提供一个实例。该bean在每次被注入的时候,都要重现创建一个实例。

( 3 ) r e q u e s t : \color{green}{(3)request:} 3request为每一个网络请求创建一个实例,在请求完成以后,bean会失效并被垃圾回收器回收

( 4 ) s e s s i o n : \color{green}{(4)session:} 4session与request范围类似,确保每个session中有一个bean的实例,在session过期后,bean会随之失效。

( 5 ) g l o b a l − s e s s i o n : \color{green}{(5)global-session:} 5globalsession类似于标准的HTTP Session作用域,不过它仅仅在基于portlet的web应用中才有意义,Protlet规范定义了全局Session的概念,它被所有构成某个portlrt web应用的各种不同的portlet所共享。

Portlet:是基于java的web组件,有portlet容器管理,并由容器处理请求,生产动态内容。

七、 spring框架中的单例Beans是线程安全的吗?

Spring框架并没有对单例进行任何多线程的封装处理,关于bean的线程安全和并发问题需要开发者自行去搞定。但实际上,大部分的Spring bean并没有可变的状态,所以在某种程度上说Spring的单例bean是线程安全的。如果你的bean有多种状态的话(比如View Model对象),就需要自行保证线程安全。

解决的办法就是:将多态bean的作用域由“singleton”变更为“prototype”或者使用ThreadLocal进行处理。

如果Bean配置为singleton,当有两个用户访问都调用到了该Bean,会出现资源共享的问题。

八、 spring如何处理线程并发问题?

  • 在一般情况下,只有无状态的Bean才可以在多线程环境下共享。

  • 绝大多数都可以声明为singleton作用域,因为Spring对一些Bean中非线程安全状态采用ThredLocal进行处理,解决线程安全问题。

ThreadLocal会为每个线程提供一个独立的变量副本从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。

九、 spring的自动装配

在Spring中,对象无需自己查找或创建与其关联的其他对象,由容器负责把需要相互协作的对象引用赋予各个对象,使用autowire来配置自动装载模式。

在Spring中xml配置中共有5种自动装配:

  • no:默认的方式是不进行自动装配,通过手动设置ref属性来进行装配bean。
  • byName:通过bean的名称进行自动装配,如果一个bean的property与另一bean的name相同,就进行自动装配。
  • byType:通过参数的数据类型进行自动装配。
  • constructor:利用构造函数进行装配,并且构造函数的参数通过byType进行装配。
  • autodetect:自动探测如果有构造方法,通过construct的方式自动装配,否则使用byType的方式自动装配。

@Autowired可用于:构造函数、成员变量、Setter方法
@Autowired和@Resource的区别

  • @Autowired默认是按照类型装配注入的,默认情况下它要求依赖对象必须存在。
  • @Resource默认是按照名称来装配注入的,只有当找不到名称匹配的bean才会按照类型来装配注入。

十、Spring 框架中都用到了哪些设计模式

(1)工厂模式:BeanFactory就是简单工厂模式的体现,用来创建对象的实例;

(2)单例模式:Bean默认为单例模式。

(3)代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术;

(4)模板方法:用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate。

(5)观察者模式:定义对象键一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被制动更新,如Spring中listener的实现–ApplicationListener。

十一、@Component和@Bean的区别是什么?

1 、 作 用 对 象 不 同 \color{green}{1、作用对象不同} 1
@Component注解作用于类,而@Bean注解作用于方法。
2 、 @ C o m p o n e n t 注 解 通 常 是 通 过 类 路 劲 扫 描 来 自 动 侦 测 以 及 自 动 装 配 到 S p r i n g 容 器 中 \color{green}{2、@Component注解通常是通过类路劲扫描来自动侦测以及自动装配到Spring容器中} 2@ComponentSpring,(可以使用@ComponentScan注解定义要扫描的路劲)。@Bean注解通常是在标有该注解的方法中定义产生这个bean,告诉Spring这是某个类的实例,当我需要用它的时候还给我。
3 、 @ B e a n 注 解 比 @ C o m p o n e n t 注 解 的 自 定 义 性 更 强 , 而 且 很 多 地 方 只 能 通 过 @ B e a n 来 注 册 b e a n \color{green}{3、@Bean注解比@Component注解的自定义性更强,而且很多地方只能通过@Bean来注册bean} 3@Bean@Component@Beanbean,比如当引用第三方库的类需要装配到Spring容器的时候,就只能通过@Bean注解实现。

十二、Spring中的事务

Spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是无法提供事务功能的。真正的数据库底层的事务提交和回滚是通过binlog或者redo log实现的。

Spring支持编程式事务管理和声明式事务管理两种方式:

  • 编程式事务管理使用TransactionTemplate
  • 声明式事务管理建立在AOP上的,其本质是通过AOP功能,对方法前后进行拦截,将事务处理的功能编织到拦截的方法中,也就是在目标方法开始之前加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。
s p r i n g 的 事 务 传 播 行 为 \color{green}{spring的事务传播行为} spring

spring事务的传播行为说的是,当多个事务同时存在的时候,spring如何处理这些事务的行为。

  • PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。

  • PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行

  • PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果不存在,就抛出异常

  • PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建事务

  • PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务则事务挂起。

  • PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。

  • PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则按REQUIRED属性执行。

S p r i n g 中 的 隔 离 级 别 \color{green}{Spring中的隔离级别} Spring
  • ISOLATION_DEFAULT:这是个 PlatfromTransactionManager 默认的隔离级别,使用数据库默认的事务隔离级别。
  • ISOLATION_READ_UNCOMMITTED:读未提交,允许另外一个事务可以看到这个事务未提交的数据。
  • ISOLATION_READ_COMMITTED:读已提交,保证一个事务修改的数据提交后才能被另一事务读取,而且能看到该事务对已有记录的更新。
  • ISOLATION_REPEATABLE_READ:可重复读,保证一个事务修改的数据提交后才能被另一事务读取,但是不能看到该事务对已有记录的更新。
  • ISOLATION_SERIALIZABLE:一个事务在执行的过程中完全看不到其他事务对数据库所做的更新。

十三、Spring中的bean的生成过程?

1、Bean的加载和解析—BeandefinationReader

在Spring中,Bean有多种定义方式,比如XML形式,Annotaion注解形式以及SpringBoot中的配置类等形式,BeandefinationReader该接口的作用就是加载和解析Bean的配置信息,每种形式都有对应的一种BeandefinationReade来进行解析,比如XML文件是XMLBeandefinationReade,他们最终都会解析为BeanDefinition对象,在IOC内部维护着一个CourrentHashMap,每个bean都对应着一个BeanDefinition对象。

2、Bean的注册—BeanDefinitionRegistry

BeanDefinitionRegistry是一个接口,实现了AliasRegistry接口,定义了一些对bean的常用操作,大概有如下功能:

  • 以Map<String,BeanDefinition>的形式注册bean
  • 根据beanName删除和获取beanDefiniation
  • 得到持有的beanDefiniation
  • 根据beanName判断是否包含beanDefiniation

一般注册bean的方法是registerBeanDefinition,DefaultListableBeanFactory 通过实现 BeanDefinitionRegistry 接口,重写该方法。

3、功能扩展和Bean的实例化

(1)第一种情况使用BeanFactoryProcessors直接扩展

Spring是一个框架,需要考虑扩展性,这里就是显示扩展性中的一环。它可以做什么事呢?

  • 注册一些BeanDefinition到注册中心
  • 修改注册中心内已注册过的BeanDdfinition信息。

(2)注册自定义BeanPostProcessors的进行扩展或者使用Spring固有的BeanPostProcessors进行扩展。

  • 想让Bean做一些自定义的处理,就可以实现BeanPostProcessors这个接口来做扩展,注意创建实例前后都可以进行扩展。
  • 根据BeanDefinition获取合适的Constructor进行反射创建实例,根据BeanDefinition注入依赖,并调用init方法进行实例的初始化。
  • 操作完成的对象可以放在实例缓存池中,比如普通单实例、FactoryBean实例、Spring系统实例等。

在这里插入图片描述

十四、FactoryBean和BeanFactory的区别

BeanFactory
BeanFactory是IOC最基本的容器,负责生产和管理bean,提供了IOC容器应遵守的最基本的接口。

FactoryBean
FactoryBean是一个接口,挡在IOC容器中的Bean实现了
一般情况下,Spring通过发射机制利用bean的class属性指定实现类实例化Bean,在某些情况下,实例化Bean过程比较复杂,如果按照传统方式,则需要在bena中提供大量的配置信息,并且配置方式的灵活性受限。这时采用编码的方式可能会得到一个简单的方案,所以Spring位次提供了一个FactoryBean的工厂类接口,用户可以是实现该接口定制是实例化Bean的逻辑。

总结:BeanFactory和FactoryBean其实没有什么可比性,只是两者的名称比较接近。BeanFactory是提供了IOC容器最基本的心事,给具体的IOC容器的实现提供了规范;FactoryBean可以说为IOC容器中Bean的实现提供了更加灵活的方式,FactoryBean在IOC容器的基础上给Bean的实现加上了一个简单工厂模式和装饰模式,我们可以再getObject()方法灵活配置。

十五、Spring中的循环依赖问题

spring中的循环依赖:其实就是循环引用,也就是两个或两个以上的bean互相持有对方,最终形成闭环。比如A依赖于B,而B依赖于C,C却依赖于A。

1、原型模式(protoType)循坏依赖—无法解决
2、单例Bean循环依赖—构造参数产生依赖—无法解决

这两种spring都无法解决,只能抛出异常:

Sprin容器会将每一个正在创建Bean的标识(BeanName)符放在一个“当前创建Bean池(CurrrentCreationSet)”中,Bean标识符在创建过程中将一直保持在这个池中。
因此如果在创建Bean过程中发现自己已经在“ 当前创建Bean池 ”里将抛出BeanCurrentlyInCreationException异常表示发生循环依赖,
而对于创建完毕的Bean将从“当前创建完毕Bean池中清理掉。”

3、单例Bean循环依赖-setter产生依赖—可以解决

使用三级缓存+提前曝光的方式来解决。

三级缓存:用来保存不同时期对象的三个集合。

  • 一级缓存:singletonObjects指单例对象的缓存。
  • 二级缓存:singletonFactories:单例对象工厂的缓存。
  • 三级缓存:earlySingletonObjects:提前曝光的单例对象的缓存。
/** Cache of singleton objects: bean name --> bean instance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
/** Cache of singleton factories: bean name --> ObjectFactory */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
/** Cache of early singleton objects: bean name --> bean instance */
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);

以A,B两个对象的相互引用为例讲解setter单例的循环依赖的解决过程。

  • getBean(A)时,先获取A对应的BeanDafiniton信息, 先到一级缓存中查询,因为该对象从未初始化过,一定拿不到,然后记录当前BeanName到CreationSet中,通过Class拿到无参构造利用反射创建早期对象。

什么是早期对象?
1、未进行属性注入
2、未进行对象init方法调用
3、未进行后处理器处理
但是早期对象和处理完后的对象内存地址是一样的,都是同一个对象,不包括AOP。

  • 执行MerageBeanDefinitionPostProcessor操作,处理@Autowried等注解,将早期对象封装到ObjectFactory内,放入三级缓存。
  • 进行依赖注入,发现依赖了类型B,进行getBean(B)操作
  • 通过反射创建B的早期对象,执行MerageBeanDefinitionPostProcessor操作,处理@Autowried等注解,将早期对象封装到ObjectFactory内,放入三级缓存
  • 进行依赖注入发现类型A,进行getBean(A)操作。
  • 获取A的BeanDefinition信息,先从一级缓存中获取,发现没有,检查CurrentlyCreationSet发现A在创建中,从三级缓存中获取到A的早期对象,并且将A对象升级到二级缓存
  • B完成依赖注入,将B对象存放至一级缓存,并且将二三级缓存中关于B的数据清理。
  • A完成依赖注入,将A存放至一级缓存,并且将二三级缓存中有关A的数据清理。

在这里插入图片描述

为什么这种机制无法解决原型模式下的setter依赖?
scope=“prototype”:意思是每次请求都会创建一个实例对象。而对于prototype作用域的Bean,Spring容器不进行缓存Bean对象,根本无法提前暴露一个创建中的Bean。

©️2020 CSDN 皮肤主题: 精致技术 设计师:CSDN官方博客 返回首页