本文收集了一些在面试时被经常问及的关于 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 的生命周期
- Spring 对 Bean 进行实例化(相当于程序中的 new Xx())
- Spring 将值和 Bean 的引用注入进 Bean 对应的属性中
- 如果 Bean 实现了 BeanNameAware 接口,Spring 将 Bean 的 ID 传递给 setBeanName()方法(实现 BeanNameAware 清主要是为了通过 Bean 的引用来获得 Bean 的 ID,一般业务中是很少有用到 Bean 的 ID 的)
- 如果 Bean 实现了 BeanFactoryAware 接口,Spring 将调用 setBeanDactory(BeanFactory bf)方法并把 BeanFactory 容器实例作为参数传入。(实现 BeanFactoryAware 主要目的是为了获取 Spring 容器,如 Bean 通过 Spring 容器发布事件等)
- 如果 Bean 实现了 ApplicationContextAwaer 接口,Spring 容器将调用 setApplicationContext(ApplicationContext ctx)方法,把 y 应用上下文作为参数传入.(作用与 BeanFactory 类似都是为了获取 Spring 容器,不同的是 Spring 容器在调用 setApplicationContext 方法时会把它自己作为 setApplicationContext 的参数传入,而 Spring 容器在调用 setBeanDactory 前需要程序员自己指定(注入)setBeanDactory 里的参数 BeanFactory)
- 如果 Bean 实现了 BeanPostProcess 接口,Spring 将调用它们的 postProcessBeforeInitialization(预初始化)方法(作用是在 Bean 实例创建成功后对进行增强处理,如对 Bean 进行修改,增加某个功能)
- 如果 Bean 实现了 InitializingBean 接口,Spring 将调用它们的 afterPropertiesSet 方法,作用与在配置文件中对 Bean 使用 init-method 声明初始化的作用一样,都是在 Bean 的全部属性设置成功后执行的初始化方法。(为同一个 bean 配置多个生命周期机制,创建顺序为:@postconstruct -> afterPropertiesSet() -> init-method, 销毁顺序为:@PreDestroy -> destroy() -> destroy-method)
- 如果 Bean 实现了 BeanPostProcess 接口,Spring 将调用它们的 postProcessAfterInitialization(后初始化)方法(作用与 6 的一样,只不过 6 是在 Bean 初始化前执行的,而这个是在 Bean 初始化后执行的,时机不同)
- 经过以上的工作后,Bean 将一直驻留在应用上下文中给应用使用,直到应用上下文被销毁
- 如果 Bean 实现了 DispostbleBean 接口,Spring 将调用它的 destory 方法,作用与在配置文件中对 Bean 使用 destory-method 属性的作用一样,都是在 Bean 实例销毁前执行的方法
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于