纵向关系OOP,横向关系AOP
什么是AOP:面向切面编程,只需要关注主业务逻辑,与主业务无关的事情(非核心功能—通用业务)提取出来横切在主业务之上,需要的时候注入到业务代码中。
应用场景:不同的主业务模块需要引用通用的业务模块,就可以使用AOP
连接点:程序执行过程中某个特定的点,比如方法的调用时,处理异常时,即一个连接点表示一个方法的执行。
切入点:连接点的基础上增加切入规则,选出的连接点就是切入点。
通知:切入点上执行的动作,许多AOP框架都是以拦截器作为通知模型,通过before,after,around等规定通知相对于切点的执行位置。
切面:通知所在的类,横切多个对象。
静态代理:
动态代理:
切入点表达式:
- 粗粒度:
within()
可以使用通配符*
,匹配指定包下的所有类,但注意,只是该包下的类不包括该包的子孙包。如果要匹配子孙包下的类,需要使用..*
。
<aop:config>
<!--配置切入点-->
<aop:pointcut expression="切入规则" id="" />
<!--配置切面-->
<aop:aspect ref="切面类名" >
<aop:before method="通知方法" pointcut-ref="切入点的id">
<aop:aspect />
</aop:config>
// 切面类,通知→何时,切入规则→应用的位置,通知的方法→做什么 即 何时何处
做什么→切面
@Aspect
class A {
@Advice()
public void a(){
}
}
- 细粒度:
execution()
<aop:pointcut expression="execution(* service..*.*(..))" id="pc1"/>
出指定包及其子孙包下所有的类中的任意方法,参数数量及类型不限,返回值类
型不限。
通知
前置通知
后置通知:没有异常会执行
环绕通知:必须显示调用目标方法,否则目标方法不会执行,显示调用通过ProceedingJoinPoint来实现的。
接收一个ProceedingJoinPoint类型的参数对象,调用proceed执行目标方法。
异常通知:有异常不会执行后置通知,执行异常通知
最终通知:最终通知有没有异常都会执行,无返回值。
SpringAOP原理及实现方式:Spring在创建bean时,除了创建目标对象bean之外,还会根据AOP配置生成目标对象的代理对象,将其存储。之后获取bean时得到的其实是代理对象,在代理对象中根据切入点规则决定哪些方法不处理直接执行,哪些方法拦截后进行增强,需要增强的方法拦截后,根据配置调用指定切面中指定的通知执行增强操作。
Spring自动为目标对象生成代理对象,默认如果对象实现过接口则采用java的动态代理机制;如果对象没有实现过接口则采用cglib动态代理。可以在配置文件中声明代理类型,强制使用。<aop:config proxy-targrt-class="true">
则无论如何都会采用cglib动态代理。
xml配置文件实现:
注解实现:
- 开启注解配置方式
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
- 指定切面类
- 编写通知方法,配置通知
<aop:config proxy-target-class="true">
<aop:pointcut expression="" id="p01"/>
<aop:aspect ref="AspectDemo">
<aop:before method="before" point-cut="p01">
<aop:around method="around" point-cut="p01">
<aop:after-returning method="afterReturn" point-cut="p01">
<aop:after-throwing method="afterThrow" point-cut="p01">
<aop:after method="after" point-cut="p01">
</aop:aspect>
</aop:config>
@Component
@Aspect
public class AspectDemo{
@Before(excution())
public void before(){}
@Around(excution())
public void around(){}
@AfterReturning(excution())
public void afterReturn(){}
@AfterThrowing(excution())
public void afterThrow(){}
@After(excution())
public void after(){}
}
- 切入表达式重复时
@Pointcut("execution()")
public void mx(){}
@Before("mx()")
public void before(){}
- 后置通知和异常通知的返回值
有其他参数的时候,需要标明value