Spring 常见问题汇总

本贴最后更新于 1611 天前,其中的信息可能已经东海扬尘

本文收集了一些在面试时被经常问及的关于 Spring 的主要问题,这些问题有可能在下次面试时就会被问到。

AOP 原理

AOP 的家庭成员

PointCut

即在哪个地方进行切入,它可以指定某一个点,也可以指定多个点。比如类 A 的 methord 函数,当然一般的 AOP 与语言(AOL)会采用多用方式来定义 PointCut,比如说利用正则表达式,可以同时指定多个类的多个函数。

Advice

在切入点干什么,指定在 PointCut 地方做什么事情(增强),打日志、执行缓存、处理异常等等。

Advisor/Aspect

PointCut + Advice 形成了切面 Aspect,这个概念本身即代表切面的所有元素。但到这一地步并不是完整的,因为还不知道如何将切面植入到代码中,解决此问题的技术就是 PROXY。

Proxy

Proxy 即代理,其不能算做 AOP 的家庭成员,更相当于一个管理部门,它管理了 AOP 的如何融入 OOP。之所以将其放在这里,是因为 Aspect 虽然是面向切面核心思想的重要组成部分,但其思想的践行者却是 Proxy,也是实现 AOP 的难点与核心据在。

AOP 代理

AOP 代理主要分为静态代理和动态代理,静态代理的代表为 AspectJ;而动态代理则以 Spring AOP 为代表。AspectJ 在编译时就增强了目标对象,Spring AOP 的动态代理则是在每次运行时动态的增强,生成 AOP 代理对象,区别在于生成 AOP 代理对象的时机不同,相对来说 AspectJ 的静态代理方式具有更好的性能,但是 AspectJ 需要特定的编译器进行处理,而 Spring AOP 则无需特定的编译器处理。

AspectJ

AspectJ 是静态代理的增强,所谓的静态代理就是 AOP 框架会在编译阶段生成 AOP 代理类,因此也称为编译时增强。实质为修改字节码。

Spring AOP

与 AspectJ 相同的是,Spring AOP 同样需要对目标类进行增强,也就是生成新的 AOP 代理类;与 AspectJ 不同的是,Spring AOP 无需使用任何特殊命令对 Java 源代码进行编译,它采用运行时动态地、在内存中临时生成“代理类”的方式来生成 AOP 代理。

  • before
    -- sayHello() --
    class com.sun.proxy.$Proxy53
    

    可以看到类型是 com.sun.proxy.$Proxy53,也就是前面提到的 Proxy 类,因此这里 Spring AOP 使用了 JDK 的动态代理。

  • before
    -- sayHello() --
    class com.listenzhangbin.aop.Chinese$$EnhancerBySpringCGLIB$$56b89168
    

    可以看到类被 CGLIB 增强了,也就是动态代理。这里的 CGLIB 代理就是 Spring AOP 的代理,这个类也就是所谓的 AOP 代理,AOP 代理类在切点动态地织入了增强处理。

Bean 的生命周期

Bean 的生命周期

  1. Spring 对 Bean 进行实例化(相当于程序中的 new Xx())
  2. Spring 将值和 Bean 的引用注入进 Bean 对应的属性中
  3. 如果 Bean 实现了 BeanNameAware 接口,Spring 将 Bean 的 ID 传递给 setBeanName()方法(实现 BeanNameAware 清主要是为了通过 Bean 的引用来获得 Bean 的 ID,一般业务中是很少有用到 Bean 的 ID 的)
  4. 如果 Bean 实现了 BeanFactoryAware 接口,Spring 将调用 setBeanDactory(BeanFactory bf)方法并把 BeanFactory 容器实例作为参数传入。(实现 BeanFactoryAware 主要目的是为了获取 Spring 容器,如 Bean 通过 Spring 容器发布事件等)
  5. 如果 Bean 实现了 ApplicationContextAwaer 接口,Spring 容器将调用 setApplicationContext(ApplicationContext ctx)方法,把 y 应用上下文作为参数传入.(作用与 BeanFactory 类似都是为了获取 Spring 容器,不同的是 Spring 容器在调用 setApplicationContext 方法时会把它自己作为 setApplicationContext 的参数传入,而 Spring 容器在调用 setBeanDactory 前需要程序员自己指定(注入)setBeanDactory 里的参数 BeanFactory)
  6. 如果 Bean 实现了 BeanPostProcess 接口,Spring 将调用它们的 postProcessBeforeInitialization(预初始化)方法(作用是在 Bean 实例创建成功后对进行增强处理,如对 Bean 进行修改,增加某个功能)
  7. 如果 Bean 实现了 InitializingBean 接口,Spring 将调用它们的 afterPropertiesSet 方法,作用与在配置文件中对 Bean 使用 init-method 声明初始化的作用一样,都是在 Bean 的全部属性设置成功后执行的初始化方法。(为同一个 bean 配置多个生命周期机制,创建顺序为:@postconstruct -> afterPropertiesSet() -> init-method, 销毁顺序为:@PreDestroy -> destroy() -> destroy-method)
  8. 如果 Bean 实现了 BeanPostProcess 接口,Spring 将调用它们的 postProcessAfterInitialization(后初始化)方法(作用与 6 的一样,只不过 6 是在 Bean 初始化前执行的,而这个是在 Bean 初始化后执行的,时机不同)
  9. 经过以上的工作后,Bean 将一直驻留在应用上下文中给应用使用,直到应用上下文被销毁
  10. 如果 Bean 实现了 DispostbleBean 接口,Spring 将调用它的 destory 方法,作用与在配置文件中对 Bean 使用 destory-method 属性的作用一样,都是在 Bean 实例销毁前执行的方法
  • Spring

    Spring 是一个开源框架,是于 2003 年兴起的一个轻量级的 Java 开发框架,由 Rod Johnson 在其著作《Expert One-On-One J2EE Development and Design》中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 JavaEE 应用程序开发提供集成的框架。

    942 引用 • 1459 回帖 • 31 关注
  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3186 引用 • 8212 回帖 • 1 关注
1 操作
AlanSune 在 2020-06-07 21:10:21 更新了该帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...