AOP优势:
* 程序运行期间,不修改源码对已有方法的增强;在实际的业务开发过程中,能对业务逻辑各个部分进行隔离,就使得业务逻辑各部分耦合程度降低,提高了程序的可重用性,提高了开发效率。
AOP 中相关术语:
* JointPoint(连接点) --- 通俗解释:业务层的方法都可称之连接点;
* PointCut(切入点) --- 对那些拦截JointPoint的定义;(即被增强的方法);
* Advice(通知/增强) --- 对拦截到的JointPoint要做的事情就是通知;
分为前置通知,后置通知,异常通知,最终通知,环绕通知;
* introduction(引介) --- 暂不讨论;
* Target(目标对象) -- 举例说明(AccountService);
* weaving(织入) --- 把增强运用到目标对象来创建新代理对象的过程 ;
(Spring --- 动态代理织入 AspectJ --- 编译期+装载类期间织入)
* proxy(代理) --- 即目标对象产生的代理对象;
* Aspect(切面) --- PointCut(切入点)与通知(advice)或引介(introduction) 的结合;
XML配置实现AOP:
* 引入相关jar包即约束,配置spring bean 代理类(Service)以及自己定义的通知类(logger)
//配置需要代理实体类
<Bean id="accountService" class="com.spring.demo.impl.AccountServiceImpl">
//配置日志类
<Bean id="logger" class="com.spring.demo.util.Logger">
* 开启AOP配置 内部使相对应的标签来配置通知类型
注意:切入点表达式 execution(表达式)
表达式: 访问修饰符 返回值类型 包名.包名.包名...类名.方法名(参数列表)
e.g. public void com.spring.demo.impl.AccountServiceImpl.transfer(int i)
可用全局通配符表示 * *..*.*(..)
实际业务开发流程中 * com.spring.demo.impl.*.*(..)
//开启aop配置
<aop:config>
//配置切面 id 给切面提供唯一标识 ref 指定通知类的beanID
<aop:aspect id="logAdvice" ref="logger">
//内部使用相对应标签来配置通知类型
// method 用于指定通知类中的什么方法
//pointCut 用于指定切入点表达式
//前置通知
<aop:before method="beforPrintLog()" pointCut="excution(* com.spring.demo.impl.*.*(..))">
//后置通知
<aop:after-returning method="afterReturnPrintLog()" pointCut="excution(* com.spring.demo.impl.*.*(..))">
//异常通知
<aop:after-throwing method="afterThrowingPrintLog()" pointCut="excution(* com.spring.demo.impl.*.*(..))">
//最终通知
<aop:after method="AfterprintLog()" pointCut="excution(* com.spring.demo.impl.*.*(..))">
</aop:aspect>
</aop:config>
** 可配置切入点表达式简化上述每一个通知都配置切入点表达式
//开启aop配置
<aop:config>
//配置切面 id 给切面提供唯一标识 ref 指定通知类的beanID
//配置切入点表达式 id 用于指定表达式唯一的标识 expression 切入点表达式
<aop:pointCut id="pl" expression="excution(* com.spring.demo.impl.*.*(..))">
<aop:aspect id="logAdvice" ref="logger">
//内部使用相对应标签来配置通知类型
// method 用于指定通知类中的什么方法
//pointCut 用于指定切入点表达式
//前置通知
<aop:before method="beforPrintLog()" pointCut-ref="pl">
//后置通知
<aop:after-returning method="afterReturnPrintLog()" pointCut-ref="pl">
//异常通知
<aop:after-throwing method="afterThrowingPrintLog()" pointCut-ref="pl">
//最终通知
<aop:after method="AfterprintLog()" pointCut-ref="pl">
</aop:aspect>
</aop:config>
* 配置环绕通知:
//配置文件
<aop:config>
//配置切入点表达式 id 用于指定表达式唯一的标识 expression 切入点表达式
<aop:pointCut id="pl" expression="execution(* com.spring.demo.impl.*.*(..))">
<aop:aspect id="logAdvice" ref="logger">
//环绕通知
<aop:around method="aroundprintLog()" pointCut-ref="pl">
</aop:aspect>
</aop:config>
* 编写环绕通知方法
/**
* Spring中的环绕通知为我们提供的一种可以在代码中手动控制增强方法合适执行的的方式
*/
public void aroundPrintLog(ProceedinJoinPoint pjp){
Object value = null;
try{
Object[] args = pjp.getArgs(); //得到方法执行所需的参数
System.out.println("Logger中around方法开始记录日志---------前置");
value = pjp.proceed(args); //明确调用业务层方法(切入点方法)
System.out.println("Logger中around方法开始记录日志--------后置");
return value;
}catch(Throwable t){
System.out.println("Logger中around方法开始记录日志--------异常");
}finally{
System.out.println("Logger中around方法开始记录日志--------最终");
}
}
注解方式实现AOP
//开启注解扫描 用spring来创建我们的对象
<context:component-scan base-package="com.spring.demo"></<context:component-scan>
//配置spring开启注解的AOP支持
<context:aspectj-autoproxy></context:aspectj-autoproxy>
@Component("logger")
@Aspect //表示当前类是一个切面类
public class Logger(){
@Pointcut("execution(* com.spring.demo.impl.*.*(..))")
private void pl(){}
/**
* 前置通知
*/
@Before("pl()")
public void beforePrintLog(){System.out.println("前置通知Logger")};
/**
* 后置通知
*/
@AfterReturning("pl()")
public void afterReturningPrintLog(){System.out.println("后置通知Logger")};
/**
* 异常通知
*/
@AfterThrowing("pl()")
public void afterThrowingPrintLog(){System.out.println("异常通知Logger")};
/**
* 最终通知
*/
@After("pl()")
public void afterPrintLog(){System.out.println("最终通知Logger")};
}
注意:在spring使用注解的方式进行aop时,其方法调用顺序会出现问题,即最终通知先于后置通知执行,导致最后不能报错不能成功执行。
但换成环绕通知时候,按照顺序先后执行。
所以,一般实际开发中,还是自己定义环绕通知,再使用注解方式进行实现。程序能够完美运行。