什么是Spring?谈谈你对IOC和AOP的理解。

Spring是一种Java开发框架,旨在简化企业级应用程序的开发和部署。它具有以下优点:

  1. 对象托管:Spring能够管理和赋值所有对象,使开发人员不再需要手动管理对象的创建和依赖关系。
  2. 动态代理:Spring的动态代理功能可以实现大部分可复用的逻辑功能,从而避免了重复的代码。
  3. 强大的框架生态系统:市面上几乎所有的框架都是基于Spring构建的,Spring提供了强有力的支持和集成能力。
  4. 低侵入性:Spring的代码对于我们的应用代码几乎是无侵入的,只需要使用几个注解就能让Spring启动。

控制反转(IoC)是Spring的一个重要特性,它使得对象的创建和依赖关系的管理由Spring容器来完成。IoC有三种实现方式:注解形式、构造器形式和set方法注入。通过IoC,我们不再需要使用new关键字手动创建对象,而是将对象的创建和管理交给Spring容器处理。

面向切面编程(AOP)是Spring的另一个重要特性,它通过动态代理实现。AOP常用于日志收集、事务管理等方面。通过AOP,我们可以在被代理对象的方法执行前后,加入一些统一的业务逻辑处理,例如日志记录或权限校验。

Spring容器的启动流程是怎么样的?

启动流程几乎跟源码息息相关,如果没有看过源码可能对启动流程只能靠自己的理解去背,如果对源码右深入理解,那么这道题可以这么说:

1:初始化reader和scanner

2:使用scanner组件扫描basePackage下的所有对象,将配置类的BeanDefinition注册到容器中。

3:refresh(); 刷新容器。

可以分为以下几个步骤:

  • 定位配置文件:Spring容器首先需要定位配置文件的位置和名称。可以通过ClassPathXmlApplicationContext、FileSystemXmlApplicationContext等类来指定配置文件的路径。
  • 加载配置文件:Spring容器会读取配置文件的内容,并将其转换为内部的数据结构,一般使用DOM或SAX解析XML文件。
  • 创建容器对象:根据配置文件中的定义,Spring容器会创建一个或多个容器对象。常用的容器对象是ApplicationContext和BeanFactory。ApplicationContext是BeanFactory的子接口,提供了更多的功能。
  • 注册Bean定义:Spring容器会解析配置文件中的Bean定义,包括Bean的名称、类型、依赖关系等,并将其注册到容器中。这些Bean定义会被封装为BeanDefinition对象。
  • 实例化Bean:根据注册的Bean定义,Spring容器会实例化相应的Bean对象,并将其放入容器中管理。实例化可以通过构造方法、工厂方法或者通过AOP代理来完成。
  • 注入依赖:Spring容器会根据Bean定义中的依赖关系,将相应的依赖注入到Bean中。可以通过构造方法、setter方法或者注解方式来完成注入。
  • 调用初始化方法:如果Bean定义中指定了初始化方法,Spring容器会在实例化和依赖注入完成后,调用Bean的初始化方法。可以通过配置文件或者注解指定初始化方法。
  • 容器就绪:当所有的Bean都被实例化、注入依赖并初始化完成后,Spring容器就处于就绪状态,可以提供相应的服务。

然后在细说自己知道的部分源码,比如我还了解到一些关于源码的细节。例如,在获取Bean定义后,Spring会在实例化之前通过合并Bean定义来进行初始化,并且AOP的逻辑是在初始化之后通过后置处理器进行动态代理。

此外,如果我们需要监听Spring的启动过程以及在启动后实现自己的业务逻辑,除了可以使用初始化对象的方法afterPropertiesSet外,还可以通过注册一个监听器来监听Spring发布的各种事件。这样,我们就可以在特定的事件触发时执行我们自己的逻辑。

Spring框架中Bean的创建过程是怎样的?

在Spring框架中,Bean的创建过程涉及到多个环节和细节。下面我将更详细地介绍每个步骤的具体内容。可以大致分为五个步骤:获取Bean定义、实例化、赋值、初始化和销毁。

  1. Bean定义是在包扫描的过程中进行注册的。通过扫描配置文件或使用注解等方式,将Bean的定义信息注册到Spring容器中。
  2. 根据Bean定义进行实例化。这一步骤会根据Bean的构造器创建对象实例,如果没有指定构造器,则使用默认的构造器。在实例化过程中,还可以应用前后实例化处理器对对象进行一些额外的处理。
  3. 在赋值阶段,会对Bean中的依赖进行赋值。这可以通过使用@Autowired注解注入依赖属性来实现。赋值完成后,Bean的属性就可以访问到依赖的对象。
  4. 通过调用init-method(@PostConstruct)方法或实现InitializingBean接口(afterPropertiesSet)来进行初始化操作。在初始化过程中,可以执行一些特定的逻辑,例如数据加载等。同样地,也可以应用前后初始化处理器对Bean进行一些额外的处理。
  5. 销毁阶段是在应用程序关闭或需要销毁Bean时执行的。通过调用destroy-method方法(@PreDestroy),可以执行对象的销毁逻辑,DisposableBean: 当Bean实现了这个接口,在对象销毁前就会调用destory()方法。

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

Spring框架中的Bean默认是单例模式,因此不是线程安全的。如果要处理线程安全问题,可以采取以下几种方式:

  • 使用prototype作用域:通过将Bean的作用域设置为prototype,确保每次请求都创建一个新的对象,从而保证线程安全。
  • 避免使用全局资源属性:尽量避免在Bean中使用全局资源属性,而是使用无状态的属性,比如使用注解形式的Bean注入。
  • 使用ThreadLocal类:可以使用ThreadLocal类将属性与线程进行绑定,确保每个线程独有一份属性副本,从而避免线程安全问题。

Spring如何处理循环依赖问题?

大家都知道spring采用的是三级缓存,那么如何理解三级缓存处理了循环依赖问题呢?

一级缓存:缓存最终的单例池对象: private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

二级缓存:缓存初始化的对象:private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

三级缓存:缓存对象的ObjectFactory: private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

其实当一个对象实例化后就会存储在 singletonFactories三级缓存,当被引用时,会执行一个后置处理器方法,这里也是给aop创建代理对象的时机:

  protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
}
}
return exposedObject;
}

如果是正常的普通对象,会直接进行如二级缓存,并返回一个实例化后的对象,所以之所以使用到了三级缓存,而不是光是用二级缓存就是考虑到了循环依赖可能是一个代理对象,我们无法直接提供实例化的对象而是一个代理对象。

    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
// Consistent creation of early reference within full singleton lock
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}

Spring如何处理事务?

Spring框架提供了两种方式来处理事务:编程式事务和声明式事务。

编程式事务是通过使用TransactionTemplate来进行事务管理的方式。它需要手动在代码中显式地指定事务的开启、提交和回滚操作。这种方式对代码的侵入性较高,因为需要在每个需要进行事务管理的方法中编写事务处理的代码。一般情况下,不推荐使用编程式事务,除非在特定的场景下需要对事务进行更精细的控制。

声明式事务是通过使用注解或XML配置的方式来声明事务的行为。在Spring中,最常用的是使用注解来声明事务。通过在方法或类上添加@Transactional注解,可以指定事务的传播行为、隔离级别、超时时间等属性。事务的传播行为指的是当一个方法调用另一个带有事务注解的方法时,事务应该如何进行传播和管理。

Spring框架提供了以下几种事务的传播级别:

  • REQUIRED(默认值):如果当前存在事务,则加入该事务,如果不存在事务,则创建一个新的事务。
  • REQUIRES_NEW:无论当前是否存在事务,都创建一个新的事务。如果当前存在事务,则将当前事务挂起。
  • SUPPORTS:如果当前存在事务,则加入该事务,如果不存在事务,则以非事务方式执行。
  • NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,则将当前事务挂起。
  • MANDATORY:当前必须存在事务,否则抛出异常。
  • NEVER:当前不能存在事务,否则抛出异常。
  • NESTED:如果当前存在事务,则在一个新的嵌套事务中执行。如果不存在事务,则执行与REQUIRED一样的操作。

Spring框架提供了以下几种事务的隔离级别:

  • READ_UNCOMMITTED(读未提交):此级别最低,允许读取其他事务尚未提交的数据,可能会读取到脏数据。
  • READ_COMMITTED(读已提交):在一个事务内,只能读取到已经提交的数据,避免了脏数据,但是可能会产生幻读。
  • REPEATABLE_READ(可重复读):在一个事务内,多次读取同一数据结果保持一致,解决了幻读的问题。
  • SERIALIZABLE(串行化):最高的隔离级别,所有事务都是串行执行,避免了并发问题,但并发度最低。

SpringMVC中的控制器是不是单例模式?如果是,如何保证线程安全?

在Spring MVC中,默认情况下,控制器是以单例模式创建的。这意味着在应用程序的整个生命周期中,只会创建一个控制器实例来处理所有的请求。为了保证控制器的线程安全性,可以采取以下措施:

1:保持控制器的无状态属性:控制器应该尽量避免使用实例变量来保存状态信息,尽量使用方法参数或局部变量来处理请求。这样可以确保每个请求都有独立的数据副本,避免多个线程之间的竞争和冲突。

2:设置控制器的作用域为非单例模式:可以将控制器的作用域设置为非单例模式,如prototype或request。这样每次请求都会创建一个新的控制器实例,确保每个请求都有独立的控制器对象,避免线程安全问题。

总结

本次面试涉及了Spring框架的多个方面,包括IOC和AOP的理解、Spring容器的启动流程、Bean的创建过程、Bean的线程安全性、循环依赖的处理、事务的处理以及Spring MVC中控制器的线程安全性。通过这些问题的回答,展示了对Spring框架的深入理解和应用经验。同时,也凸显了对面试题目的认真思考和清晰表达的能力。

Spring面试攻略:如何展现你对Spring的深入理解的更多相关文章

  1. IOS面试攻略

    IOS面试攻略(1.0) 2013-10-13 20:58:09|  分类: IOS面试 |  标签:ios知识点总汇  ios面试  |举报|字号 订阅     来自:伊甸网 @ 看到这个关键字,我 ...

  2. oc面试攻略

    原文出自:http://mobile.51cto.com/iphone-402619.htm 1.Object-C有多继承吗?没有的话用什么代替?cocoa 中所有的类都是NSObject 的子类 多 ...

  3. Spring Cloud Consul 之Greenwich版本全攻略

    什么是Consul Consul是HashiCorp公司推出的开源软件,使用GO语言编写,提供了分布式系统的服务注册和发现.配置等功能,这些功能中的每一个都可以根据需要单独使用,也可以一起使用以构建全 ...

  4. Spring Boot Admin 2.1.0 全攻略

    转载请标明出处: https://www.fangzhipeng.com 本文出自方志朋的博客 Spring Boot Admin简介 Spring Boot Admin是一个开源社区项目,用于管理和 ...

  5. 【JAVA EE企业级开发四步走完全攻略】

    本文是J2EE企业级开发四步走完全攻略索引,因内容比较广泛,涉及整个JAVA EE开发相关知识,这是一个长期的计划,单个发blog比较零散,所以整理此索引,决定以后每发一季JAVA EE blog后会 ...

  6. 17个经典的Spring面试问答

    Q1.什么是Spring Framework? Spring是Java企业版应用程序开发中使用最广泛的框架.Spring的核心功能可用于开发任何Java应用程序. 我们可以使用它的扩展来在Java E ...

  7. 图文详解:阿里宠儿【小兔】RabbitMQ的养成攻略

  8. 从小工到专家 ——读《Java程序员职场全攻略》有感

    从小工到专家 ——读<Java程序员职场全攻略>有感   <Java程序员职场全攻略>是以故事的形式,向读者介绍Java程序员的职场经验.作者牛开复在北京从事软件开发,已经是一 ...

  9. JAVA EE企业级开发四步走完全攻略 [转]

    http://bbs.51cto.com/thread-550558-1.html 本文是J2EE企业级开发四步走完全攻略索引,因内容比较广泛,涉及整个JAVA EE开发相关知识,这是一个长期的计划, ...

  10. 【转】Perl Unicode全攻略

    Perl Unicode全攻略 耐心看完本文,相信你今后在unicode处理上不会再有什么问题. 本文内容适用于perl 5.8及其以上版本. perl internal form 在Perl看来, ...

随机推荐

  1. golang调用sdl2,播放pcm音频,报错signal arrived during external code execution。

    golang调用sdl2,播放pcm音频,报错signal arrived during external code execution. win10 x64下测试成功,其他操作系统下不保证成功. 采 ...

  2. 2021-05-08:给定两个非负数组x和hp,长度都是N,再给定一个正数range。x有序,x[i]表示i号怪兽在x轴上的位置;hp[i]表示i号怪兽的血量 。range表示法师如果站在x位置,用A

    2021-05-08:给定两个非负数组x和hp,长度都是N,再给定一个正数range.x有序,x[i]表示i号怪兽在x轴上的位置:hp[i]表示i号怪兽的血量 .range表示法师如果站在x位置,用A ...

  3. java.sql.SQLException: The connection property 'useSSL' only accepts values of the form: 'true', 'false', 'yes' or 'no'. The value 'true;' is not in this set.

    错误说明: 数据库连接属性"useSSL"的值只能为'true','false','yes','no',多余的都不行 因此我们要到数据库的连接属性(properties)里找答案 ...

  4. 17.AQS中的Condition是什么?

    欢迎关注:王有志 期待你加入Java人的提桶跑路群:共同富裕的Java人 今天来和大家聊聊Condition,Condition为AQS"家族"提供了等待与唤醒的能力,使AQS&q ...

  5. MC我的世界模拟城市模拟大都sim-u常见问题总结

    title: 常见问题 date: 2022-08-22 09:58:01 permalink: /course/2 sidebar: auto article: true copyright: tr ...

  6. 如何使用Map处理Dom节点

    本文浅析一下为什么Map(和WeakMap)在处理大量DOM节点时特别有用. 我们在JavaScript中使用了很多普通的.古老的对象来存储键/值数据,它们处理的非常出色: const person ...

  7. 代码随想录算法训练营Day51 动态规划

    代码随想录算法训练营 代码随想录算法训练营Day51 动态规划| 309.最佳买卖股票时机含冷冻期 714.买卖股票的最佳时机含手续费 总结 309.最佳买卖股票时机含冷冻期 题目链接:309.最佳买 ...

  8. SCI 投稿中像素、DPI、图片分辨率的一些知识

    最近在学习 Linux 命令行下的 ImageMagick 图像处理,对图像本身的一些概念有点懵,搜集整理了一点资料,仅供自己和大家学习与参考. SCI 期刊对分辨率大多都有一定的要求,例如一段来自 ...

  9. 拒绝conda, 用virtualenv构建多版本的python开发环境

    本文章转载自公众号 "生信码农笔记(ID:bio-coder)",已获得原作者授权. 1. 不喜欢用 conda 特别不喜欢 bioconda, miniconda, Anacon ...

  10. Docker运行Django框架

    Django框架 创建django-pg项目目录 [root@docker ~]# mkdir docker-compose-django [root@docker ~]# cd docker-com ...