【Spring面试】七、AOP相关

文章目录

  • Q1、什么是AOP?能做什么?
  • Q2、解释下Spring AOP中常见的概念名词
  • Q3、Spring AOP的通知有哪些类型?
  • Q4、Spring AOP和AspectJ AOP有什么区别?
  • Q5、JDK动态代理和CGLIB动态代理的区别是什么?
  • Q6、JavaConfig方式如何启用AOP?如何强制使用cglib?
  • Q7、介绍下AOP有几种实现方式?
  • Q8、什么情况下AOP会失效?怎么解决?
  • Q9、Spring的AOP是在哪里创建的动态代理?
  • Q10、描述Spring AOP完整的实现流程?

Q1、什么是AOP?能做什么?

答案:

AOP(Aspect Oriented Programming),即面向切面编程,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑(如日志采集),抽取并封装形成切面(Aspect)后对已有方法或对象做一个增强,减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。可用于权限认证、日志、事务处理等。

AOPOOP

AOP、OOP在字面上虽然非常类似,但却是面向不同领域的两种设计思想,OOP面向对象编程,针对的是业务处理过程的实体及其属性和行为进行抽象封装,以获得更加清浙高效的逻单元划分。而AOP作为面向对象的一种补充,则是针对业务处理过程中的切面进行提取,已达到业务代码和公共行为代码之间低耦合性的隔离效果,这两种设计思想在目标上有着本质的差异。

Q2、解释下Spring AOP中常见的概念名词

答案:

  • 切面:Aspect,在Spring AOP中就是切面类,切面类中管理者切点和通知
  • 连接点:Join Point,在Spring AOP中就是被增强的业务方法,广义的来说,是程序执行过程中的任意位置,粒度为执行方法、抛出异常、设置变量等
  • 通知:Advice,就是需要增强到业务方法中的公共代码,有五种类型:前置通知、后置通知、异常通知、环绕通知、返回通知
  • 切点:PoinCut,即要增强哪些方法,哪些不需要增强,结合切点表达式来实现匹配
  • 目标对象:Target Object,被增强的那个对象
  • 织入:Weaving,在Spring AOP中就是为目标对象创建动态代理的过程。在Aspectj中比较繁琐,Spring不涉及。

【Spring面试】七、AOP相关_第1张图片

Q3、Spring AOP的通知有哪些类型?

答案:

在AOP术语中,在的某个特定的连接点上执行的动作。Spring切面可以应用5种类型的通知:

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

关于执行顺序:

【Spring面试】七、AOP相关_第2张图片

Q4、Spring AOP和AspectJ AOP有什么区别?

先说下二者的关系:当在Spring中要使用@Aspect、@Before等这些注解时,就需要添加AspectJ相关依赖:

<dependency>
	<groupId>org.aspectjgroupId>
	<artifactId>aspectjweaverartifactId>
	<version>1.9.5version>
dependency>

Spring Aop需要 AspectJ 框架的支持,但只用到的AspectJ的切点解析和匹配(@Aspect、@Before..等这些注解都是由AspectJ 发明的),底层的实现则没有用AspectJ的静态织入。

答案:

  • AOP实现的关键在于代理模式,AOP代理主要分为静态代理和动态代理。静态代理的代表为AspectJ,动态代理则以Spring AOP为代表
  • Spring AOP基于动态代理实现(JDK提供的动态代理和CGLIB动态代理)
  • AspectJ是静态代理的增强,就是AOP框架会在编译阶段生成AOP代理类,在编译阶段就将增强代码织入到Java字节码(.class)中,因此也叫编译时增强
PS:关于织入时机,可了解下:

【Spring面试】七、AOP相关_第3张图片

  • AspcetJ是AOP的完全解决方案,能做很多Spring AOP不能做的事,Spring AOP则只专注于解决企业级开发中常见的AOP需求
  • AspectJ在实际代码运行前就完成了织入,而Spring AOP是Spring容器启动时才生成需要的代理对象,在启动上相比AspectJ会有一点性能损失

Q5、JDK动态代理和CGLIB动态代理的区别是什么?

答案:

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

JDK动态代理:
  • JDK动态代理只提供接口的代理,不支持类的代理
  • JDK会在运行时为目标类生成一个动态代理类$proxy*.class
  • 该代理类是实现了目标类接口, 并且代理类会实现接口所有的方法,并加增强代码
  • 调用时 通过代理类先去调用处理类进行增强,再通过反射的方式进行调用目标方法,从而实现AOP
CGLIB动态代理:

如果代理类没有实现 接口,那么Spring AOP会选择使用CGLIB来动态代理目标:

  • CGLIB的底层是通过ASM在运行时动态的生成目标类的一个子类(会生成多个,还有其他相关类,主要是为增强调用时效率)
  • 并且会重写父类所有的方法增强代码
  • 调用时先通过代理类进行增强,再直接调用父类对应的方法进行调用原目标方法,从而实现AOP

需要注意的是:

  • CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为fnal,那么它是无法使用CGLIB做动态代理的
  • CGLIB 除了生成目标子类代理类,还有一个FasiClass(路由类),可以(但不是必须)让本类方法调用进行增强,而不会像Jdk代理那样本类方法调用增强会失效

最后,关于JDK动态代理和CGLIB动态代理的性能问题:

  • jdk动态代理生成类速度快,调用慢,cglib生成类速度慢,但后续调用快
  • 在老版本中,CGLIB速度优于JDK动态代理
  • 在新版本(JDK7、JDK8…),JDK动态代理性能要比CGLIB好20%左右,总之就是看版本

Q6、JavaConfig方式如何启用AOP?如何强制使用cglib?

答案:

  • 首先需要添加Spring AOP和AspectJ的依赖
  • 其次加@EnableAspectJAutoProxy注解

@EnableAspectJAutoProxy注解有两个属性:

  • proxyTargetClass = true ,即强制使用cglib动态代理
  • exposeProxy = true, 即在线程中暴露代理对象

PS:所谓在线程中暴露代理对象,就是为true时,可以拿到当前的代理对象,这样就可以防止本类方法不会增强的问题。

//拿到当前的代理对象,记得再转型为本类
AopContext.currentProxy()
//用法:本类中的方法调用其他方法
( (本类)AopContext.currentProxy() ).本类中的其他方法();

Q7、介绍下AOP有几种实现方式?

答案:

  • 第一种:Spring1.2,基于接口来实现,也是现在注解背后的逻辑

【Spring面试】七、AOP相关_第4张图片

【Spring面试】七、AOP相关_第5张图片

  • 第二种:Spring2.0的schema-based配置,使用xml以及标签

【Spring面试】七、AOP相关_第6张图片

  • 第三种:Spring2.0的@AspectJ配置,基于注解实现
  • 第四种:以上三种是Spring AOP的实现,也可以直接用AspectJ框架,这时和Spring没有关系了,静态织入+AspectJ单独编译

Q8、什么情况下AOP会失效?怎么解决?

答案:

内部调用不会触发AOP,即同一个类里的方法A内部调用方法B,则执行方法A时,中间的方法B不会被增强。

public class AopTest{

	public void add(){
	}
	
	public void mod(){
		this.add();  //这里不是增强的add方法
	}

}

【Spring面试】七、AOP相关_第7张图片
解决思路是必须走代理,用代理对象去调用

【Spring面试】七、AOP相关_第8张图片
想拿到动态代理对象,有两种方式:

  • 方式一:在这个类里,注入对象自身
public class AopTest{

	@Autowired
	AopTest aopTest;

	public void add(){
	}
	
	public void mod(){
		aopTest.add();  //此时是增强的add方法
	}

}
  • 方式二:设置暴露当前代理对象到本地线程,这样AopContext.currentProxy()就可以拿到正在调用的代理对象
@EnableAspectJAutoProxy(exposeProxy = true)
//....
public class AopTest{

	public void add(){
	}
	
	public void mod(){
		((AopTest) AopContext.currentProxy()).add();   
		//这个封装的静态方法currentProxy,底层在操作ThreadLocal
	}

}

最后补充下其余失效场景:(多为使用不当)

  • 方法是private修饰,需要改为public
  • 目标类没有配置为Bean,Spring是为Bean去创建动态代理

Q9、Spring的AOP是在哪里创建的动态代理?

答案:

  • 正常的Bean在Bean的生命周期初始化之后,通过Bean的后置处理器来创建aop(BeanPostProcessor.postProcessAfterInitialization)

【Spring面试】七、AOP相关_第9张图片

  • 还有一种特殊的情况,即在Bean的生命周期的属性注入阶段,当出现循环依赖,也会为循环依赖的Bean创建aop(MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition)

Q10、描述Spring AOP完整的实现流程?

答案:

以JavaConfig为例(xml实现AOP的方式跳过):@EnableAspectJAutoProxy会通过@Import注册一个Bean的后置处理器来处理AOP,然后:

【Spring面试】七、AOP相关_第10张图片

  • 解析切面:在创建Bean之前,调用Bean的后置处理器去解析切面,一个通知解析成一个advisor对象,这个对象中包含着通知和被增强的pointcut

【Spring面试】七、AOP相关_第11张图片

  • 创建动态代理:正常的Bean初始化后调用BeanPostProcessor拿到之前缓存的advisor,再通过advisor里的pointcut来判断当前Bean是否被切点表达式匹配,如果匹配,就会为Bean创建动态代理

【Spring面试】七、AOP相关_第12张图片

  • 调用:通过之前创建的动态代理,调用方法执行增强,通过调用链设计模式依次调用各个类型的通知方法

【Spring面试】七、AOP相关_第13张图片

你可能感兴趣的:(面试,spring,面试,数据库)