spring是什么

spirng是一个基于java语言开发的用于web的轻量级框架,

ioc和aop是spring的核心。

并且让开发人员专心于业务开发

spring 中的一些重要组件

core核心组件:其中beans和context是实现IOC的重要依赖(依赖注入)

aop组件主要用来做切面编程

web组件实现了web层的技术,例如springmvc对于请求做处理的

websocket:全双工及时通讯,服务器也能主动发送消息给客户端。

transactions: spring事务的管理依赖

JDBC、ORM、OXM等

Portlet被替换成webflex了

image-20231129170928603

spring给我们带来了什么好处

对象解耦,对于一些类我们可以交给spring 来管理,而不用每次都自己来创建。这样不仅解决的硬编码的解耦,而且提高了对象的复用

AOP的支持,对于一些通用的功能,我们无需对很多地方进行修改,而是通过切面编程来处理,例如日志记录、认证权限的处理等

声明式事务,我们可通过transational这个注解来对事物进行处理,如果处理事务中失败则可回滚恢复数据

测试的支持,由于spirng提供的ioc,我们在单元测试中可通过直接注入某个实例进行测试,调用需要测试的方法,非常好用

多框架的支持,spring几乎集成了我们创建的例如mybatis、junit等

聊聊spring 中的IOC

ioc(控制反转)是一种规范,其他语言也能有自己的实现方式。spring 通过依赖注入的方式来实现ioc

spring中的对象是很多的并且关系可能也是十分复杂的,如果我们手动来管理这些对象,将很繁琐。如果交给spring管理,对象的创建、赋值、初始化都有spring来统一管理执行。这样就让我们有更多精力放在业务上。如果有个对象依赖很深,我们通过手动的方式来创建然后赋值,必定很繁琐。代码也不美观

springIOC的实现原理

spring 在启动的时候会去读取我们的配置文件(xml配置文件或者注解,@Component或者@ Autowired),生成bean的注册表,然后根据这个注册表通过反射实例化bean。最终将这些实例化的bean放入单例池中(一个hashmap),并且springioc还提供了实例缓存、生命周期管理、bean实例的代理(可能是原来的bean或者代理过得bean,根据配置来)

spirng 的注入方式有哪些

xml方式

  1. setter方法注入
  2. 构造器注入
  3. 静态工厂方法(需要对象仅调用静态方法即可,无需关注方法内部怎么实现的。直接调;需要自己定义一个工厂,然后注册成工厂,指名工厂方法,其他依赖的ref需要指明这个工厂bean)
  4. 普通工厂,和静态类似,但是需要先创建这个工厂,才能调用这个方法。

注解方式

  1. @Autowired: 默认通过bean类型注入,如果需要来名称则还需要添加@Qualifier配置名称。如果不能唯一指定一个bean会报错。例如:如果有两个相同类型的,但是变量名不同,使用@Primary为首要,@Resource也可以,
  2. @Resource:默认通过bean名称,如果设置类型则需要两个都相同才能注入
  3. @Inject:需要下载依赖,和第一个注解类似

注解类型推断

如果我们没有指定用那个构造器来依赖注入属性,spirng会自动帮助我们来选择使用那个构造器注入。其中如果多个构造器的参数一致会从上往下,优先选择。否认按照参数多的来。

@Autowired自动装配流程

可配置地方:方法、属性、构造器,会先去找到对应的type类型,然后是对应的名称。名称如果自己设置的话会使用设置的名称,还是找不到类型和名称唯一的就报错。其中构造器会有自动推断的功能,一般会根据参数个数来选择使用的构造器进行注入,相同参数个会选择代码中最上面的,参数个数不同就选择参数多的。

@Resource自动装配流程

可配置地方:属性和方法,会先根据名称查找然后才是类型。如果不唯一还是会报错。

@Component和@Bean区别

相同:都能注册bean

不同:

  1. component应用在类上或者其他注解上,bean一般用在方法上
  2. component应用在很多注解里,比如controller、service、configration等
  3. bean能直接在注解内可以指定初始方法和销毁前方法等
  4. component使用更简单

@Value中的$和#区别

${}代表环境信息,一般获取环境信息,spring cloud和spring boot中的application.properties文件中的内容和nacos配置中动态获取某个配置信息

#{}可以为属性进行赋值,例如对某个属性注入值(依赖注入)

springbean的理解

springbean是由spring管理创建的bean,这个bean可能是实体对象也有可能是controller、service或者dao对象。它并不和我们直接new出来的对象是一样的,它是通过反射来创建的,在创建过程中会对bean的属性进行赋值、依赖注入以及一些aop的处理操作,并且可能返回的并不是原始bean,而是被代理过得bean。所以和我们手动new出来的bean,然后手动去赋值,处理一些依赖管理有本质区别。

定义bean 的方式有那些

  1. xml
  2. 注解:component、controller、service。。。
  3. 利用spring的容器去注册bean
  4. @Import + 其他注解(component、configration..)

bean的作用域

单例 (默认)

原型(多例)

request

session:仅仅在portlet中有用,但是spring 5 使用webflex替换了portlet,所以已经无法使用了

websocket

注解通过scope,xml也是配置scope这个属性

spring 懒加载机制(延迟加载)

如果开启懒加载(注解@Lazy),仅在我们使用的时候才创建初始化bean

spring控制bean的加载顺序

如果有两个beanA和beanB,其中beanA需要操作beanB的数据,如果beanB的加载在beanA的后面,就会出问题

  1. @DependsOn 注解来提前加载
  2. 通过@Bean注解,默认没有参数会根据代码从上往下加载,如果参数中有其他,则会先加载参数中的(初始化a中需要b,就会去先加载b)

spring的内部bean(inner bean)

不用注册格外的id来声明bean,直接设置需要依赖这个bean的property,通过这种匿名方式在注入bean。setter和构造方法都可以(xml中)

springAOP

对于程序中,有一些公共的功能,可用过aop来实现,这样不用到每个功能中去添加重复代码,修改起来也不方便,一个地方修改牵连多处代码,难以维护。具体一些功能有:请求日志监控、性能检测、一些鉴权的功能, 以及事务切面。

  1. 切面:在Java中某个类,管理切点和通知,一种功能类表示一个切面
  2. 连接点:需要增强的方法
  3. 切点:通过切点表达式来决定那些方法需要增强和那些方法不需要增强
  4. 织入:目标对象创建动态代理过程
  5. 通知:对增强方法的具体增强逻辑,前置通知,后置通知,异常通知,最终通知、环绕通知
  6. 顾问(Advisor):对通知和切点的包装,管理通知和切点
  7. 引入:可以将其他接口和实现动态引入targetclass
  8. 目标对象:需要被增强的目标对象,原来的业务对象

FactoryBean和BeanFoctory区别

factoryBean也是一个bean,受beanfactoryBean管理,

如果我们实现了factoryBean,然后返回的不是原来的bean而是实现的getObject这个方法返回的bean (getBean的时候添加&符号可获得原来的bean)

IOC容器加载过程

  1. 实例化一个applicationContext的对象
  2. 调用bean工厂后置处理器来完成扫描
  3. 循环解析扫描出来的类信息
  4. 实例化一个beanDefinition,并放入到beanDefinitionMap中缓存,以便后续实例化使用
  5. 再次调用其他bean工厂后置处理器
  6. 实例化之前会对bean做验证,判定这个bean是否是懒加载是否是非单例的等等
  7. 验证完成,spring会自动推断然后调用构造方法,然后通过反射实例化对象;此对象现在并不完整,可能还需要对属性进行注入。
  8. 然后判定bean类型调用aware接口,再调用生命周期回调方法,如果需要代理则完成代理
  9. 创建完成,将bean对象放入singletonObjects中

spring IOC有哪些扩展点,什么时候调用

在注册bean定义的时候:

BeanDefinitionRegistryPostProcessor:实现此接口,可手动对bena定义定义和操作(这个接口继承了下面接口)
BeanFactoryPostProcessor:实现此接口可对bean定义可以操作,不可以获取bean定义

在初始化bean的时候:

BeanNameAware、BeanFactoryAware等,BeanPostProcessor 对bean初始化前后操作

在bean销毁的时候:

DisposableBean或者xml配置文件中设置销毁方法

spring 的bena 是线程安全的吗

spring中默认的bean的实例作用域是单例的,是需要看你的操作变量是属于类的还是方法的如果是方法的则是安全否则不安全除非对方法加了锁之类的

还是看代码具体是怎么写的。

spring 中如何处理并发问题

  1. 设置作用域为多例
  2. 加锁
  3. threadlocal

spring 实例化bean的方式

  1. 反射:xml配置中有类的全路径,然后反射出对象
  2. 静态工厂:xml配置中指定静态工厂实例化
  3. 实例工厂:通过指定factory-bean、和factory-method来指定工厂和生产实例的工厂方法
  4. FactoryBean方式:实现factoryBean的getObjects方法返回任意Bean

spring 的生命周期

  1. 实例化
  2. 依赖注入
  3. 初始化(初始化前后有个postprocessor可以自定义)
  4. 使用
  5. 销毁

spring 在并发情况下怎么避免获取不完整的bean

通过双重检查锁的方式处理,线程a 初始化对象时会将二三级缓存给加上锁,线程b后入发现一级缓存中没有想要的bean实例,就回去二三级中查找,此时就会堵塞住。a线程处理完成后,线程b会再一次去尝试拿去,此时就会拿到想要的bena实例。如果还是没有则线程b才会去创建这个想要的bean

描述创建beandefinition的加载过程

当我们去创建一个spring容器的时候,spirng去读取配置信息,然后解析配置去,比如:如果是去解析component的话,就会去扫描带有这注解的类,将这个类的信息注册为beandefinition,然后放入到beandefinitionMap中。

如何在spring的bean创建完成后做扩展

实现 SmartInitializingSingleton

还有一种通过spring的事件来通知bena已经加载完毕了。refreshEvent

如何在所有BeanDefinition注册完成后做扩展

实现 beanFctoryPostProcessor(bean工厂后置处理器);

bean实例是 beanPostProcessor

bean的生产顺序是由什么确定的

是有beandefinition来决定的;

beandefinition是有注解配置解析的顺序确定的

@Configuration > @Component > @Import一类 > @Bean > @Import

@Import几种用法

  1. @Import(User.class),直接注册一个bena
  2. @Import(ImportSelectorTest.class), 放入一个实现了ImportSelector接口的类,可注册多个bean
  3. @Import(ImportBeanDefinitionRegistrarTest.class), 这种是对bean定义的操作,可通过注册bean定义

@Configuration的作用和原理

用于代替xml方式配置,可以配置一些bean信息,以及一些数据源信息。

此注解中包含了@component注解

@Bean方法调用如何保证是单例的

@Configuration中的cglib动态代理来保证的。通过对配置类的增强,对与@Bean的方法进行增强,实际会去IOC容器中获取这个bean 的单例

注册一个第三方的Bean

  1. 通过@Improt注解
  2. 通过@Bean来手动new
  3. Bean定义注册后置处理器

为什么没有设置@CompoentScan没有设置也能扫描到

如果设置了就会使用你设置的,如果没有设置,就会把当前类的路径设置为默认的路径。