答案:
AOP(Aspect Oriented Programming),即面向切面编程,用于将那些与业务无关
,但却对多个对象产生影响的公共行为和逻辑
(如日志采集),抽取并封装形成切面(Aspect)后对已有方法或对象做一个增强,减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。可用于权限认证、日志、事务处理等。
AOP与OOP?
AOP、OOP在字面上虽然非常类似,但却是面向不同领域的两种设计思想,OOP面向对象编程,针对的是业务处理过程的实体及其属性和行为进行抽象封装
,以获得更加清浙高效的逻单元划分。而AOP作为面向对象的一种补充,则是针对业务处理过程中的切面进行提取,已达到业务代码和公共行为代码之间低耦合性的隔离效果,这两种设计思想在目标上有着本质的差异。
答案:
被增强的业务方法
,广义的来说,是程序执行过程中的任意位置,粒度为执行方法、抛出异常、设置变量等公共代码
,有五种类型:前置通知、后置通知、异常通知、环绕通知、返回通知要增强哪些方法,哪些不需要增强
,结合切点表达式来实现匹配答案:
在AOP术语中,在的某个特定的连接点上执行的动作。Spring切面可以应用5种类型的通知:
关于执行顺序:
先说下二者的关系:当在Spring中要使用@Aspect、@Before等这些注解时,就需要添加AspectJ相关依赖:
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.9.5version>
dependency>
Spring Aop需要 AspectJ 框架的支持
,但只用到的AspectJ的切点解析和匹配(@Aspect、@Before..等这些注解都是由AspectJ
发明的),底层的实现则没有用AspectJ的静态织入。
答案:
在编译阶段就将增强代码织入到Java字节码(.class)中
,因此也叫编译时增强PS:关于织入时机,可了解下:
完全解决方案
,能做很多Spring AOP不能做的事,Spring AOP则只专注于
解决企业级开发中常见的AOP需求答案:
Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理:
JDK动态代理:
$proxy*.class
CGLIB动态代理:
如果代理类没有实现 接口,那么Spring AOP会选择使用CGLIB来动态代理目标:
需要注意的是:
最后,关于JDK动态代理和CGLIB动态代理的性能问题:
答案:
@EnableAspectJAutoProxy
注解@EnableAspectJAutoProxy注解有两个属性:
PS:所谓在线程中暴露代理对象,就是为true时,可以拿到当前的代理对象,这样就可以防止本类方法不会增强的问题。
//拿到当前的代理对象,记得再转型为本类
AopContext.currentProxy()
//用法:本类中的方法调用其他方法
( (本类)AopContext.currentProxy() ).本类中的其他方法();
答案:
第一种
:Spring1.2,基于接口
来实现,也是现在注解背后的逻辑第二种
:Spring2.0的schema-based配置
,使用xml以及
标签第三种
:Spring2.0的@AspectJ配置,基于注解实现第四种
:以上三种是Spring AOP的实现,也可以直接用AspectJ框架,这时和Spring没有关系了,静态织入+AspectJ单独编译答案:
内部调用不会触发AOP
,即同一个类里的方法A内部调用方法B,则执行方法A时,中间的方法B不会被增强。
public class AopTest{
public void add(){
}
public void mod(){
this.add(); //这里不是增强的add方法
}
}
方式一:
在这个类里,注入对象自身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
}
}
最后补充下其余失效场景:(多为使用不当)
答案:
初始化之后
,通过Bean的后置处理器来创建aop(BeanPostProcessor.postProcessAfterInitialization)属性注入阶段,当出现循环依赖
,也会为循环依赖的Bean创建aop(MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition)答案:
以JavaConfig为例(xml实现AOP的方式跳过):@EnableAspectJAutoProxy会通过@Import注册一个Bean的后置处理器来处理AOP,然后:
解析切面
:在创建Bean之前,调用Bean的后置处理器去解析切面,一个通知解析成一个advisor对象,这个对象中包含着通知和被增强的pointcut创建动态代理
:正常的Bean初始化后调用BeanPostProcessor拿到之前缓存的advisor,再通过advisor里的pointcut来判断当前Bean是否被切点表达式匹配,如果匹配,就会为Bean创建动态代理调用
:通过之前创建的动态代理,调用方法执行增强,通过调用链设计模式依次调用各个类型的通知方法