面试:Spring全家桶

本文深入探讨了Spring框架的核心组件,包括Bean的生命周期、作用域、单例bean的线程安全性,以及IoC和DI的概念。此外,详细阐述了AOP的实现方式、通知类型及其在Spring AOP中的应用,还有事务管理的实现原理、传播行为和隔离级别。文章还讨论了Spring如何解决循环依赖问题,解释了Spring MVC的工作流程,Spring Boot的自动装配原理,@Autowired和@Resource的区别,以及BeanFactory与ApplicationContext的差异。最后,文章提到了Spring中运用的设计模式,如工厂模式、代理模式、单例模式等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

主要内容出自:Java知识体系最强总结(2020版)

一、Spring Beans

1、容器中bean的生命周期

在这里插入图片描述

2、bean的作用域

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

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

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

3、Spring框架中的单例bean是线程安全的吗?

不是

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

二、IOC与DI

IOC 控制反转

“控制反转”概念就是对组件对象控制权的转移,从程序代码本身转移到了外部容器

IOC作用:

  • 由容器管理对象的创建和整个生命周期,并进行依赖关系的维护。对象的创建并不是一件简单的事,在对象关系比较复杂时,如果依赖关系需要程序猿来维护的话,那是相当头疼的
  • 解耦,由容器去维护具体的对象

IOC的实现方式:依赖注入(DI)和依赖查找

依赖注入(DI)

让容器全权负责依赖查询,受管组件只需要暴露JavaBean的setter方法或者带参数的构造器或者接口,使容器可以在初始化时组装对象的依赖关系。

依赖注入与依赖查找方式相比,主要优势为:

  • 查找定位操作与应用代码完全无关。
  • 不依赖于容器的API,可以很容易地在任何容器以外使用应用对象。
  • 不需要特殊的接口,绝大多数对象可以做到完全不必依赖容器。

IOC实现原理:

  1. 加载配置文件,解析成 BeanDefinition 放在 Map 里。
  2. 调用 getBean 的时候,从 BeanDefinition 所属的 Map 里,拿出 Class 对象进行实例化,同时,如果有依赖关系,将递归调用 getBean 方法 —— 完成依赖注入。

三、AOP

用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect),减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。可用于权限认证、日志、事务处理等。
在这里插入图片描述

1、AOP 有哪些实现方式?Spring AOP 和 AspectJ AOP 有什么区别?

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

  1. AspectJ是静态代理的增强,所谓静态代理,就是AOP框架会在编译阶段生成AOP代理类,因此也称为编译时增强,他会在编译阶段将AspectJ(切面)织入到Java字节码中,运行的时候就是增强之后的AOP对象。

  2. Spring AOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改字节码,而是每次运行时在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。

2、JDK动态代理和CGLIB动态代理的区别

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

  • JDK动态代理只提供接口的代理,不支持类的代理。核心InvocationHandler接口和Proxy类,InvocationHandler 通过invoke()方法反射来调用目标类中的代码,动态地将横切逻辑和业务编织在一起;接着,Proxy利用 InvocationHandler动态创建一个符合某一接口的的实例, 生成目标类的代理对象。

  • 如果代理类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现AOP。CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。

  • 就二者的效率来说,大部分情况都是 JDK 动态代理更优秀,随着 JDK 版本的升级,这个优势更加明显。

3、AOP中特定名词

  • 切面(Aspect):切面是通知和切点的结合。 在Spring AOP中,切面可以使用通用类(基于模式的风格) 或者在普通类中以 @AspectJ 注解来实现。

  • 连接点(Join point):指方法,在Spring AOP中,一个连接点 总是 代表一个方法的执行。连接点是在应用执行过程中能够插入切面的一个点。这个点可以是调用方法时、抛出异常时、甚至修改一个字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。

  • 通知(Advice):在AOP术语中,切面的工作被称为通知。

  • 切点(Pointcut):切点的定义会匹配通知所要织入的一个或多个连接点。我们通常使用明确的类和方法名称,或是利用正则表达式定义所匹配的类和方法名称来指定这些切点。

  • 引入(Introduction):引入允许我们向现有类添加新方法或属性。

  • 目标对象(Target Object): 被一个或者多个切面(aspect)所通知(advise)的对象。它通常是一个代理对象。

  • 织入(Weaving):织入是把切面应用到目标对象并创建新的代理对象的过程。

编译期:切面在目标类编译时被织入。AspectJ的织入编译器是以这种方式织入切面的。

类加载期:切面在目标类加载到JVM时被织入。需要特殊的类加载器,它可以在目标类被引入应用之前增强该目标类的字节码。AspectJ5的加载时织入就支持以这种方式织入切面。

运行期:切面在应用运行的某个时刻被织入。一般情况下,在织入切面时,AOP容器会为目标对象动态地创建一个代理对象。SpringAOP就是以这种方式织入切面。

4、Spring AOP的通知类型

  • 前置通知(Before):在目标方法被调用之前调用通知功能;
  • 后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么;
  • 返回通知(After-returning ):在目标方法成功执行之后调用通知;
  • 异常通知(After-throwing):在目标方法抛出异常后调用通知;
  • 环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。

注意:环绕通知,最后不要忘了把结果return出去

同一个aspect,不同advice的执行顺序:

①没有异常情况下的执行顺序:

环绕通知   around before advice
前置通知   before advice
目标方法   target method 执行
环绕通知   around after advice
后置通知   after advice
返回通知   afterReturning

②有异常情况下的执行顺序:

环绕通知   around before advice
前置通知   before advice
目标方法   target method 执行
环绕通知   around after advice
后置通知   after advice
异常通知   afterThrowing
异常发生   java.lang.RuntimeException: 

5、Spring AOP内部调用失效问题

一个需要进行AOP增强的类,外部调用methodA()且该方法中调用methodB(),调用methodB()不会执行AOP的增强逻辑。

原因:真正执行methodA()的是目标对象,那么methodA()中调用的methodB()是目标对象的方法,而不是代理对象的。也就自然不会执行AOP的增强逻辑

/**
 * 目标对象类
 */
@Service
public class TestAopService {

    public void methodA() {
        System.out.println("method A run");
        //内部调用方法B时AOP的增强处理方法不会执行
        methodB();
    }
    
    public void methodB() {
        System.out.println("method B run");
    }
}

解决方法

1、开启暴露代理对象

(1)开启暴露代理对象(expose-proxy=“true”),它使用的是ThreadLocal设计模式
(2)目标对象内部调用时获取代理对象,再调用代理对象的方法

<!-- 通过@Aspect注解实现AOP -->
<!-- proxy-target-class="true"表示使用CGlib动态代理 -->
<!-- expose-proxy="true"暴露代理对象 -->
<aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true"/>

import org.springframework.aop.framework.AopContext;
import org.springframework.stereotype.Service;
/**
 * 目标对象类
 * @author Gufung
 *
 */
@Service
public class TestAopService {

    public void methodA() {
        System.out.println("method A run");
        //methodB();
        //从spring上下文获取代理对象执行方法
        ((TestAopService) AopContext.currentProxy()).methodB();
    }
    
    public void methodB() {
        System.out.println("method B run");
    }
}

2、利用 初始化方法 在目标对象中注入代理对象

在目标对象类中注入ApplicationContext,通过ApplicationContext获取代理对象,并调用代理对象的方法。

注意:该方案对于scope为prototype的bean无法适用,因为每次获取bean时都返回一个新的对象。

@Service
public class TestServiceImpl implements TestService {

    @Autowired
    private ApplicationContext context;

    private TestService proxyObject;

    @PostConstruct
    // 初始化方法,在IOC注入完成后会执行该方法
    private void setSelf() {
        // 从spring上下文获取代理对象(直接通过proxyObject=this是不对的,this是目标对象)
        // 此种方法不适合于prototype Bean,因为每次getBean返回一个新的Bean
        proxyObject = context.getBean(TestService.class);
    }

    public void methodA() throws Exception {
        System.out.println("method A run");
        System.out.println("method A 中调用method B,通过注入的代理对象,调用代理对象的方法,解决内部调用实现的问题。");
        proxyObject.methodB(); //调用代理对象的方法,解决内部调用失效的问题
    }

    public void methodB() {
        System.out.println("method B run");
    }
}

Spring AOP内部调用失效问题

变形题

一个类有两个成员方法 A 和 B,两者都加了事务处理注解,定义了事务传播级别为 REQUIRES_NEW,问 A 方法内部直接调用 B 方法时能否触发事务处理机制。

Spring 的事务处理其实是通过AOP实现的,而实现AOP的方法有好几种,对于通过 Jdk 和 cglib 实现的 aop 处理,上述问题的答案为否,对于通过AspectJ实现的,上述问题答案为是。

Spring Boot教程(20) – 用AspectJ实现AOP内部调用

在一个Service内部,事务方法之间的嵌套调用,普通方法和事务方法之间的嵌套调用,都不会开启新的事务.

  1. spring采用动态代理机制来实现事务控制,而动态代理最终都是要调用原始对象的,而原始对象在去调用方法时,是不会再触发代理了!

  2. Spring的事务管理是通过AOP实现的,其AOP的实现对于非final类是通过cglib这种方式,即生成当前类的一个子类作为代理类,然后在调用其下的方法时,会判断这个方法有没有@Transactional注解,如果有的话,则通过动态代理实现事务管理(拦截方法调用,执行事务等切面)。当b()中调用a()时,发现b()上并没有@Transactional注解,所以整个AOP代理过程(事务管理)不会发生。

Spring事务管理嵌套事务详解 : 同一个类中,一个方法调用另外一个有事务的方法

Spring AOP 对象内部方法间的嵌套调用

四、Spring事务

Spring支持两种类型的事务管理:

编程式事务管理:这意味你通过编程的方式管理事务,给你带来极大的灵活性,但是难维护。

声明式事务管理:这意味着你可以将业务代码和事务管理分离,你只需用注解和XML配置来管理事务。

1、Spring事务的实现方式和实现原理

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

2、Spring事务传播行为

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

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

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

  3. PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。(mandatory)

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

  5. PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

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

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

3、Spring事务隔离级别

spring 有五大隔离级别,默认值为 ISOLATION_DEFAULT(使用数据库的设置),其他四个隔离级别和数据库的隔离级别一致:

  • ISOLATION_DEFAULT:用底层数据库的设置隔离级别,数据库设置的是什么我就用什么;

  • ISOLATION_READ_UNCOMMITTED:未提交读,最低隔离级别、事务未提交前,就可被其他事务读取(会出现幻读、脏读、不可重复读);

  • ISOLATION_READ_COMMITTED:提交读,一个事务提交后才能被其他事务读取到(会造成幻读、不可重复读),SQL server 的默认级别;

  • ISOLATION_REPEATABLE_READ:可重复读,保证多次读取同一个数据时,其值都和事务开始时候的内容是一致,禁止读取到别的事务未提交的数据(会造成幻读),MySQL 的默认级别;

  • ISOLATION_SERIALIZABLE:序列化,代价最高最可靠的隔离级别,该隔离级别能防止脏读、不可重复读、幻读。

4、Spring事务常用属性

属性名说明
propagation事务的传播行为,默认值为 REQUIRED
isolation事务的隔离级别,默认值采用 DEFAULT
timeout事务的超时时间,默认值为-1(不会超时)。如果超过该时间限制但事务还没有完成,则自动回滚事务
readOnly指定事务是否为只读事务,默认值为 false
rollbackFor回滚规则,用于指定能够触发事务回滚的异常类型,并且可以指定多个异常类型

五、Spring如何解决循环依赖问题

循环依赖问题在Spring中主要有三种情况:

(1)通过构造方法进行依赖注入时产生的循环依赖问题。
(2)通过setter方法进行依赖注入且是在多例(原型)模式下产生的循环依赖问题。
(3)通过setter方法进行依赖注入且是在单例模式下产生的循环依赖问题。

在Spring中,只有第(3)种方式的循环依赖问题被解决了,其他两种方式在遇到循环依赖问题时都会产生异常。这是因为:

第一种构造方法注入的情况下,在new对象的时候就会堵塞住了,其实也就是”先有鸡还是先有蛋“的历史难题。

第二种setter方法(多例)的情况下,每一次getBean()时,都会产生一个新的Bean,如此反复下去就会有无穷无尽的Bean产生了,最终就会导致OOM问题的出现。

Spring在单例模式下的setter方法依赖注入引起的循环依赖问题,主要是通过二级缓存和三级缓存来解决的,其中三级缓存是主要的。解决的核心原理就是:在对象实例化之后,依赖注入之前,Spring提前暴露的Bean实例的引用在第三级缓存中进行存储。

/**
   * 单例对象的缓存:bean 名称——bean 实例,即:所谓的单例池。
   * 表示已经经历了完整生命周期的 Bean 对象
   * <b>第一级缓存</b>
   */
  Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);/**
   * 早期的单例对象的高速缓存:bean 名称——bean 实例。
   * 表示 Bean 的生命周期还没走完(Bean 的属性还未填充)就把这个 Bean 存入该缓存中
   * 也就是实例化但未初始化的 bean 放入该缓存里
   * 
   * 存放早期暴露出来的 Bean 对象,Bean 的生命周期未结束(属性还未填充完),
   * 可能是代理对象,也可能是原始对象
   * 
   * <b>第二级缓存</b>
   */
  Map<String, Object> earlySingletonObjects = new HashMap<>(16);/**
   * 单例工厂的高速缓存:bean 名称——ObjectFactory。
   * 表示存放生成 bean 的工厂
   * 工厂主要用来生成 Bean 的代理对象
   * <b>第三级缓存</b>
   */
  Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

(1)仅使用一级缓存可以吗?

不可以。 如果直接将未初始化完的 Bean 放在 singletonObjects 里面。当其他类需要使用这个Bean时,但这个Bean还未初始化完,可能会产生空指针异常。

我们以 A 、 B、C 举例子

  1. 先实例化 A 类,叫 a
  2. 将 a 放入 singletonObjects 中(此时 a 中的 b 属性还是空的呢)
  3. C 类需要使用 A 类,去 singletonObjects 获取,且获取到了 a
  4. C 类使用 a,拿出 a 类的 b 属性,然后 NullPointerException了

(2)使用二级缓存可以吗?

二级缓存可以解决循环依赖的问题

看完图,可能还会有疑惑,A 没初始化完成放入了缓存,那么 B 用的岂不是就是未完成的 A,是这样的没错! 在整个过程当中,A 是只有 1 个,而 B 那里的 A 只是 A 的引用,所以后面 A 完成了初始化,B 中的 A 自然也就完成了。
在这里插入图片描述

(3)仅使用二级缓存,存在的问题

Spring 中 Bean 的管理

由 Spring 来管理 Bean,要经过的主要步骤有:

  1. Spring 加载配置文件,扫描哪些类由 Spring 来管理,并为每个类生成一个 BeanDefintion,里面封装了类的一些信息,如全限定类名、哪些属性、是否单例等等
  2. 根据 BeanDefintion 的信息,通过反射,去实例化 Bean(此时就是实例化但未初始化 的 Bean)
  3. 填充上述未初始化对象中的属性(依赖注入
  4. 如果上述未初始化对象中的方法被 AOP 了,那么就需要生成代理类(也叫包装类)
  5. 最后将完成初始化的对象存入缓存中( singletonObjects),下次使用从缓存获取

二级缓存的问题

如果 Bean 没有 AOP,那么用二级缓存其实没有什么问题的,一旦有上述生命周期中第四步,就会导致的一个问题。因为 AOP 处理后,往往是需要生成代理对象的,代理对象和原来的对象根本就不是 1 个对象

  1. 生成 a 的实例,然后放入缓存,a 需要 b
  2. 再生成 b ,填充 b 的时候,需要 a,从缓存中取到了 a,完成 b 的初始化;
  3. 紧接着 a 把初始化好的 b 拿过来用,完成 a 的属性填充和初始化
  4. 由于 A 类涉及到了 AOP,再然后 a 要生成一个代理类:代理 a

a 最终的产物是代理 a,那 b 中其实也应该用代理 a,而现在 b 中用的却是原始的 a 。

代理 a 和原始的 a 不是一个对象,现在这就有问题了。

在这里插入图片描述

(4)三级缓存

singletonFactories 中存的是某个 beanName 及对应的 ObjectFactory,这个 ObjectFactory 其实就是生成这个 Bean 的工厂。

实际中,这个 ObjectFactory 是个 Lambda 表达式:() -> getEarlyBeanReference(beanName, mbd, bean),而且,这个表达式并没有执行。

getEarlyBeanReference

核心就是两步:

  1. 根据 beanName 将它对应的实例化后且未初始化完的 Bean,存入 Map<Object, Object> earlyProxyReferences = new ConcurrentHashMap<>(16);。这个 earlyProxyReferences其实就是用于记录哪些 Bean 执行过 AOP,防止后期再次对 Bean 进行 AOP
  2. 生成该 Bean 对应的代理类返回

那么 getEarlyBeanReference 什么时候被触发,什么时候执行?

  1. 在二级缓存示例中,填充 B 的属性时候,需要 A,

  2. 然后去缓存中拿 A,此时先去第三级缓存中去取 A,如果存在,此时就执行 getEarlyBeanReference 函数,然后该函数就会返回 A 对应的代理对象。

  3. 后续再将该代理对象放入第二级缓存中,也就是 Map<String, Object> earlySingletonObjects里。

(5)三级缓存之间的转换

假如 A 经过第三级缓存,获得代理对象,这个代理对象仍然是未初始化完的。那么就暂时把这个代理对象放入第二级缓存,然后删除该代理对象原本在第三级缓存中的数据(确保后期不会每次都生成新的代理对象),后面其他类要用了 A,就去第二级缓存中找,就获取到了 A 的代理对象,而且都用的是同一个 A 的代理对象,这样后面只需要对这一个代理对象进行完善,其他引入该代理对象的类就都完善了。

再往后面,继续完成 A 的初始化,那么先判断 A 是否存在于 earlyProxyReferences 中, 存在就说明 A 已经经历过 AOP 了,就无须再次 AOP。那 A 就从二级缓存中获取,把 A 的代理类拿出来,填充代理类的属性。

完成后再将 A 的代理对象加入到第一级缓存,再把它原本在第二级缓存中的数据删掉,确保后面还用到 A 的类,直接从第一级缓存中获取。
在这里插入图片描述

Spring如何解决循环依赖问题

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

高频面试题:Spring 如何解决循环依赖?

六、Spring MVC的控制器是不是单例模式,如果是,有什么问题,怎么解决?

是单例模式,所以在多线程访问的时候有线程安全问题。不要用同步,会影响性能,解决方案是在控制器里面不能写字段。

七、SpringMVC工作流程

(1)用户发送请求至前端控制器(DispatcherServlet);
(2)前端控制器收到请求后,调用处理器映射器(HandlerMapping);
(3)处理器映射器根据请求url找到具体的处理器,生成处理器对象和处理器拦截器(如果有则生成),并返回给前端控制器;
(4)前端控制器调用处理器适配器(HandlerAdapter);
(5)处理器适配器经过适配调用 具体处理器(Handler,也叫后端控制器);
(6)处理器执行完成返回ModelAndView;
(7)处理器适配器将处理器的执行结果ModelAndView返回给前端控制器;
(8)前端控制器将ModelAndView传给视图解析器(ViewResolver)进行解析;
(9)视图解析器解析后返回具体View;
(10)前端控制器对View进行渲染视图(即将模型数据填充至视图中)
(11)前端控制器响应用户。
在这里插入图片描述

八、SpringBoot自动装配原理

  1. @SpringBootApplication是自动装配的核心,它由 @Configuration@EnableAutoConfiguration@ComponentScan 注解组成。其中@EnableAutoConfiguration是关键。

    @EnableAutoConfiguration:启用 SpringBoot 的自动配置机制
    @Configuration:允许在上下文中注册额外的 bean 或导入其他配置类
    @ComponentScan: 扫描被@Component (@Service,@Controller)注解的 bean,注解默认会扫描启动类所在的包下所有的类 ,可以自定义不扫描某些 bean。

  2. @EnableAutoConfiguration导入自动配置文件选择器,获取所有候选的配置

  3. 通过spring.factories获取配置类的位置

  4. 根据@ConditionalOnClass检测相关类是否存在,如果不存在,则不加载该配置文件

  5. 如果存在,则获取配置类,在上层方法中封装为Properties供用户使用

在这里插入图片描述

SpringBoot:认认真真梳理一遍自动装配原理

九、@Autowired和@Resource之间的区别

@Autowired是Spring提供的注解,@Resource是JDK提供的注解

(1)@Autowired的执行流程

  1. 先按照类型匹配,即按照当前bean类型寻找是否存在指定类型的bean
  2. 如果找到唯一的bean直接注入
  3. 如果找不到或找到多个则开始按照id注入(名字)
  4. 找到唯一的则注入,找不到则抛出异常,不可能找到多个。

@Autowired和@Qualifier一起使用

  • 此注解可以配合@Autowired使用
  • 一旦同时配置@Autowired和@Qualifier,则不再按照类型去匹配,直接按照id匹配
  • 如果按照id能找到就注入,找不到就抛出异常
  • 此注解可以指定要使用的id,如果不指定默认是空串,会抛出异常

required属性

  • @Autowired(required=true):当使用@Autowired注解的时候,其实默认就是@Autowired(required=true),表示注入的时候,该bean必须存在,否则就会注入失败。
  • @Autowired(required=false):表示忽略当前要注入的bean,如果有直接注入,没有跳过,不会报错。

Spring中@Autowired注解用法详解

(2)@Resource的执行流程

  1. 如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常
  2. 如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常
  3. 如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常
  4. 如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则按类型进行匹配,如果匹配则自动装配;

@Autowired和@Resource的区别是什么?

十、BeanFactory 和 ApplicationContext有什么区别?

BeanFactory和ApplicationContext是Spring的两大核心接口,都可以当做Spring的容器。其中ApplicationContext是BeanFactory的子接口

依赖关系

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

ApplicationContext接口作为BeanFactory的派生,除了提供BeanFactory所具有的功能外,还提供了更完整的框架功能:

  • 继承MessageSource,因此支持国际化。
  • 统一的资源文件访问方式。
  • 提供在监听器中注册bean的事件。
  • 同时加载多个配置文件。
  • 载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层。

加载方式

BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。这样,我们就不能发现一些存在的Spring的配置问题。如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常。

ApplicationContext在容器启动时,一次性创建了所有的单实例Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误,这样有利于检查所依赖属性是否注入。 ApplicationContext启动后预载入所有的单实例Bean,通过预载入单实例bean ,确保当你需要的时候,你就不用等待,因为它们已经创建好了。

相对于基本的BeanFactory,ApplicationContext 唯一的不足是占用内存空间。当应用程序配置Bean较多时,程序启动较慢。

创建方式

BeanFactory通常以编程的方式被创建,ApplicationContext还能以声明的方式创建,如使用ContextLoader

注册方式

BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册。

十一、为什么要用Spring Boot的Starter ,而不是和普通的maven一样导入

可以认为starter是一种服务(和JS的插件类似)——使得使用某个功能的开发者不需要关注各种依赖库的处理,不需要具体的配置信息,由Spring Boot自动通过classpath路径下的类发现需要的Bean,并织入bean

即SpringBoot的自动配置功能

十二、让你写个starter 你会怎么写?

1、编写自己的properties类(用来加载属性文件进行默认的配置)和核心服务类(要自动配置的bean)
2、自定义AutoConfiguration 配置类CustomerAutoConfiguration ,通过@Condition*系列注解控制自动配置的条件。
3、然后在src/main/resources新建文件夹META-INF,然后新建一个spring.factories文件
在里面使用org.springframework.boot.autoconfigure.EnableAutoConfiguration指定我们自定义的自动配置类的全路径。
这样,一个自定义的spring-boot-start工程就完成了。

SpringBoot——自定义一个spring-boot-starter包

十三、Spring中用到的设计模式

工厂模式 : Spring使用工厂模式通过 BeanFactory、ApplicationContext 创建 bean 对象。
代理模式 : Spring AOP 功能的实现。
单例模式 : Spring 中的 Bean 默认都是单例的。
模板方法模式 : Spring 中 jdbcTemplate、hibernateTemplate 等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。
适配器模式 :Spring AOP 的增强或通知(Advice)使用到了适配器模式、spring MVC 中也是用到了适配器模式适配Controller。(springmvc流程—>处理器适配器)

装饰者模式 : 我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。
观察者模式: Spring 事件驱动模型就是观察者模式很经典的一个应用。

Spring 中都用到了那些设计模式?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值