Spring面试题大全

一、Spring Framework

1.谈谈你对Spring的理解

什么是spring
Spring是一个生态:可以构建java应用所需的一切基础设施通常Spring指的就是Spring Framework
核心解释
1、spring是一个轻量级的开源容器框架。
2、spring是为了解决企业级应用开发的业务逻辑层和其他各层对象和对象直接的耦合问题
3、spring是一个IOC和AOP的容器框架。
IOC:控制反转
AOP:面向切面编程
容器:包含并管理应用对象的生命周期

2.Spring的优缺点是什么?

优点:

1.方便解耦,简化开发
通过Spring提供的IoC容器,我们可以将对象之间的依赖关系交由Spring进行控制,避免硬编码所造成的过度程序耦合。
有了Spring,用户不必再为单实例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。
人话:集中管理对象,对象和对象之间的耦合度减低,方便维护对象。

2.AOP编程的支持

通过Spring提供的AOP功能,方便进行面向切面的编程,许多不容易用传统OOP实现的功能可以通过AOP轻松应付。
Spring的AOP支持允许将一些通用任务如安全、事务、日志等进行集中式管理,从而提供了更好的复用.
人话: 在不修改代码的情况下可以对业务代码进行增强 减少重复代码 提高开发效率 维护方便

3.声明事物的支持

在Spring中,我们可以从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活地进行事务的管理,提高开发效率和质量。
人话:提高开发效率,只需要一个简单注解@Transactional

4.方便程序的测试

可以用非容器依赖的编程方式进行几乎所有的测试工作,在Spring里,测试不再是昂贵的操作,而是随手可做的事
情。例如:Spring对Junit4支持,可以通过注解方便的测试Spring程序。

人话: Spring实现测试 使我们 可以结合junit非常方便测试Spring Bean SpringMVC

5.方便集成各种优秀框架

Spring不排斥各种优秀的开源框架,相反,Spring可以降低各种框架的使用难度,Spring提供了对各种优秀框架
(如Struts,Hibernate、Hessian、Quartz)等的直接支持。

人话: 拥有非常强大粘合度、集成能力非常,只需要简单配置就可以集成第三方框架

6.降低Java EE API的使用难度

Spring对很多难用的Java EE API(如JDBC,JavaMail,远程调用等)提供了一个薄薄的封装层,通过Spring的简易
封装,这些Java EE API的使用难度大为降低。

人话:简化开发, 帮我封装很多功能性代码

7.Java 源码是经典学习范例

Spring的源码设计精妙、结构清晰、匠心独用,处处体现着大师对Java设计模式灵活运用以及对Java技术的高深造
诣。Spring框架源码无疑是Java技术的最佳实践范例。如果想在短时间内迅速提高自己的Java技术水平和应用开发水
平,学习和研究Spring源码将会使你收到意想不到的效果。

人话:学习到了Spring底层的实现、反射…设计模式 都是我们值得学习, 提供非常多的扩展接口供外部进行扩展

缺点
从应用层面来说是没有缺点的
简化开发, 如果想深入到底层去了解就非常困难(上层使用越简单、底层封装得就越复杂)
源码缺点:由于spring 大而全(要集成这么多框架、提供非常非常多的扩展点,经过十多年的代码迭代) 代码量非常庞大 一百多万 , 对于去深入学习源码带来了一定困难。

二、Spring IOC

3.什么是Spring IOC 容器?有什么作用?

什么是Spring IOC 容器
控制反转即IoC (Inversion of Control),它把传统上由程序代码直接操控的对象的调用权交给容器,通过容器来实现对象组件的装配和管理。所谓的“控制反转”概念就是对组件对象控制权的转移,从程序代码本身转移到了外部容器。
Spring IOC 负责创建对象,管理对象(通过依赖注入(DI),装配对象,配置对象,并且管理这些对象的整个生命周期。
对于 IOC 来说,最重要的就是容器。容器管理着 Bean 的生命周期,控制着 Bean 的依赖注入。

控制反转(IoC)有什么作用
管理对象的创建和依赖关系的维护。 对象的创建并不是一件简单的事,在对象关系比较复杂时,如果依赖关系需要程序猿来维护的话,那是相当头疼的
解耦,由容器去维护具体的对象,托管了类的产生过程,比如我们需要在类的产生过程中做一些处理,最直接的例子就是代理,如果有容器程序可以把这部分处理交给容器,应用程序则无需去关心类是如何完成代理的

作用:
控制反转 控制了什么?
UserService service=new UserService(); // 耦合度太高 、维护不方便
引入Ioc 就将创建对象的控制权交给Spring的Ioc. 以前由程序员自己控制对象创建, 现在交给Spring的Ioc去创建,如果要去使用对象需要通过DI(依赖注入)@Autowired 自动注入 就可以使用对象
;

优点: 1.集中管理对象、方便维护 。2.降低耦合度

IOC的优点是什么?
最小的代价和最小的侵入性使松散耦合得以实现。
IOC容器支持加载服务时的饿汉式初始化和懒加载。

4.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 hello{
     
     public static void main(String[] a){
     
         fruit f=Factory.getInstance("Reflect.Apple");  
         if(f!=null){
     
             f.eat();  
         }  
     }  
 }  

5.什么是Spring的依赖注入(DI)?IOC和DI的区别是什么

很多人把IOC和DI说成一个东西,笼统来说的话是没有问题的,但是本质上还是有所区别的,希望大家能够严谨一点,

IOC和DI是从不同的角度描述的同一件事,IOC是从容器的角度描述,而DI是从应用程序的角度来描述,也可以这样说,IOC是依赖倒置原则的设计思想,而DI是具体的实现方式
人话:DI(依赖注入)是IOC的一个具体实现方式,IOC是一种解决耦合问题的设计思想

在面向对象设计的软件系统中,底层的实现都是由N个对象组成的,所有的对象通过彼此的合作,最终实现系统的业务逻辑。
在这里插入图片描述
有一个对象出了问题,就可能会影响到整个流程的正常运转。现在,伴随着工业级应用的规模越来越庞大,对象之间的依赖关系也越来越复杂,经常会出现对象之间的多重依赖性关系,因此,架构师和设计师对于系统的分析和设计,将面临更大的挑战。对象之间耦合度过高的系统,必然会出现牵一发而动全身的情形。在这里插入图片描述
大家看到了吧,由于引进了中间位置的“第三方”,也就是IOC容器,对象和对象之间没有了耦合关系, 它起到了一种
类似“粘合剂”的作用,把系统中的所有对象粘合在一起发挥作用,如果没有这个“粘合剂”,对象与对象之间会彼此
失去联系,这就是有人把IOC容器比喻成“粘合剂”的由来。

6.紧耦合和松耦合有什么区别?

一、区别

紧耦合:紧密耦合是指类之间高度依赖
松耦合:松耦合是通过促进单一职责和关注点分离、依赖倒置的设计原则来实现的。

二、额外扩展
松耦合架构和紧耦合架构是软件系统设计中两种常用的架构模式。它们代表了不同的设计原则和关注方向,对于软件设计和开发人员来说,选择合适的架构模式非常重要,它将直接影响到软件系统的可靠性、可维护性、可扩展性和可重用性。下面在阐述两种架构的基础特点的同时,以LAXCUS分布式操作系统的系统发展过程为例,说说两种架构的各自优劣和特点。

咱们先说松耦合架构。

松耦合架构是一种基于低耦合度的软件设计方法。在这种架构中,各个组件之间的依赖关系非常弱,它们可以独立地进行修改和扩展。这种设计方式的优点在于,系统更加灵活,易于维护和升级,并且稳定性和可靠性也更强。由于各个组件之间没有太多的依赖关系,因此在需要修改或更新某个组件时,其他组件不会受到影响。此外,松耦合架构解耦了关联和依赖,它的代码的可读性和可维护性也比较好。

与之相反的是紧耦合架构。

在紧耦合架构中,各个组件之间的依赖关系非常强,它们通常会通过共享数据结构或接口来实现通信。这种设计方式的优点在于,系统实时处理能力和响应能力更好,但是由于各个组件之间的依赖和关联关系比较紧密,因此如果其中一个组件出现问题,也可能导致整个系统崩溃。此外,紧耦合架构实时处理能力和响应能力好,它可以减少数据传输和处理的时间。

紧耦合架构还有很多缺点。首先,它的可维护性和可扩展性较差。因为各个组件之间的依赖关系过于紧密,因此在需要修改或添加新的功能时,可能需要对整个系统进行大规模的重构。其次,紧耦合架构也容易导致代码冗余和重复工作。由于各个组件之间必须共享数据结构或接口,因此很容易出现重复编写代码的情况。

7.BeanFactory的作用

BeanFactory是Spring中非常核心的一个顶层接口;
它是Bean的“工厂”、它的主要职责就是生产Bean;
它实现了简单工厂的设计模式,通过调用getBean传入标识生产一个Bean;
它有非常多的实现类、每个工厂都有不同的职责(单一职责)功能,最强大的工厂是:DefaultListableBeanFactory
Spring底层就是使用的该实现工厂进行生产Bean的
BeanFactory它也是容器 Spring容器(管理着Bean的生命周期)

8. BeanDefinition的作用

它主要负责存储Bean的定义信息:决定Bean的生产方式

如:spring.xml

 <bean class="com.tuling.User" id="user" scope="singleton" lazy="false" abstract="false" autowire="none" ....>
	<property name="username" value="xushu">
</bean

后续BeanFactory根据这些信息就行生产Bean: 比如实例化 可以通过class进行反射进而得到实例对象 , 比如lazy 则不会在ioc加载时创建Bean

二、额外扩展

BeanDefinition中的常用属性

beanClass:表示Bean类型,未加载类的时候存放Bean的名字,加载类后存放Bean的class信息。
scope:表示Bean的作用域,一般值为单例或者原型。
lazyInit:表示Bean是否是懒加载。
initMethodName:Bean初始化需要执行的方法。
destroyMethodName:Bean销毁时要执行的方法。
factoryBeanName:创建当前Bean的工厂。

几种Bean的定义方式

@Bean

@Component(@Service,@Controller)

编程式定义Bean

// 得到beanDefinition 
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
// 把一个类设置为Bean
beanDefinition.setBeanClass(Test.class);
// 把这个Bean注册到容器中
context.registerBeanDefinition("test", beanDefinition);

BeanDefinition是如何存放的?

private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

当Bean注册完成后,会在spring容器中把扫描到的beanDefinition存放到beanDefinitionMap中,方便后续的使用。

9. BeanFactory 和 ApplicationContext有什么区别?

BeanFactory和ApplicationContext是Spring的两大核心接口,
BeanFactory的子接口。

依赖关系

BeanFactory:是Spring里面最顶层的接口,包含了各种Bean的定义,读取bean配置文档,管理bean的加载、实例化,控制bean的生命周期,维护bean之间的依赖关系。

BeanFactory 简单粗暴,可以理解为就是个 HashMap,Key 是 BeanName,Value 是 Bean 实例。通常只提供注册(put),获取(get)这两个功能。我们可以称之为 “低级容器”。

ApplicationContext 可以称之为 “高级容器”。因为他比 BeanFactory 多了更多的功能。他继承了多个接口。因此具备了更多的功能。例如资源的获取,支持多种消息(例如 JSP tag 的支持),对BeanFactory 多了工具级别的支持等待。所以你看他的名字,已经不是 BeanFactory 之类的工厂了,而是 “应用上下文”, 代表着整个大容器的所有功能。该接口定义了一个 refresh 方法,此方法是所有阅读 Spring 源码的人的最熟悉的方法,用于刷新整个容器,即重新加载/刷新所有的 bean。
ApplicationContext 类似于4s店做的事情更多

ApplicationContext接口作为BeanFactory的派生,除了提供BeanFactory所具有的功能外,还提供了更完整的框架功能:在这里插入图片描述

10.BeanFactory 和FactoryBean有什么区别?

BeanFactory是一个工厂,也就是一个容器,是来管理和生产bean的;

FactoryBean是一个bean,但是它是一个特殊的bean,所以也是由BeanFactory来管理的,它是一个接口,他必须被一个bean去实现。不过FactoryBean不是一个普通的Bean,它会表现出工厂模式的样子,是一个能产生或者修饰对象生成的工厂Bean,里面的getObject()就是用来获取FactoryBean产生的对象。所以在BeanFactory中使用“&”来得到FactoryBean本身,用来区分通过容器获取FactoryBean产生的对象还是获取FactoryBean本身。

代码演示

/**
 * @className: MyFactoryBean
 * @Description: TODO 自定义FactoryBean
 * @version: v1
 * @author: GONGWENXUE
 * @date: 2022/5/19 11:46
 */
@Component("MyFactoryBean")
public class MyFactoryBean implements FactoryBean {
   

    /**
     * @Author GONGWENXUE
     * @Description //TODO 此方法返回要注入的bean,也就是@Component("xx"),
     *               外界取值时用"xx" 取到的是getObject()返回的对象
     *               如果想取MyFactoryBean,需要在注入的key值前 + &,也就是"&xx"
     * @version: v1
     * @Date 11:48 2022/5/19
     * @Param
     * @return
     **/
    @Override
    public Object getObject() throws Exception {
   
        System.out.println("返回MyFactoryBean的getObject对象");
        Object o = new Object();
        return o;
    }

    @Override
    public Class<?> getObjectType() {
   
        return MyFactoryBean.class;
    }

    @Override
    public boolean isSingleton() {
   
        return true;
    }
}
@Configuration
public class AppConfig {
   

    @Bean("myFactoryBean")
    public MyFactoryBean getMyBean() {
   
        return new MyFactoryBean();
    }
}
 public static void main(String[] args) {
   
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        Object myFactoryBean = context.getBean("&myFactoryBean");
        System.out.println(myFactoryBean);
    }

总结:
1.BeanFactory是spring的bean工程,生产管理bean;

2.FactoryBean是一个spring开放给用户自定义bean的接口,以方便第三方技术的接入和自定义的需要。继承FactoryBean的实现类交给spring管理后,会以懒加载的方式,用到的时候才初始化,并产生两个对象注入给spring,其中getObject()的返回值对象以@Component(“key”)的方式注入,FactoryBean的实现类以@Component(“&key”)的方式注入。

11. IOC容器的加载过程:

一、加载过程
1、从概念态—>定义态的过程
1、实例化一个ApplicationContext的对象;
2:调用bean工厂后置处理器完成扫描;
3:循环解析扫描出来的类信息;
4、实例化一个BeanDefinition对象来存储解析出来的信息;
5、把实例化好的beanDefinition对象put到beanDefinitionMap当中缓存起来, 以便后面实例化bean;
6、再次调用其他bean工厂后置处理器;
2、从定义态到纯净态
7:当然spring还会干很多事情,比如国际化,比如注册BeanPostProcessor等 等,如果我们只关心如何实例化一个bean的话那么这一步就是spring调用 finishBeanFactoryInitialization方法来实例化单例的bean,实例化之前spring要做验证, 需要遍历所有扫描出来的类,依次判断这个bean是否Lazy,是否prototype,是否 abstract等等;
8:如果验证完成spring在实例化一个bean之前需要推断构造方法,因为spring实 例化对象是通过构造方法反射,故而需要知道用哪个构造方法;
9:推断完构造方法之后spring调用构造方法反射实例化一个对象;注意我这里说 的是对象、对象、对象;这个时候对象已经实例化出来了,但是并不是一个完整的bean, 最简单的体现是这个时候实例化出来的对象属性是没有注入,所以不是一个完整的bean;
3、从纯净态到成熟态
10:spring处理合并后的beanDefinition
11:判断是否需要完成属性注入
12:如果需要完成属性注入,则开始注入属性
4、初始化
13、判断bean的类型回调Aware接口
14、调用生命周期回调方法
15、如果需要代理则完成代理
5、创建完成
16、put到单例池——bean完成——存在spring容器当中
二、流程图
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

12.你知道Spring的哪些扩展点,在什么时候调用?

Spring中非常非常多的扩展接口,当然你也不需要全部回答,可以挑重点回答:
执行BeanFactoryPostProcessor的postProcessBeanFactory方法

1 /***
2 * 作用: 在注册BeanDefinition的可以对beanFactory进行扩展 后
3 * 调用时机: Ioc加载时注册BeanDefinition 的时候会调用
4 */
5 public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
   
6  @Override
7 public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
   
8
9 }
10 }

执行BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法:

/***
3 * 作用:动态注册BeanDefinition
4 * 调用时机: Ioc加载时注册BeanDefinition 的时候会调用
5 */
6 @Component
7 public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
   
8  @Override
9 public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
   
10
11
12  RootBeanDefinition beanDefinition = new RootBeanDefinition(Car.class);
13  registry.registerBeanDefinition("car",beanDefinition);
14 }

加载BeanPostProcessor实现类 : 在Bean的生命周期会调用9次Bean的后置处理器
创建所有单例bean

初始化阶段:

初始化阶段调用XXXAware接口的SetXXXAware方法 :生命周期回调: 初始化、销毁
执行BeanPostProcessor实现类的postProcessBeforeInitialization方法
执行InitializingBean实现类的afterPropertiesSet方法
执行bean的init-method属性指定的初始化方法
执行BeanPostProcessor实现类的postProcessAfterInitialization方法
初始化完成
关闭容器,执行DiposibleBean实现类的destory
执行bean的destroy-method属性指定的初始化方法

三、Spring Beans

13.什么是Spring beans?

Spring 官方文档对 bean 的解释是:
In Spring, the objects that form the backbone of your application and that are managed by the Spring IoC container
are called beans. A bean is an object that is instantiated, assembled, and otherwise managed by a Spring IoC
container.
翻译过来就是:

在 Spring 中,构成应用程序主干并由Spring IoC容器管理的对象称为bean。bean是一个由Spring IoC容器实例化、组装和管理的对象。
概念简单明了,我们提取处关键的信息:

bean是对象,一个或者多个不限定
bean由Spring中一个叫IoC的东西管理

额外扩展

从官方定义中,我们可以提取出以下信息:

1、Bean是对象,一个或者多个不限定。
2、Bean托管在Spring中一个叫IoC的容器中。
3、我们的程序是由一个个Bean构成的。

Spring是通过声明式配置的方式来定义Bean的,所有创建Bean需要的前置依赖或者参数都是通过配置文件先声明,Spring启动以后会解析这些声明好的配置内容。那么,我们该如何去定义Spring中的Bean呢?

定义Spring Bean有哪些方式?

一般来说,Spring Bean的定义配置有三种方式:

第一种:基于XML的方式配置
第二种:基于注解扫描的方式配置
第三种:基于Java类的配置

14.配置Bean有哪几种方式?

基于xml:

基于注解:
@Component(@Controller 、@Service、@Repostory)
前提:需要配置扫描包 反射调用构造方法
基于java类配置:
@Bean 可以自己控制实例化过程
@Import 3种方式

额外扩展

Spring Bean 的作用域
Spring 容器在初始化一个 Bean 实例时,同时会指定该实例的作用域。Spring 5 支持 6 种作用域。

singleton
默认的作用域,单例模式。 表示在 Spring 容器中只有一个 Bean 实例,Bean 以单例的方式存在。在容器启动前就创建好了对象,任何时间获取都是之前创建好的那个对象。配置方式可以缺省,因为是默认值。

prototype
原型作用域,多实例模式。
每次调用 Bean 时都会创建一个新实例。Bean 以多实例的方式存在。容器启动默认不会创建多实例 bean,每次获取都会创建一个新的实例 bean 。配置方式为

request
在 web 环境下,每次 HTTP 请求都会创建一个 Bean 实例,该作用域只在当前 HTTP Request 内有效。 配置方式为

session
在 web 环境下,每次 HTTP 会话共享一个 Bean 实例,不同的 Session 使用不同的 Bean 实例。该作用域只在当前 HTTP Session 内有效。配置方式为

application
在web 环境下,同一个 web application 共享一个 Bean 实例,该作用域在当前 ServletContext 内有效。

websocket
在web 环境下,同一个 websocket 共享一个 Bean 实例,该作用域在整个 websocket 中有效。

15.解释Spring支持的几种bean的作用域

Spring框架支持以下五种bean的作用域:

singleton : bean在每个Spring ioc 容器中只有一个实例。
prototype:一个bean的定义可以有多个实例。
request:每次http请求都会创建一个bean,该作用域仅在基于web的Spring ApplicationContext情形下有效。
session:在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。
application:全局 Web 应用程序范围的范围标识符

注意: 缺省的Spring bean 的作用域是Singleton。使用 prototype 作用域需要慎重的思考,因为频繁创建和销毁 bean 会带来很大的性能开销。

16、单例bean的优势

一、单例bean的优势
由于不会每次都新创建新对象所以有以下几个性能上的优势:

减少了新生成实例的消耗。 新生成实例消耗包括两方面,第一,spring会通过反射或者cglib来生成bean实例,这都是耗性能的操作,其次给对象分配内存也会涉及复杂算法。 提供服务器内存的利用率 ,减少服务器内存消耗,减少jvm垃圾回收。 由于不会给每个请求都新生成bean实例,所以自然回收的对象少了。
可以快速获取到bean。因为单例的获取bean操作除了第一次生成之外其余的都是从缓存里获取的所以很快。
二、 额外扩展
单例Bean与原型Bean

阅读源码可认识到,单例Bean在第一次请求后将会被创建并存放到缓存map中,之后的请求到缓存map中寻找。生成bean时先判断单例的还是原型->如果是单例的则先尝试从缓存里获取,没有再新创建。
原型Bean则会重复创建

17.Spring实例化bean方式的几种方式

Spring实例化bean方式的几种方式
构造器方式(反射);
静态工厂方式; factory-method
实例工厂方式(@Bean); factory-bean+factory-method
FactoryBean方式

Spring 三种实例化 bean 的方式比较
方式一:通过bean的缺省构造函数创建,当各个bean的业务逻辑相互比较独立的时候或者和外界关联较少的时候可以使用。

方式二:利用静态factory方法创建,可以统一管理各个bean的创建,如各个bean在创建之前需要相同的初始化处理,则可用这个factory方法先进行统一的处理等等。

方式三:利用实例化factory方法创建,即将factory方法也作为了业务bean来控制,

1、可用于集成其他框架的bean创建管理方法

2、能够使bean和factory的角色互换

开发中项目一般使用一种方式实例化bean,项目开发基本采用第一种方式,交给spring托管,使用时直接拿来使用即可,另外两种了解即可。

18.Spring框架中的单例bean是线程安全的吗?(阿里一面)

不是,Spring框架中的单例bean不是线程安全的。
spring 中的 bean 默认是单例模式,spring 框架并没有对单例 bean 进行多线程的封装处理。
实际上大部分时候 spring bean 无状态的(比如 dao 类),所以某种程度上来说 bean 也是安全的,但如果 bean 有状态的话(比如 view model 对象),那就要开发者自己去保证线程安全了。
最简单的就是改变 bean 的作用域,把“singleton”变更为“prototype”,这样请求 bean 相当于 new Bean()了,所以就可以保证线程安全了。
有状态就是有数据存储功能(比如成员变量读写)。
无状态就是不会保存数据。

额外扩展

Spring 的 bean 作用域(scope)类型

1、singleton:单例,默认作用域。

2、prototype:原型,每次创建一个新对象。

3、request:请求,每次Http请求创建一个新对象,适用于WebApplicationContext环境下。

4、session:会话,同一个会话共享一个实例,不同会话使用不用的实例。

5、global-session:全局会话,所有会话共享一个实例。

线程安全这个问题,要从单例与原型Bean分别进行说明。

原型Bean

对于原型Bean,每次创建一个新对象,也就是线程之间并不存在Bean共享,自然是不会有线程安全的问题。

单例Bean

对于单例Bean,所有线程都共享一个单例实例Bean,因此是存在资源的竞争。

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

有状态对象(Stateful Bean) :就是有实例变量的对象,可以保存数据,是非线程安全的。每个用户有自己特有的一个实例,在用户的生存期内,bean保持了用户的信息,即“有状态”;一旦用户灭亡(调用结束或实例结束),bean的生命期也告结束。即每个用户最初都会得到一个初始的bean。

无状态对象(Stateless Bean):就是没有实例变量的对象,不能保存数据,是不变类,是线程安全的。bean一旦实例化就被加进会话池中,各个用户都可以共用。即使用户已经消亡,bean 的生命期也不一定结束,它可能依然存在于会话池中,供其他用户调用。由于没有特定的用户,那么也就不能保持某一用户的状态,所以叫无状态bean。但无状态会话bean 并非没有状态,如果它有自己的属性(变量),那么这些变量就会受到所有调用它的用户的影响,这是在实际应用中必须注意的。

对于有状态的bean,Spring官方提供的bean,一般提供了通过ThreadLocal去解决线程安全的方法,比如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等。

使用ThreadLocal的好处

使得多线程场景下,多个线程对这个单例Bean的成员变量并不存在资源的竞争,因为ThreadLocal为每个线程保存线程私有的数据。这是一种以空间换时间的方式。

当然也可以通过加锁的方法来解决线程安全,这种以时间换空间的场景在高并发场景下显然是不实际的。

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

人话:
1、让spring生成时用多例
2、将成员变量放在ThreadLocal当中
3、使用同步锁加synchronized,会影响服务器吞吐量

在一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域,因为Spring对一些Bean中非线程安全状态采用ThreadLocal进行处理,解决线程安全问题。

ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。同步机制采用了“时间换空间”的方式,仅提供一份变量,不同的线程在访问前需要获取锁,没获得锁的线程则需要排队。而ThreadLocal采用了“空间换时间”的方式。

ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。

1
2 /***
6 * 单例Bean的情况
7 * 如果在类中声明成员变量 并且有读写操作(有状态),就是线程不安全
8 * 解决:
9 * 1.设置为多例
10 * 2.将成员变量放在ThreadLocal
11 * 3.同步锁 会影响服务器吞吐量
12 * 但是!
13 * 只需要把成员变量声明在方法中(无状态), 单例Bean是线程安全的
14 */
15 public class Run {
   
16 public static void main(String[] args) {
   
17
18
19  AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
20
21 // 线程一
22  UserService bean = applicationContext.getBean(UserService.class);
23 new Thread(()> {
   
24  System.out.println(bean.welcome("张三"));
25 }).start();
26
27 // 线程二
28  UserService bean2 = applicationContext.getBean(UserService.class);
29 new Thread(()> {
   
30  System.out.println(bean2.welcome("李四"));
31 }).start();
32
33 }

20.什么是bean装配?

什么是bean装配?
装配,或bean 装配是指在Spring 容器中把bean组装到一起,前提是容器需要知道bean的依赖关系,如何通过依赖注入来把它们装配到一起。

额外扩展
Bean中主要包含三种装配方式,分别为:
基于XML的装配
基于Annotation的装配
自动装配

这三种装配方式的用法如下:

(1)基于XML的装配:
Spring提供了2种基于XML的装配方式:设值注入(Setter Injection)和构造注入(Constructor Injection)。设置注入中的Bean类必须提供一个默认的无参构造方法,同时必须为需要注入的属性提供对应的setter方法。使用设值注入时,在Spring配置文件中,需要使用元素的子元素来为每个属性注入值。使用构造注入时,在配置文件里,需要使用元素的子元素来定义构造方法的参数,可以使用其value属性(或子元素)来设置该参数的值。

(2)基于Annotation的装配:

使用基于Annotation的装配时,首先需要使用 @Repository、@Service与@Constroller分别对实现类进行标注,然后用@Autowired或@Resource注解对注入的Bean的属性进行标注 ,最后在Spring的配置文件中,通过<context:annotation-config />来开启注解处理器,或使用<context:component-scan base-package=“Bean所在的包路径”/>的配置方式即可实现Annotation的装配。

(3)自动装配:
在元素中使用autowire属性,并将其属性值设置为byName或者byType即可实现自动装配。

21.什么是bean的自动装配?

什么是bean的自动装配?
在Spring框架中,在配置文件中设定bean的依赖关系是一个很好的机制,Spring 容器能够自动装配相互合作的bean,这意味着容器不需要配置,能通过Bean工厂自动处理bean之间的协作。这意味着 Spring可以通过向Bean Factory中注入的方式自动搞定bean之间的依赖关系。自动装配可以设置在每个bean上,也可以设定在特定的bean上。

通俗易懂一点就是:

自动装配是 Spring 满足 bean 依赖的一种方式。

在使用 Spring 配置 bean 时,我们都要给配置的 bean 的属性设置一个值,如果不手动设置则都是空。而自动的好处就在于,我们不用手动去设置一个值,spring 会在上下文中自动寻找并装配合适的值。

额外补充
1、在 Spring 中有三种装配的方式:

在 XML 中显示配置
在 Java 代码中显示的配置
隐式的自动装配

2、自动装配有三种方式:

byName
byName 通过匹配 bean 的 id是否跟 setter 对应,对应则自动装配。
意思就是说,如果我的 Person 中有一个 setCat() 而配置文件中有一个 「bean 的 id 为 cat」,则能够自动装配。
把刚刚手动装配方式的 bean 做一些修改,来演示自动装配:

<bean id="cat" class="com.javastudyway.pojo.Cat"/>
<bean id="dog" class="com.javastudyway.pojo.Dog"/>
<!--加上了 person 的 bean 的 autowired 属性
    并去掉了手动装配的 cat 和 dog -->
<bean id="person" class="com.javastudyway.pojo.Person" autowire="byName">
    <property name="name" value="Java学习之道"/>
</bean>

byType
constructor

3、byName 和 byType 优异比较

使用 byName 需要保证 bean 的 id 唯一,且这个 bean 需要自动注入的属性和set方法与 bean 的 id 要一致。

使用 byType 需要保证 bean 的 class 唯一,且这个 bean 需要自动注入的属性和类型要一致。

22. 自动装配有哪些限制(需要注意)?

自动装配有哪些限制(需要注意)?
一定要声明set方法
覆盖: 你仍可以用 < constructor-arg >和 < property > 配置来定义依赖,这些配置将始终覆盖自动注入。
基本数据类型:不能自动装配简单的属性,如基本数据类型、字符串和类。 (手动注入还是可以注入基本数据类型的 <property value=“” @Value)
模糊特性:自动装配不如显式装配精确,如果有可能尽量使用显示装配。

所以更推荐使用手动装配(@Autowired(根据类型、再根据名字) ref=“” 这种方式 更加灵活更加清晰 )

额外补充
1、自动装配模式

no
这是默认的自动装配模式。这意味着默认情况下没有自动装配。
byName
byName模式根据bean的名称注入对象依赖项。在这种情况下,属性名称和bean名称必须相同。它在内部调用setter方法。
byType
byType模式根据类型注入对象依赖项。因此属性名称和bean名称可以不同。它在内部调用setter方法。
constructor
构造函数模式通过调用类的构造函数来注入依赖项。它会调用具有大量参数的构造函数。
autodetect
从Spring 3开始不推荐使用。

自动装配的优点
它需要 更少的代码,因为我们不需要编写代码来显式注入依赖项。

23.解释不同方式的自动装配,spring 自动装配 bean 有哪些方式?

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

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

no:默认的方式是不进行自动装配的,通过手工设置ref属性来进行装配bean。@Autowired 来进行手动指定
需要自动注入的属性
byName:通过bean的名称进行自动装配,如果一个bean的 property 与另一bean 的name 相同,就进行自
动装配。
byType:通过参数的数据类型进行自动装配。
constructor:利用构造函数进行装配,并且构造函数的参数通过byType进行装配。
autodetect:自动探测,如果有构造方法,通过 construct的方式自动装配,否则使用 byType的方式自动装
配。 (在spring3.0+弃用)

20.Spring 在加载过程中Bean有哪几种形态:

有两个重要的bean 生命周期方法,

第一个是init , 它是在容器加载bean的时候被调用。
第二个方法是 destroy 它是在容器卸载类的时候被调用。
bean 标签有两个重要的属性(init-method和destroy-method)。用它们你可以自己定制初始化和注销方法。它们也有相应的注解(@PostConstruct和@PreDestroy)。

额外扩展
Spring生命周期全过程大致分为五个阶段:创建前准备阶段、创建实例阶段、依赖注入阶段、容器缓存阶段和销毁实例阶段。

1、创建前准备阶段
这个阶段主要是在开始Bean加载之前,从Spring上下文和相关配置中解析并查找Bean有关的配置内容,

比如init-method-容器在初始化bean时调用的方法、destory-method,容器在销毁Bean时调用的方法。

以及,BeanFactoryPostProcessor这类的bean加载过程中的前置和后置处理。

这些类或者配置其实是Spring提供给开发者,用来实现Bean加载过程中的扩展机制,在很多和Spring集成的中间件经常使用,比如Dubbo。

2、创建实例阶段
这个阶段主要是通过反射来创建Bean的实例对象,并且扫描和解析Bean声明的一些属性。

3、依赖注入阶段
在这个阶段,会检测被实例化的Bean是否存在其他依赖,如果存在其他依赖,1就需要对这些被依赖Bean进行注入。比如通过读取

@Autowired、@Setter等依赖注入的配置。

在这个阶段还会触发一些扩展的调用,比如常见的扩展类:BeanPostProcessors(用来实现Bean初始化前后的回调)、

InitializingBean类(这个类有一个afterPropertiesSet()方法,给属性赋值)、还有BeanFactoryAware等等。

4、容器缓存阶段
容器缓存阶段主要是把Bean保存到IoC容器中缓存起来,到了这个阶段,Bean就可以被开发者使用了。

这个阶段涉及到的操作,常见的有,init-method这个属性配置的方法,会在这个阶段调用。

在比如BeanPostProcessors方法中的后置处理器方法如:postProcessAfterInitialization,也是在这个阶段触发的。

5、销毁实例阶段
这个阶段,是完成Spring应用上下文关闭时,将销毁Spring上下文中所有的Bean。

如果Bean实现了DisposableBean接口,或者配置了destory-method属 性,将会在这个阶段被调用。

23. 解释Spring框架中bean的生命周期

Bean生命周期:指定的就是Bean从创建到销毁的整个过程: 分4大类:

实例化
a. 通过反射去推断构造函数进行实例化
b. 实例工厂、 静态工厂

属性赋值
a. 解析自动装配(byname bytype constractor none @Autowired) DI的体现
b. 循环依赖

初始化
a. 调用XXXAware回调方法
b. 调用初始化生命周期回调(三种)
c. 如果bean实现aop 创建动态代理

销毁
a. 在spring容器关闭的时候进行调用
b. 调用销毁生命周期回调
bean在Spring容器中从创建到销毁经历了若干阶段,每一阶段都可以针对Spring如何管理bean进行个性化定制。
正如你所见,在bean准备就绪之前,bean工厂执行了若干启动步骤。

详细描述如下:

Spring对bean进行实例化;
Spring将值和bean的引用注入到bean对应的属性中;
如果bean实现了BeanNameAware接口,Spring将bean的ID传递给setBean-Name()方法;
如果bean实现了BeanFactoryAware接口,Spring将调用setBeanFactory()方法,将BeanFactory容器实例传入;
如果bean实现了ApplicationContextAware接口,Spring将调用setApplicationContext()方法,将bean所在的应用上下文的引用传入进来;
如果bean实现了BeanPostProcessor接口,Spring将调用它们的post-ProcessBeforeInitialization()方法;
如果bean实现了InitializingBean接口,Spring将调用它们的after-PropertiesSet()方法。类似地,如果bean使用
initmethod声明了初始化方法,该方法也会被调用;
如果bean实现了BeanPostProcessor接口,Spring将调用它们的post-ProcessAfterInitialization()方法;
此时,bean已经准备就绪,可以被应用程序使用了,它们将一直驻留在应用上下文中,直到该应用上下文被销毁;
如果bean实现了DisposableBean接口,Spring将调用它的destroy()接口方法。同样,如果bean使用destroy-method声明了销毁方法,该方法也会被调用。

现在你已经了解了如何创建和加载一个Spring容器。但是一个空的容器并没有太大的价值,在你把东西放进去之前,它里面什么都没有。为了从Spring的DI(依赖注入)中受益,我们必须将应用对象装配进Spring容器中。

26、Spring是如何解决Bean的循环依赖?

采用三级缓存解决的 就是三个Map ; 关键: 一定要有一个缓存保存它的早期对象作为死循环的出口

一级缓存:存储完整的Bean
二级缓存: 避免多重循环依赖的情况 重复创建动态代理。
三级缓存:
a. 缓存是函数接口:通过lambda 把方法传进去( 把Bean的实例和Bean名字传进去(aop创建) )
b. 不会立即调:(如果在实例化后立即调用的话:所有的aop 不管bean是否循环依赖都会在 实例化后创建
proxy, 正常Bean 其实spring还是希望遵循生命周期在初始化创建动态代理, 只能循环依赖才创建)
c. 会在 ABA (第二次getBean(A) 才会去调用三级缓存(如果实现了aop才会创建动态代理,如果没有实现依然返回的Bean的实例))
d. 放入二级缓存(避免重复创建)
夺命连环问:

二级缓存能不能解决循环依赖?

a. 如果只是死循环的问题: 一级缓存就可以解决 :无法避免在并发下获取不完整的Bean?
b. 二级缓存也可以解决循环依赖: 只不过如果出现重复循环依赖 会多次创建aop的动态代理

Spring有没有解决多例Bean的循环依赖?

a. 多例不会使用缓存进行存储(多例Bean每次使用都需要重新创建)
b. 不缓存早期对象就无法解决循环

Spring有没有解决构造函数参数Bean的循环依赖?

a. 构造函数的循环依赖也是会报错
b. 可以通过人工进行解决:@Lazy
i. 就不会立即创建依赖的bean了
ii. 而是等到用到才通过动态代理进行创建

27.Spring如何避免在并发下获取不完整的Bean?

1、为什么获取不到完整的Bean?
我们知道, 如果spring容器已经加载完了, 那么肯定所有bean都是完整的了, 但如果, spring没有加载完, 在加载的过程中, 构建bean就有可能出现不完整bean的情况

2、如何解决读取到不完整bean的问题.
其实, 之所以出现这样的问题, 原因就在于, 第一个bean还没有被创建完, 第二个bean就开始了. 这是典型的并发问题. 针对这个问题, 其实,我们加锁就可以了, 解决方案如下:

双重检查锁
为什么一级缓存不加到锁里面:性能:避免已经创建好的Bean阻塞等待

代码演示
第一: 将整个创建过程加一把锁

/**
     * 获取bean, 根据beanName获取
     */
    public static Object getBean(String beanName) throws Exception {
   

        // 增加一个出口. 判断实体类是否已经被加载过了
        Object singleton = getSingleton(beanName);
        if (singleton != null) {
   
            return singleton;
        }
        Object instanceBean;
        synchronized (singletonObjects) {
   
  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值