创建通知
Spring中通知的类型:前置通知Before、后置通知After、环绕通知Around、异常通知Throws、引入通知
Before:接口是org.springframework.aop.BeforeAdvice,在目标方法被调用之前调用
After:接口是org.springframework.aop.AfterReturningAdvice,在目标方法被调用之后调用
Around:接口是org.aopalliance.intercept.MethodInterceptor,拦截对目标对象方法的调用
Throws:接口是:org.springframework.aop.ThrowsAdvice,在目标方法抛出异常时调用
引入通知:给目标对象添加新的方法(以及属性),而其他通知是在目标对象的方法被调用的时候织入。
切入点
定义切入点
Spring根据需要织入通知的类和方法来定义切入点,通知是根据其特性织入目标类和方法。Spring的切入点框架的核心接口是Pointcut。
接口MethodMatcher有三个方法,用在代理对象生命周期的特定时期。
1、 matches(Method,class)方法根据目标类和方法决定一个方法是否被通知,在Aop代理被创建时调用一次,其结果决定通知是否被织入;
2、 若matches(Method,class)返回true,isRuntime()被调用来决定MethodMatcher的类型:静态和动态。静态切入点通知总被执行,如果一个切入点是静态的,isRuntime()方法应返回false;动态切入点根据运行时方法的参数值来决定通知是否执行,若一个切入点是动态的IsRuntime()方法返回true,在代理类创建时调用一次。
3、 如果一个切点是静态的额,matches(Method,Class,Object[])方法永远不会被调用。对于动态切入点,目标对象方法每次被调用时,matches(Method.Class,Object[])方法被调用。
理解Advisor
Advisor是把通知和切入点组合到一个对象中,接口为PointcutAdvisor。
静态切入点
如创建自制的静态切入点继承类StaticMethodMatcherPointcut
常用的静态切入点:NameMatchMethodPointcut和JdkPegexpMethodPointcut
动态切入点
内置ControlFlowPointcut
切入点实施
为了在应用系统中重复使用切入点,进行合并与交叉操作,Spring提供了如下两个类:
1、 ComposablePointcut:通过将已有的ComposablePointcut、切入点、MethodMatcher及classFilter对象进行合并与交叉,组装成一个新的ComposablePointcut对象,调用其实例的intersection()或union方法实现
2、 Pointcuts:可对两个pointcut对象进行合并
综合案例:
创建一个EmpService的接口:
public interface EmpService {
void work(String name);
void sleep(Integer hours);
}
创建一个实现EmpService接口的类EmpserviceImpl
public class EmpServiceImpl implements EmpService {
private String time;
@Override
public void work(String name) {
System.out.println(name+"的工作时间是:" + time);
}
@Override
public void sleep(Integer hours) {
System.out.println("工作时间是:"+ time+"---休息时间是:" + hours);
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
}
创建一个ModifyDate的接口
public interface ModifyDate {
public void setMDate(Date MDate);
public Date getMDate();
}
创建一个实现ModifyDate接口的类ModifyDateImpl
public class ModifyDateImpl extends DelegatingIntroductionInterceptor implements ModifyDate {
/**
* 引入通知
*/
private Date MDate;
@Override
public Date getMDate() {
return MDate;
}
@Override
public void setMDate(Date MDate) {
}
}
/**前置通知*/
public class MyBeforeAdvice implements MethodBeforeAdvice{
/**第一个参数是:方法、第二个参数是:方法的参数、第三个参数是:目标对象*/
@Override
public void before(Method arg0, Object[] arg1, Object arg2)
throws Throwable {
System.out.println("方法的名称是:"+arg0);
System.out.println("方法的参数"+arg1.length+arg1[0]);
System.out.println("目标对象"+arg2);
}
}
/**环绕通知(方法执行前后)*/
public class MyAroundAdvice implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("around start。。。。。");
Object obj = invocation.proceed();// 让它去执行目标方法
//注:proceed()方法会调用目标方法
System.out.println("around end。。。。。");
return obj;
}
}
/**后置通知(方法执行之后)*/
public class MyAfterAdvice implements AfterReturningAdvice {
@Override
public void afterReturning(Object arg0, Method arg1, Object[] arg2,
Object arg3) throws Throwable {
System.out.println("方法的返回类型:"+arg0);
System.out.println("方法的名称"+arg1+"方法的参数:"+arg2.length+
"目标对象:"+arg3.getClass().getName());
}
}
/**异常通知(方法产生bug)*/
public class MyException implements ThrowsAdvice{
/**写方法的时候一定参考API中的方法样式 不能够随意写*/
public void afterThrowing(Method method, Object[] args, Object target, Exception ex){
System.out.println("方法的名称:"+method+"方法的参数"+args.length+"目标对象:"+target.getClass().getName()+"异常"+ex.getMessage());
}
}
Xml文件
<!-- 前置通知(方法操作之前进行处理) -->
<bean id="myBeforeAdvice" class="cn.csdn.aop.advice.MyBeforeAdvice" scope="singleton" />
<!-- 后置通知(方法的操作之后进行处理) -->
<bean id="myAfterAdvice" class="cn.csdn.aop.advice.MyAfterAdvice" scope="singleton"/>
<!-- 环绕通知(方法执行前后处理) -->
<bean id="myAroundAdvice" class="cn.csdn.aop.advice.MyAroundAdvice" scope="singleton"/>
<!-- 异常通知(方法执行期间出现bug的时候) -->
<bean id="myException" class="cn.csdn.aop.advice.MyException" scope="singleton"/>
<!-- 引入通知 -->
<bean id="modifyDateImpl" class="cn.csdn.service.ModifyDateImpl"></bean