Spring框架学习之SpringAOP(二)
AOP概念
AOP(Aspect-Oriented Programming,面向切面编程),AOP是OOP(面向对象编程)的补充和完善 AOP的核心思想就是“将应用程序中的商业逻辑同对其提供支持的通用服务进行分离”
AOP的概念和术语
切面(Aspect) 事务处理,日志
连接点(Joinpoint) 方法调用,异常处理
通知(Advice) around,before,拦截器
切入点(Pointcut)匹配连接点的表达式
引入(Introduction)增加方法或者字段
目标对象(Target Object) 代理对象
AOP代理(AOP Proxy) 框架产生的对象,包括Advice
织入(Weaving) Aspect连接到其他对象
AOP代理是AOP框架创建的对象。 Spring有两种代理方式:默认使用JDK动态代理实现AOP代理,主要用于代理接口;CGLIB代理,实现类的代理,而不是接口
SpringAOP管理
首先引入AOP命名空间
<beans xmlns=http://www.springframework.org/schema/beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
</beans>
Spring的切入点(joinpoint)
切入点是可以触发处理的连接点集合 同一个切入点可触发不同的处理 可以判断触发哪些处理,即哪些方法可以触发AOP代理。
切入点表达式
切入点指示符:如execution
布尔运算符:AND(&&)、OR(||)和NOT(!)
通配符:星号(*),用于匹配任何方法、类名或者参数类型。
双点号(..),用于表示0个或者多个参数类型。
方法可见性修饰符:如public的。
方法返回类型:如void、int、String等*表示所有类型。
类名:指定完整的类名 ,缩小到某个目标类。
方法名称:可以是全名。如get*,即所有名称以get开头的方法。
如:execution( public void com.biz.impl.PhoneBizImpl.*Phone(int) )表示的就是当public void com.biz.impl.PhoneBizImpl类下的*Phone(int)方法被调用执行时触发AOP代理类
Spring的切面Aspect
系统中抽象出来的的某一个系统服务功能模块,日志管理模块
用一个POJO 类来表示抽象的切面, 用方法表示通知,也可以说是切入点切入的类
在使用之前需要实例化该切面类
<bean id="logAspectBean" class=“….LogAspect"></bean>
Spring中存在5种通知(advice)
Before:在目标方法被调用之前调用(前置通知)
AfterReturning:在某连接点正常完成后执行的通知(后置通知)
After:当某连接点退出的时候执行的通知(最终通知)
Throws:当目标方法抛出异常时调用(异常通知)
Around:拦截对目标对象方法的调用(环绕通知)
通知方法获取目标类的方法使用joinpoint对象
public void before(JoinPoint jp) throws Throwable{
Object[] args = jp.getArgs();// 目标方法所有参数
String methodname=jp.getSignature().getName();//获取目标方法名称
if("buyPhone".equals(methodname)) {
System.out.println("日志:"+currentTime()+“ 即将执行进货操作,数量为 "+args[0]+" 部");
}
JoinPoint对象提供了如下方法以获得连接点
Object[] getArgs():返回方法参数
Signature getSignature():返回方法签名
getModifiers()方法可以得到方法修饰符。
String getKind():返回当前连接点的类型,
Object getTarget():返回连接点所在的目标对象。
Object getThis():返回AOP自动创建的代理对象
值得注意的是,在使用环绕通知的时候使用ProceedingJoinPoint对象pjp
使用pjp.proceed();执行后才能执行环绕通知
<aop:config>
<!-- 定义一个可以被多个切面共享的切入点 -->
<aop:pointcut id="p1" expression="execution( void *Phone(int))"/>
<!-- 定义一个切面 ,指向实例化的切面类bean的id-->
<aop:aspect id="logAspect" ref="logAspectBean">
<!-- 切面业务的方法名 ,要指向切入点-->
<aop:before method="before" pointcut-ref="p1"/>
<!-- 最终通知:目标业务发生异常,不影响最终通知业务执行 -->
<aop:after method="afterReturning" pointcut-ref="p1"/>
<!-- 后置通知:目标业务发生异常,后置业务不再执行 -->
<aop:after-returning method="log" pointcut-ref="p1"/>
<!-- 环绕通知:拦截目标方法的调用 -->
<aop:around method="around" pointcut-ref="p1"/>
<!-- 异常通知 -->
<aop:after-throwing method="exceptionThrowing" pointcut-ref="p1" throwing="e"/>
</aop:aspect>
</aop:config>
Spring注解式通知
<!-- 启用注解配置 -->
<aop:aspectj-autoproxy />
<!-- 目标业务对象 -->
<bean id="phoneBiz" class="s3spring.ch2.biz.impl.PhoneBizImpl"></bean>
<!-- 日志管理切面 -->
<bean class="s3spring.ch2.log.annotation.LogAspect"></bean>
@Aspect注解声明为切面
@Aspect
public class LogAspect {……}
为切面添加注解
@Before("execution( void *Phone(int))")
@AfterReturning("execution( void *Phone(int))")
@After("execution( void *Phone(int))")
@Around("execution( void *Phone(int))")
@AfterThrowing注解配置异常通知除了需要指定切入点外还需要根据方法参数名称绑定异常对象
@AfterThrowing(pointcut="execution( void *Phone(int))",throwing="e")