在开发过程种,很多地方会用到Spring的AOP ,之前一致使用
<aop:aspect ref='eventMethodInterceptor'>
<aop:before method='beforeMethod' pointcut-ref='prAspect'>
</aop:aspect>
,偶然间发现居然还有这种写法
<aop:advisor pointcut-ref='prAspect' advice-ref='eventMethodInterceptor' />
,顿时发现自己是多么无知,于是就赶快学习一下,记录一下,以供温故知新吧。
再次重复一下使用aop的目的,就是不改变源码的前提下,往一个方法的前后插入一个代码块
两者简单区别:
1、Adivisor是一种特殊的Aspect,Advisor代表spring中的Aspect
2、区别:advisor只持有一个Pointcut和一个advice,而aspect可以多个pointcut和多个advice
3、实现方式不同
< aop:aspect>定义切面时,只需要定义一般的bean就行,
< aop:advisor>中引用的通知时,通知必须实现MethodInterceptor接口
execution表达式规则:
* 只能匹配一级路径
.. 可以匹配多级,可以是包路径,也可以匹配多个参数
+ 只能放在类后面,表明本类及所有子类
//表示任意返回值,com.smallkey.dynamicProxy包及子包下的任意类,任意方法,任意参数
"execution(* com.smallkey.dynamicProxy..*.*(..))"
首先,看一下< aop:advisor>的实现方式:
我们定义一个目标类,代码如下:
//接口
public interface CustomerService {
//保存
public void save();
//查询
public int find();
}
// 实现类
@Service(value = "customerService")
public class CustomerServiceImpl implements CustomerService {
@Override
public void save() {
//int a = 1/0; //测试异常通知
System.out.println("客户保存了。。。。。");
}
@Override
public int find() {
System.out.println("客户查询数量了。。。。。");
return 100;
}
}
定义通知:
//定义通知(jdk的默认代理方式)
public class EventMethodInterceptor implements MethodInterceptor{
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("<aop:advisor advice-ref='' pointcut-ref=''>方式的通知");
// 最后必须返回methodInvocation.proceed()执行目标方法
return methodInvocation.proceed();
}
}
//aop配置
<!--1、装配目标对象到ioc容器-->
<bean id="customerService" class="com.smallkey.dynamicProxy.jdk.CustomerServiceImpl"/>
//把通知配置到Spring容器中
<bean id="EatHelper" class="com.ghs.aop.EatHelper"></bean>
<!--2、装配通知对象-->
<bean id="EventMethodInterceptor" class="com.smallkey.aopAdvice.EventMethodInterceptor"/>
<aop:config>
<!--配置切点-->
<aop:pointcut id="myPointcut" expression="execution(* com.smallkey.dynamicProxy..*.*(..))"/>
<!--配置通知和切点,进行织入-->
<aop:advisor advice-ref="EventMethodInterceptor" pointcut-ref="myPointcut"/>
</aop:config>
下面是< aop:aspect>的实现方式:
// 定义目标类
@Service(value = "customerService")
public class CustomerServiceImpl implements CustomerService {
@Override
public void save() {
//int a = 1/0; //测试异常通知
System.out.println("客户保存了。。。。。");
}
@Override
public int find() {
System.out.println("客户查询数量了。。。。。");
return 100;
}
}
//定义通知
@Component(value = "myAspectAdvice")
@Aspect
public class MyAspectAdvice {
//参数1:连接点对象(方法的包装对象:方法,参数,目标对象)
@Before("bean(*Service)")
public void before(JoinPoint joinPoint) {
System.out.println("方法名称" + joinPoint.getSignature().getName());
System.out.println("目标对象" + joinPoint.getTarget().getClass().getName());
System.out.println("代理对象" + joinPoint.getThis().getClass().getName());
//判断是否是save方法
if ("save".equals(joinPoint.getSignature().getName())) {
System.out.println("前置通知~~~~");
}
}
//参数1:连接点对象(方法的包装对象:方法,参数,目标对象)
//参数2:目标方法执行后的返回值,类型是object,“参数名”随便,但也不能太随便,一会要配置
@AfterReturning(value = "bean(*Service)",returning = "returnVal")
public void afterReturning(JoinPoint joinPoint, Object returnVal) {
System.out.println("后置通知:" + "小伙子您刚才调用的" + joinPoint.getSignature().getName() + "的返回值是" + returnVal);
}
//应用场景:日志、缓存、权限、性能监控、事务管理
//环绕通知:在目标方法的执行前后均可增强
//参数:ProceedingJointPoint可执行的连接点对象,特点是调用proceed()方法
//可以随时随地执行目标对象的方法
//必须抛出Throwable
@Around(value = "bean(*Service)")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println(" 开启事务 ");
Object resultObject = proceedingJoinPoint.proceed();
System.out.println(" 关闭事务 ");
return resultObject;
}
//当方法真正发生异常时才会增强方法
//参数1:静态连接点(方法对象)
//参数2:目标方法抛出的异常,参数名随便,但也不能太随便
@AfterThrowing(value = "bean(*Service)",throwing = "throwable")
public void afterThrowing(JoinPoint joinPoint, Throwable throwable) {
System.out.println(
joinPoint.getTarget().getClass().getName() + "类的"
+ joinPoint.getSignature().getName()
+ "方法发生异常,异常为" + throwable.getMessage()
);
}
//不管目标方法是否发生异常,最终通知都会执行(类似于finally代码功能)
//应用场景:释放资源 (关闭文件、 关闭数据库连接、 网络连接、 释放内存对象
@After(value = "bean(*Service)")
public void after(JoinPoint joinPoint) {
System.out.println("数据库的connection被释放了。。。。。,执行的方法是:"+
joinPoint.getSignature().getName()
);
}
}
//aop配置
<!--1、装配目标对象到ioc容器-->
<bean id="customerService" class="com.smallkey.dynamicProxy.jdk.CustomerServiceImpl"/>
<bean id="cglibService" class="com.smallkey.dynamicProxy.cglib.CglibService"/>
<!--2、装配通知(增强)对象-->
<bean id="myAspectAdvice" class="com.smallkey.aopAdvice.MyAspectAdvice"/>
<!--3、配置切点和切面,进行织入-->
<aop:config>
<!--配置切点-->
<aop:pointcut id="myPointcut" expression="execution(* com.smallkey.dynamicProxy..*.*(..))"/>
<!--配置切面-->
<aop:aspect ref="myAspectAdvice">
<!--<aop:before method="before" pointcut-ref="myPointcut"/>-->
<!--<aop:after-returning method="afterReturning" returning="returnVal" pointcut-ref="myPointcut"/>-->
<!--<aop:around method="around" pointcut-ref="myPointcut"/>-->
<!--<aop:after-throwing method="afterThrowing" throwing="throwable" pointcut-ref="myPointcut"/>-->
<aop:after method="after" pointcut-ref="myPointcut"/>
</aop:aspect>
</aop:config>
其实除了xml方法,更多的使用注解的方法,让配置更简单
//通知对象、目标方法同上
//配置AOP
<!--只需要在applicationContent.xml中配置如下配置,在通知对象上配置@Aspect,在前置、环绕、后置方法上加@Before、@Around、@After,另外在切点方法返回后执行的方法加上@AfterReturning, 切点方法抛异常执行的方法加上@AfterThrowing-->
<!--1、开启注解包扫描-->
<context:component-scan base-package="com.smallkey"/>
<!--2、开启AspectJ注解自动扫描-->
<aop:aspectj-autoproxy/>