7,Spring框架

7.1.1 为什么要使用Spring

Spring 是一个轻量级应用框架,它提供了 IoC 和 AOP 这两个核心的功能。
它的核心目的是简化企业级应用程序的开发,使得开发者只需要关心业务需求,不需要关心 Bean的管理,以及通过切面增强功能减少代码的侵入性。
从 Spring 本身的特性来看,我认为有几个关键点是我们选择 Spring 框架的原因。
 轻量:Spring 是轻量的,基本的版本大约 2MB。
 IOC/DI:Spring 通过 IOC 容器实现了 Bean 的生命周期的管理,以及通过 DI 实现依赖注入,从而实现了对象依赖的松耦合管理。
 面向切面的编程(AOP):Spring 支持面向切面的编程,从而把应用业务逻辑和系统服务分开。
 MVC 框架:Spring MVC 提供了功能更加强大且更加灵活的 Web 框架支持
 事务管理:Spring 通过 AOP 实现了事务的统一管理,对应用开发中的事务处理提供了非常灵活的支持
在业务开发领域,Spring 生态几乎提供了 非常完善地支持,更重要的是社区的活跃度和技术的成熟度都非常高。

7.1.2 Spring IOC的工作流程是怎样的

IOC 也就是控制反转,它的核心思想是把对象的管理权限交给容器
应用程序如果需要使用到某个对象实例,直接从 IOC 容器中去获取就行,这样设计的好处是降低了程序里面对象与对象之间的耦合性。使得程序的整个体系结构变得更加灵活。
在这里插入图片描述

7.1.3 Spring中 BeanFactory和FactoryBean的区别是什么

首先,Spring 里面的核心功能是 IOC 容器,所谓 IOC 容器呢,本质上就是一个 Bean 的容器或者是一个 Bean 的工厂。
它能够根据 xml 里面声明的 Bean 配置进行 bean 的加载和初始化,然后 BeanFactory 来生产我们需要的各种各样的 Bean。
所以我对 BeanFactory 的理解了有两个。
 BeanFactory 是所有 Spring Bean 容器的顶级接口,它为 Spring 的容器定义了一套规范,并提供像 getBean 这样的方法从容器中获取指定的 Bean 实例。
 BeanFactory 在产生 Bean 的同时,还提供了解决 Bean 之间的依赖注入的能力,也就是所谓的
DI。

FactoryBean 是一个工厂 Bean,它是一个接口,主要的功能是动态生成某一个类型的 Bean 的实例,也就是说,我们可以自定义一个 Bean 并且加载到 IOC 容器里面。
它里面有一个重要的方法叫 getObject(),这个方法里面就是用来实现动态构建 Bean 的过程。
Spring Cloud 里面的 OpenFeign 组件,客户端的代理类,就是使用了 FactoryBean 来实现的。

7.1.4 谈谈你对Spring Bean的理解

在 Spring 中,构成应用程序主干并由 Spring IoC 容器管理的对象称为 Bean。Bean 是一个由 SpringIoC 容器实例化、组装和管理的对象。
Spring 是通过声明式配置的方式来定义 Bean 的,所有创建 Bean 需要的前置依赖或者参数都是通过配置文件先声明,Spring 启动以后会解析这些声明好的配置内容。

7.1.5 Spring Bean的定义包含哪些内容, Spring 容器如何加载 Bean?

Spring 解析这些声明好的配置内容,将这些配置内容都转化为 BeanDefinition 对象,BeanDefinition中几乎保存了配置文件中声明的所有内容,再将 BeanDefinition 存到一个叫做 beanDefinitionMap中。以 beanName 作为 Key,以 BeanDefinition 对象作为 Value。之后 Spring 容器,根据 beanName找到对应的 BeanDefinition,再去选择具体的创建策略。而 Spring 具体的创建策略如图所示,
在这里插入图片描述

7.1.6 Spring中 Bean的作用域有哪些

首先呢,Spring 框架里面的 IOC 容器,可以非常方便地去帮助我们管理应用里面的 Bean 对象实例。
我们只需要按照 Spring 里面提供的 xml 或者注解等方式去告诉 IOC 容器,哪些 Bean 需要被 IOC 容器管理就行了。
其次呢,既然是 Bean 对象实例的管理,那意味着这些实例,是存在生命周期,也就是所谓的作用域。
理论上来说,常规的生命周期只有两种:
 singleton, 也就是单例,意味着在整个 Spring 容器中只会存在一个 Bean 实例。
 prototype,翻译成原型,意味着每次从 IOC 容器去获取指定 Bean 的时候,都会返回一个新的实例对象。
但是在基于 Spring 框架下的 Web 应用里面,增加了一个会话维度来控制 Bean 的生命周期,主要有三个选择
 request, 针对每一次 http 请求,都会创建一个新的 Bean
 session,以 sesssion 会话为纬度,同一个 session 共享同一个 Bean 实例,不同的 session 产生不同的 Bean 实例
 globalSession,针对全局 session 纬度,共享同一个 Bean 实例

7.1.7Spring Bean 生命周期的执行流程

Spring 生命周期全过程大致分为五个阶段:
创建前准备阶段、
创建实例阶段、
依赖注入阶段、
容器缓存阶段和销毁实例阶段。
1. 创建前准备阶段
这个阶段主要的作用是,Bean 在开始加载之前,需要从上下文和相关配置中解析并查找 Bean 有关的扩展实现, 比如像`init-method-容器在初始化 bean 时调用的方法、destory-method容器在销毁 bean 时调用的方法。
以及,BeanFactoryPostProcessor 这类的 bean 加载过程中的前置和后置处理。
这些类或者配置其实是 Spring 提供给开发者,用来实现 Bean 加载过程中的扩展机制,在很多和 Spring集成的中间件中比较常见,比如 Dubbo。
2. 创建实例阶段
这个阶段主要是通过反射来创建 Bean 的实例对象,并且扫描和解析 Bean 声明的一些属性。
3. 依赖注入阶段
如果被实例化的 Bean 存在依赖其他 Bean 对象的情况,则需要对这些依赖 bean 进行对象注入。比如常见的`@Autowired`setter 注入等依赖注入的配置形式。
同时,在这个阶段会触发一些扩展的调用,比如常见的扩展类:BeanPostProcessors(用来实现 bean初始化前后的扩展回调)、InitializingBean(这个类有一个 afterPropertiesSet(),这个在工作中也比较常见)、BeanFactoryAware 等等。
4. 容器缓存阶段
容器缓存阶段主要是把 bean 保存到容器以及 Spring 的缓存中,到了这个阶段,Bean 就可以被开发者使用了。
这个阶段涉及的操作,常见的有,init-method`这个属性配置的方法, 会在这个阶段调用。
以及像 BeanPostProcessors 方法中的后置处理器方法如:postProcessAfterInitialization,也会在这个阶段触发。
5. 销毁实例阶段
当 Spring 应用上下文关闭时,该上下文中的所有 bean 都会被销毁。
如果存在 Bean 实现了 DisposableBean 接口,或者配置了`destory-method`属性, 会在这个阶
段被调用。

7.1.8 Spring中Bean是线程安全的吗

Spring 中的 Bean 是否线程安全,其实跟 Spring 容器本身无关。Spring 框架中没有提供线程
安全的策略,因此,Spring 容器中的 Bean 本身也不具备线程安全的特性。

对于多例 Bean 每次都会新创建新实例,也就是说线程之间不存在 Bean 共享的问题。因此,多例 Bean 是不存在线程安全问题的。
而单例 Bean 是所有线程共享一个实例,因此,就可能会存在线程安全问题。但是单例 Bean 又分为无状态 Bean 和有状态 Bean。在多线程操作中只会对 Bean 的成员变量进行查询操作,不会修改成员变量的值,这样的 Bean 称之为无状态 Bean。所以,无状态的单例 Bean 是不存在线程安全问题的。但是,在多线程操作中如果需要对 Bean 中的成员变量进行数据更新操作,这样的 Bean 称之为有状态 Bean,所以,有状态的单例 Bean 就可能存在线程安全问题。
3、如何处理 Spring Bean 的线程安全问题?
处理有状态单例 Bean 的线程安全问题有以下两种方法:
1、将 Bean 的作用域由 “singleton” 单例 改为 “prototype” 多例。
2、在类中定义 ThreadLocal 的成员变量,并将需要的可变成员变量保存在 ThreadLocal 中,ThreadLocal 本身就具备线程隔离的特性,这就相当于为每个线程提供了一个独立的变量副本,每个线程只需要操作自己的线程副本变量,从而解决线程安全问题。

7.1.9 Spring中有几种依赖注入的方式

7.1.10 Spring 如何解决循环依赖的问题

问题解析
循环依赖是指一个或多个 Bean 实例之间存在直接或间接的依赖关系,构成循环调用。
通常表现为三种形态。
 互相依赖,也就是 A 依赖 B,B 依赖 A
 间接依赖,两个以上的 Bean 存在间接依赖关系造成循环调用
 自我依赖,自己依赖自己造成了循环依赖
Spring 本身也考虑到了这方面的问题,所以它设计了三级缓存来解决部分循环依赖的问题。
所谓三级缓存,其实就是用来存放不同类型的 Bean。
 第一级缓存存放完全初始化好的 Bean,这个 Bean 可以直接使用了
 第二级缓存存放原始的 Bean 对象,也就是说 Bean 里面的属性还没有进行赋值
 第三级缓存存放 Bean 工厂对象,用来生成原始 Bean 对象并放入到二级缓存中

假设 BeanA 和 BeanB 存在循环依赖,那么在三级缓存的设计下,
 初始化 BeanA,先把 BeanA 实例化,然后把 BeanA 包装成 ObjectFactory 对象保存到三级缓存
中。
 接着 BeanA 开始对属性 BeanB 进行依赖注入,于是开始初始化 BeanB,同样做两件事,创建 BeanB 实例,以及加入三级缓存。
 然后,BeanB 也开始进行依赖注入,在三级缓存中找到了 BeanA,于是完成 BeanA 的依赖注入
 BeanB 初始化成功以后保存到一级缓存,于是 BeanA 可以成功拿到 BeanB 的实例,从而完成正
常的依赖注入。
在这里插入图片描述
整个流程看起来很复杂,但是它的核心思想就是把 Bean 的实例化和 Bean 中属性的依赖注入这两个过程分离出来。
不过要注意的是,Spring 本身只能解决单实例存在的循环引用问题,但是存在以下四种情况需要人为干预:
 多实例的 Setter 注入导致的循环依赖,需要把 Bean 改成单例。
 构造器注入导致的循环依赖,可以通过@Lazy 注解
 DependsOn 导致的循环依赖,找到注解循环依赖的地方,迫使它不循环依赖。
 单例的代理对象 Setter 注入导致的循环依赖,
 可以使用@Lazy 注解,
 或者使用@DependsOn 注解指定加载先后关系。
在实际开发中,出现循环依赖的根本原因还是在代码设计的时候,因为模块的耦合度较高,
依赖关系复杂导致的,我们应该尽可能地从系统设计角度去考虑模块之间的依赖关系,避免循环依赖的问题

7.1.11 Spring中用到了哪些设计模式

  1. 工厂模式(Factory Pattern)
    Spring 使用工厂模式来创建对象实例。BeanFactory和ApplicationContext是 Spring 框架中实现工厂模式的核心接口。
    ● 应用场景:Spring 容器负责创建和管理 bean 的实例。

ApplicationContext context = new ClassPathXmlApplicationContext(“beans.xml”);
MyBean myBean = (MyBean) context.getBean(“myBean”);

  1. 单例模式(Singleton Pattern)
    Spring 默认以单例模式管理 bean,这意味着每个 bean 在 Spring 容器中只有一个实例。
    ● 应用场景:默认情况下,Spring 容器中的每个 bean 都是单例的。
    @Component
    public class MySingletonBean {
    // This bean will be a singleton
    }
  2. 代理模式(Proxy Pattern)
    Spring AOP(面向切面编程)使用代理模式来创建代理对象,以实现方法拦截和增强功能。
    ● 应用场景:AOP 实现事务管理、日志记录、安全检查等。
    @Aspect
    public class LoggingAspect {
    @Before(“execution(* com.example.service..(…))”)
    public void logBeforeMethod(JoinPoint joinPoint) {
    System.out.println("Executing method: " + joinPoint.getSignature().getName());
    }
    }
  3. 模板方法模式(Template Method Pattern)
    Spring 提供了多种模板类(如JdbcTemplate、RestTemplate),这些类封装了常见的操作步骤,允许用户只需实现特定的步骤。
    ● 应用场景:简化数据库操作、RESTful 服务调用等。
    @Autowired
    private JdbcTemplate jdbcTemplate;

public void saveData(String data) {
String sql = “INSERT INTO my_table (data) VALUES (?)”;
jdbcTemplate.update(sql, data);
}
5. 观察者模式(Observer Pattern)
Spring 的事件处理机制使用观察者模式。ApplicationEventPublisher和ApplicationListener是实现观察者模式的核心接口。
● 应用场景:实现事件驱动的编程模型。
public class MyEvent extends ApplicationEvent {
public MyEvent(Object source) {
super(source);
}
}

@Component
public class MyEventListener implements ApplicationListener {
@Override
public void onApplicationEvent(MyEvent event) {
System.out.println("Received event: " + event.getSource());
}
}

@Component
public class MyEventPublisher {
@Autowired
private ApplicationEventPublisher applicationEventPublisher;

public void publishEvent() {
    MyEvent event = new MyEvent(this);
    applicationEventPublisher.publishEvent(event);
}

}
6. 依赖注入模式(Dependency Injection Pattern)
Spring 的核心功能之一就是依赖注入,通过构造函数注入、setter 注入或字段注入,将对象的依赖关系注入到对象中。
● 应用场景:解耦对象之间的依赖关系,便于测试和维护。
@Component
public class MyService {
private final MyRepository myRepository;

@Autowired
public MyService(MyRepository myRepository) {
    this.myRepository = myRepository;
}

}
7. 装饰器模式(Decorator Pattern)
Spring 使用装饰器模式来增强 bean 的功能,特别是在 AOP 中,通过将增强逻辑应用到目标对象上。
● 应用场景:动态地为对象添加职责,而不影响其他对象。
@Aspect
public class PerformanceAspect {
@Around(“execution(* com.example.service..(…))”)
public Object measureExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
Object result = joinPoint.proceed();
long executionTime = System.currentTimeMillis() - start;
System.out.println("Method executed in: " + executionTime + “ms”);
return result;
}
}
8. 策略模式(Strategy Pattern)
Spring 中的TransactionManager使用策略模式来定义事务管理的策略,允许在运行时选择不同的事务管理器。
● 应用场景:定义一系列算法,允许在运行时选择具体的算法。
@Configuration
public class AppConfig {
@Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource());
}
}
单例模式
Spring默认创建的Bean就是单例的。进一步的,依赖注入Bean实例默认是double check的单例模式。并且用ConcurrentHashMap保证线程安全。
代理模式
AOP底层就是动态代理模式的实现。分JDK动态代理和CGLIB两种默认使用jdk动态代理,如果开启了cglib则优先使用cglib。如果没有接口,jdk动态代理会失效。cglib则没有这个问题
工厂方法Spring中的BeanFactory就是工厂模式的体现,将对象的创建交给工厂来做。
原来依赖方和被依赖方耦合在一起,现在beanFactory将bean之间的耦合打开了。依赖方到工厂里拿依赖,不直接和被依赖方接触耦合了,
模版模式
模板方法模式是一种行为设计模式,模版模式类似继承,父类定义规则,子类具体实现
Spring 中 jdbcTemplate、hibernateTemplate 等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。
装饰者模式
Spring 在配置 DataSource 的时候,DataSource 可能是不同的数据库和数据源。这个时候就要用到装饰者模式。Spring 中用到的包装器模式在类名上含有 Wrapper或者 Decorator。这些类基本上都是动态地给一个对象添加一些额外的职责。
适配器模式
实现方式:SpringMVC中的适配器HandlerAdatper。
实现原理:HandlerAdatper根据Handler规则执行不同的Handler。
实现过程:DispatcherServlet根据HandlerMapping返回的handler,向HandlerAdatper发起请求,处理Handler。HandlerAdapter根据规则找到对应的Handler并让其执行,执行完毕后Handler会向HandlerAdapter返回一个ModelAndView,最后由HandlerAdapter向DispatchServelet返回一个ModelAndView。
实现意义:HandlerAdatper使得Handler的扩展变得容易,只需要增加一个新的Handler和一个对应的HandlerAdapter即可。因此Spring定义了一个适配接口,使得每一种Controller有一种对应的适配器实现类,让适配器代替controller执行相应的方法。这样在扩展Controller时,只需要增加一个适配器类就完成了SpringMVC的扩展了。
观察者模式
观察者模式是一种对象行为型模式。a观察b的行为,b发生时a相应的做出反应、Spring 事件驱动模型就是观察者模式很经典的一个应用。主要包括三个组成部分:事件,事件发布者,事件订阅者,当事件发生时,事件订阅者相应的做出改变。

7.1.12 Spring中的事务传播行为有哪些

事务传播行为,它解决的核心问题是,多个声明了事务的方法相互调用的时候存在事务嵌套问题,那么这个事务的行为应该如何进行传递?
(如图)比如说,methodA()调用 methodB(),两个方法都显示的开启了事务。
那么 methodB()是开启一个新事务,还是继续在 methodA()这个事务中执行?就取决于事物的传播行为。
在这里插入图片描述
所以,Spring 为了解决这个问题,定义了 7 种事务传播行为。

  1. REQUIRED:默认的 Spring 事务传播级别,如果当前存在事务,则加入这个事务,如果不存在事务,就新建一个事务。
  2. REQUIRE_NEW:不管是否存在事务,都会新开一个事务,新老事务相互独立。外部事务抛出异常回滚不会影响内部事务的正常提交。
  3. NESTED:如果当前存在事务,则嵌套在当前事务中执行。如果当前没有事务,则新建一个事务,类似于 REQUIRE_NEW。
  4. SUPPORTS:表示支持当前事务,如果当前不存在事务,以非事务的方式执行。
  5. NOT_SUPPORTED:表示以非事务的方式来运行,如果当前存在事务,则把当前事务挂起。
  6. MANDATORY:强制事务执行,若当前不存在事务,则抛出异常。
  7. NEVER:以非事务的方式执行,如果当前存在事务,则抛出异常。

7.1.15 谈谈你对Spring Aop的理解

分为四个阶段给大家介绍:创建代理对象阶段、拦截目标对象阶段、调用代理对象阶段、调用目标对象阶段。
1、第一阶段:创建代理对象阶段
在 Spring 中,创建 Bean 实例都是从 getBean()方法开始的,在实例创建之后,Spring 容器将根据 AOP 的配置去匹配目标类的类名,看目标类的类名是否满足切面规则。如果满足切面规则,就会调用 ProxyFactory 创建代理 Bean 并缓存到 IoC 容器中。 根据目标对象的自动选择不同的代理策略。 如果目标类实现了接口,Spring 会默认选择 JDK Proxy,如果目标类没有实现接口,Spring 会默认选择 CglibProxy,当然,我们也可以通过配置强制使用 Cglib Proxy。
2、第二阶段:拦截目标对象阶段
当用户调用目标对象的某个方法时,将会被一个叫做 AopProxy 的对象拦截,Spring 将所有的调用策略封装到了这个对象中,它默认实现了 InvocationHandler 接口,也就是调用代理对象的外层拦截器。在这个接口的 invoke()方法中,按顺序执行符合所有 AOP 拦截规则的拦截器链。
3、第三阶段:调用代理对象阶段
Spring AOP 拦截器链中的每个元素被命名为 MethodInterceptor,其实就是界面配置中的 Advice 通知。这个回调通知可以简单地理解为是新生成的代理 Bean中的方法。也就是我们常说的被织入的代码片段,这些被织入的代码片段会在这个阶段执行.
4、第四阶段:调用目标对象阶段
MethodInterceptor 接口也有一个 invoke()方法,在 MethodInterceptor 的 invoke()方法中会触发对目标对象方法的调用,也就是反射调用目标对象的方法。
在这里插入图片描述

  • 13
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值