死磕Spring之AOP篇 - Spring AOP常见面试题,java后端面试题

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024b (备注Java)
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

正文

  • Spring AOP:非完整的 AOP 实现框架

Spring AOP 是基于 JDK 动态代理和 Cglib 提升实现的,两种代理方式都属于运行时的一个方式,所以它没有编译时的一个处理,那么因此 Spring 是通过 Java 代码实现的。AspectJ 自己有一个编译器,在编译时期可以修改 .class 文件,在运行时也会进行处理。

Spring AOP 有别于其他大多数 AOP 实现框架,目的不是提供最完整的 AOP 实现(尽管 Spring AOP 相当强大);相反,其目的是在 AOP 实现和 Spring IoC 之间提供紧密的集成,以提供企业级核心特性。

Spring AOP 从未打算与 AspectJ 竞争以提供全面的 AOP 解决方案,我们认为 Spring AOP 等基于代理实现的框架和 AspectJ 等成熟的框架都是有价值的,并且它们是互补的,而不是竞争关系。Spring 将 Spring AOP 和 IoC 与 AspectJ 无缝集成,以实现 AOP 的所有功能都可以在一个 Spring 应用中。这种集成不会影响 Spring AOP API 或 AOP Alliance API,保持向后兼容。

什么是 AOP 代理?

代理模式是一种结构性设计模式,通过代理类为其他对象提供一种代理以控制对这个对象的访问。AOP 代理是 AOP 框架中 AOP 的实现,主要分为静态代理和动态代理,如下:

  • 静态代理:代理类需要实现被代理类所实现的接口,同时持有被代理类的引用,新增处理逻辑,进行拦截处理,不过方法还是由被代理类的引用所执行。静态代理通常需要由开发人员在编译阶段就定义好,不易于维护。

  • 常用 OOP 继承和组合相结合

  • AspectJ,在编辑阶段会织入 Java 字节码,且在运行期间会进行增强。

  • 动态代理:不会修改字节码,而是在 JVM 内存中根据目标对象新生成一个 Class 对象,这个对象包含了被代理对象的全部方法,并且在其中进行了增强。

  • JDK 动态代理

  • 字节码提升,例如 CGLIB

讲讲 JDK 动态代理?

基于接口代理,通过反射机制生成一个实现代理接口的类,在调用具体方法时会调用 InvocationHandler 来处理。

需要借助 JDK 的 java.lang.reflect.Proxy 来创建代理对象,调用 Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 方法创建一个代理对象,方法的三个入参分别是:

  • ClassLoader loader:用于加载代理对象的 Class 类加载器

  • Class<?>[] interfaces:代理对象需要实现的接口

  • InvocationHandler h:代理对象的处理器

新生成的代理对象的 Class 对象会继承 Proxy,且实现所有的入参 interfaces 中的接口,在实现的方法中实际是调用入参 InvocationHandler 的 invoke(..) 方法。

为什么 JDK 动态代理只能基于接口代理,不能基于类代理?

因为 JDK 动态代理生成的代理对象需要继承 Proxy 这个类,在 Java 中类只能是单继承关系,无法再继承一个代理类,所以只能基于接口代理。

为什么 InvocationHandler 不直接声明到这个代理对象里面,而是放入继承的 Proxy 父类中?

我觉得代理类既然是 JDK 动态生成的,那么 JDK 就需要识别出哪些类是生成的代理类,哪些是非代理类,或者说 JDK 需要对代理类做统一的处理,这时如果没有一个统一的类 Proxy 来进行引用根本无法处理。这只是笔者的想法,具体为什么这么做不知道有小伙伴知道不 ~

讲讲 CGLIB 动态代理?

JDK 动态代理的目标对象必须是一个接口,在我们日常生活中,无法避免开发人员不写接口直接写类,或者根本不需要接口,直接用类进行表达。这个时候我们就需要通过一些字节码提升的手段,来帮助做这个事情,在运行时,非编译时,来创建一个新的 Class 对象,这种方式称之为字节码提升。在 Spring 内部有两个字节码提升的框架,ASM(过于底层,直接操作字节码)和 CGLIB(相对于前者更加简便)。

CGLIB 动态代理则是基于类代理(字节码提升),通过 ASM(Java 字节码的操作和分析框架)将被代理类的 class 文件加载进来,修改其字节码生成一个子类。

需要借助于 CGLIB 的 org.springframework.cglib.proxy.Enhancer 类来创建代理对象,设置以下几个属性:

  • Class<?> superClass:被代理的类

  • Callback callback:回调接口

新生成的代理对象的 Class 对象会继承 superClass 被代理的类,在重写的方法中会调用 callback 回调接口(方法拦截器)进行处理。

如果你想设置一个 Callback[] 数组去处理不同的方法,那么需要设置一个 CallbackFilter 筛选器,用于选择具体的方法使用数组中的哪个 Callback 去处理

JDK 动态代理和 CGLIB 动态代理有什么不同?

两者都是在 JVM 运行时期新创建一个 Class 对象,实例化一个代理对象,对目标类(或接口)进行代理。JDK 动态代理只能基于接口进行代理,生成的代理类实现了这些接口;而 CGLIB 动态代理则是基于类进行代理的,生成的代理类继承目标类,但是不能代理被 final 修饰的类,也不能重写 final 或者 private 修饰的方法。

CGLIB 动态代理比 JDK 动态代理复杂许多,性能也相对比较差。

Spring AOP 和 AspectJ 有什么关联?

Spring AOP 和 AspectJ 都是 AOP 的实现框架,AspectJ 是 AOP 的完整实现,Spring AOP 则是部分实现。AspectJ 有一个很好的编程模型,包含了注解的方式,也包含了特殊语法。Spring 认为 AspectJ 的实现在 AOP 体系里面是完整的,不需要在做自己的一些实现。

Spring AOP 整合 AspectJ 注解与 Spring IoC 容器,比 AspectJ 的使用更加简单,也支持 API 和 XML 的方式进行使用。不过 Spring AOP 仅支持方法级别的 Pointcut 拦截。

为什么 Spring AOP 底层没有使用 AspectJ 动态代理创建代理对象?

我觉得是因为 AspectJ 的特殊语法对于 Spring 或者 Java 开发人员来说不是很友好,使用起来可能有点困难。Spring 也选择整合 AspectJ 的注解,使用起来非常方便。

Spring AOP 中有哪些 Advice 类型?

  • Around Advice,围绕型通知器,需要主动去触发目标方法的执行,这样可以在触发的前后进行相关相关逻辑处理

  • Before Advice,前置通知器,在目标方法执行前会被调用

  • After Advice,后置通知器

  • AfterReturning,在目标方法执行后被调用(方法执行过程中出现异常不会被调用)

  • After,在目标方法执行后被调用(执行过程出现异常也会被调用)

  • AfterThrowing,执行过程中抛出异常后会被调用(如果异常类型匹配)

执行顺序(Spring 5.2.7 之前的版本):Around “前处理” > Before > 方法执行 > Around “后处理” > After > AfterReturning|AfterThrowing

执行顺序(Spring 5.2.7 开始):Around “前处理” > Before > 方法执行 > AfterReturning|AfterThrowing > After > Around “后处理”

如下(在后续文章会进行分析,Spring 5.1.14):

Spring AOP 中 Advisor 接口是什么?

Advisor 是 Advice 的一个容器接口,与 Advice 是一对一的关系,它的子接口 PointcutAdvisor 是 Pointcut 和 Advice 的容器接口,将 Pointcut 过滤 Joinpoint 的能力和 Advice 进行整合,这样一来就将两者进行关联起来了。

Pointcut 提供 ClassFilter 和 MethedMatcher,分别支持筛选类和方法,通过 PointcutAdvisor 和 Advice 进行整合,可以说是形成了一个“切面”。

简述 Spring AOP 自动代理的实现

在我们有了 Join point(连接点)、Pointcut(切点)、Advice(通知)以及 AspectJ(切面)后,我们应该如何将他们“织入”我们的应用呢?在 Sping AOP 中提供了自动代理的实现,底层借助 JDK 动态代理和 CGLIB 动态代理创建对象。

回顾 Spring IoC 中 Bean 的加载过程,在整个过程中,Bean 的实例化前和初始化后等生命周期阶段都提供了扩展点,会调用相应的 BeanPostProcessor 处理器对 Bean 进行处理。当我们开启了 AspectJ 自动代理(例如通过 @EnableAspectJAutoProxy 注解),则会往 IoC 容器中注册一个 AbstractAutoProxyCreator 自动代理对象,该对象实现了几种 BeanPostProcessor,例如在每个 Bean 初始化后会被调用,解析出当前 Spring 上下文中所有的 Advisor(会缓存),如果这个 Bean 需要进行代理,则会通过 JDK 动态代理或者 CGLIB 动态代理创建一个代理对象并返回,所以得到的这个 Bean 实际上是一个代理对象。这样一来,开发人员只需要配置好 AspectJ 相关信息,Spring 则会进行自动代理,和 Spring IoC 完美地整合在一起。

参考:《死磕Spring之IoC篇 - Bean 的创建过程》

请解释 Spring @EnableAspectJAutoProxy 的原理?

使用了 @EnableAspectJAutoProxy 注解则会开启 Spring AOP 自动代理,该注解上面有一个 @Import(AspectJAutoProxyRegistrar.class) 注解,AspectJAutoProxyRegistrar 实现了 ImportBeanDefinitionRegistrar 这个接口,在实现的方法中会注册一个 AnnotationAwareAspectJAutoProxyCreator 自动代理对象(如果没有注册的话),且将其优先级设置为最高,同时解析 @EnableAspectJAutoProxy 注解的配置并进行设置。这个自动代理对象是一个 BeanPostProcessor 处理器,在 Spring 加载一个 Bean 的过程中,如果它需要被代理,那么会创建一个代理对象(JDK 动态代理或者 CGLIB 动态代理)。

除了注解的方式,也可以通过 <aop:aspectj-autoproxy /> 标签开启 Spring AOP 自动代理,原理和注解相同,同样是注册一个自动代理对象。

@Import 注解的原理参考:《死磕Spring之IoC篇 - @Bean 等注解的实现原理》

XML 自定义标签的原理参考:《死磕Spring之IoC篇 - 解析自定义标签(XML 文件)》

Spring Configuration Class CGLIB 提升与 AOP 类代理关系?

在 Spring 底层 IoC 容器初始化后,会通过 BeanDefinitionRegistryPostProcessor 对其进行后置处理,其中会有一个 ConfigurationClassPostProcessor 处理器会对 @Configuration 标注的 BeanDefinition 进行处理,进行 CGLIB 提升,这样一来对于后续的 Spring AOP 工作就非常简单了,因为这个 Bean 天然就是一个 CGLIB 代理。

在 Spring 5.2 开始 @Configuration 注解中新增了一个 proxyBeanMethods 属性(默认为 true),支持显示的配置是否进行 CGLIB 提升,毕竟进行 CGLIB 提升在启动过程会有一定的性能损耗,且创建的代理对象会占有一定的内存,通过该配置进行关闭,可以减少不必要的麻烦,对 Java 云原生有一定的提升。

@Configuration 注解的 Bean 进行 CGLIB 提升后有什么作用呢?

举个例子,大多数情况下,@Configuration Class 会通过 @Bean 注解为 Bean 定义,比如 @Bean User user() { return new User(); },那这样是不是每次主动调用这个方法都会返回一个新的 User 对象呢?

不是的,@Configuration Class 在得到 CGLIB 提升后,会设置一个拦截器专门对 @Bean 方法进行拦截处理,通过依赖查找的方式从 IoC 容器中获取 Bean 对象,如果是单例 Bean,那么每次都是返回同一个对象,所以当主动调用这个方法时获取到的都是同一个 User 对象。

参考:《死磕Spring之IoC篇 - @Bean 等注解的实现原理》

Sping AOP 应用到哪些设计模式?

最后

ActiveMQ消息中间件面试专题

  • 什么是ActiveMQ?
  • ActiveMQ服务器宕机怎么办?
  • 丢消息怎么办?
  • 持久化消息非常慢怎么办?
  • 消息的不均匀消费怎么办?
  • 死信队列怎么办?
  • ActiveMQ中的消息重发时间间隔和重发次数吗?

ActiveMQ消息中间件面试专题解析拓展:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传


redis面试专题及答案

  • 支持一致性哈希的客户端有哪些?
  • Redis与其他key-value存储有什么不同?
  • Redis的内存占用情况怎么样?
  • 都有哪些办法可以降低Redis的内存使用情况呢?
  • 查看Redis使用情况及状态信息用什么命令?
  • Redis的内存用完了会发生什么?
  • Redis是单线程的,如何提高多核CPU的利用率?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传


Spring面试专题及答案

  • 谈谈你对 Spring 的理解
  • Spring 有哪些优点?
  • Spring 中的设计模式
  • 怎样开启注解装配以及常用注解
  • 简单介绍下 Spring bean 的生命周期

Spring面试答案解析拓展

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传


高并发多线程面试专题

  • 现在有线程 T1、T2 和 T3。你如何确保 T2 线程在 T1 之后执行,并且 T3 线程在 T2 之后执行?
  • Java 中新的 Lock 接口相对于同步代码块(synchronized block)有什么优势?如果让你实现一个高性能缓存,支持并发读取和单一写入,你如何保证数据完整性。
  • Java 中 wait 和 sleep 方法有什么区别?
  • 如何在 Java 中实现一个阻塞队列?
  • 如何在 Java 中编写代码解决生产者消费者问题?
  • 写一段死锁代码。你在 Java 中如何解决死锁?

高并发多线程面试解析与拓展

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传


jvm面试专题与解析

  • JVM 由哪些部分组成?
  • JVM 内存划分?
  • Java 的内存模型?
  • 引用的分类?
  • GC什么时候开始?

JVM面试专题解析与拓展!

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
?

  • 引用的分类?
  • GC什么时候开始?

JVM面试专题解析与拓展!

[外链图片转存中…(img-4pA3rT0E-1713139671975)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-wxGBrp4Q-1713139671975)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 21
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值