当前位置: 首页 > 科技观察

SpringAOP的通知AdviceAPI详细介绍和使用

时间:2023-03-12 12:33:51 科技观察

Advice生命周期每个Advice都是一个Bean。Advice实例可以在所有Advisors之间共享,或者对于每个Advisor对象都是唯一的。这对应于每个类或每个实例的建议。最常用的是每种类型的通知。它适用于一般建议,例如交易顾问。这些不依赖于代理对象的状态或添加新状态。他们只处理方法和参数。Per-instanceAdvice适用于导入以支持mixins。在这种情况下,通知将状态添加到代理对象。您可以在同一个AOP代理中混合共享建议和每个实例的建议。通知类型Spring提供了多种通知类型,并且可以扩展以支持任意通知类型。拦截周围通知Spring中最基本的通知类型是拦截周围通知。Spring与AOPAlliance接口兼容,并支持使用方法拦截的环绕通知。实现MethodInterceptor和aroundadvice的类也应该实现以下接口:调用,连接点、AOP代理和方法的目标参数。invoke()方法应该返回调用的结果:连接点的返回值。上面的例子展示了一个简单的MethodInterceptor实现:对象rval=invocation.proceed();System.out.println("调用返回");返回值;}}注:在MethodInvocation中调用proceed()方法。这将沿着拦截器链向下到达连接点。大多数拦截器调用此方法并返回其返回值。但是,MethodInterceptor与任何环绕通知一样,可以返回不同的值或抛出异常,而不是调用proceed方法。但是,如果没有充分的理由,您不会想这样做。MethodInterceptor实现提供与遵循AOP联盟的其他AOP实现的互操作性。虽然使用最具体的通知类型有好处,但如果您可能想在另一个AOP框架中运行方面,请坚持使用MethodInterceptor。请注意,切入点目前不能在框架之间互操作,并且AOP联盟目前没有定义切入点接口。AdvanceAdvice一种简单的Advice类型是AdvanceAdivce。它不需要MethodInvocation对象,因为它只在进入方法之前被调用。beforeadvice的主要优点是不需要调用proceed()方法,因此不可能无意中导致拦截器链继续失败。publicinterfaceMethodBeforeAdviceextendsBeforeAdvice{voidbefore(Methodm,Object[]args,Objecttarget)throwsThrowable;}注:返回类型为void。Beforeadvice可以在连接点运行之前插入自定义行为,但不能改变返回值。如果之前的通知抛出异常,它会停止进一步执行拦截器链。异常向上传播到拦截器链。如果未选中或在被调用方法的签名上,它将直接传递给客户端。否则,它将被AOP代理包装在未经检查的异常中。以下示例显示了Spring中的前通知,它计算所有方法调用:publicclassCountingBeforeAdviceimplementsMethodBeforeAdvice{publicvoidbefore(Methodm,Object[]args,Objecttarget)throwsThrowable{++count;}publicintgetCount(){返回计数;}}异常通知如果连接点抛出异常,则在连接点返回后调用Throws通知。Spring提供类型化的异常通知。请注意,这意味着org.springframework.aop.ThrowsAdvice接口不包含任何方法。它是一个标记接口,标识给定对象实现了一个或多个类型化的抛出通知方法。格式如下:afterThrowing([Method,args,target],subclassOfThrowable)Method,args,target3个参数可选。publicclassBusinessThrowsAdviceimplementsThrowsAdvice{publicvoidafterThrowing(BusinessExceptionex)throwsThrowable{//...}}下一个示例声明了4个参数,因此它可以访问被调用的方法、方法参数和目标对象。如果抛出ServletException,将调用以下Advice:异常建议类PublicstaticclassCombinedThrowsAdviceimplementsThrowsAdvice{publicvoidafterThrowing(BusinessExceptionex)throwsThrowable{//...}publicvoidafterThrowing(Methodm,Object[]args,Objecttarget,MethodArgumentNotValidExceptionex){//...}}Post-adviceSpring中的Post-advice必须实现org.springframework.aop.AfterReturningAdvice接口,如下:抛出可抛出的;返回通知可以访问返回值(不能修改)、被调用的方法、方法的参数和目标。公共类CountingAfterReturningAdvice实现AfterReturningAdvice{privateintcount;publicvoidafterReturning(ObjectreturnValue,Methodm,Object[]args,Objecttarget)throwsThrowable{++count;}publicintgetCount(){返回计数;}}如果抛出异常,就会向上抛出拦截器链,而不是返回值。推荐通知Spring将推荐通知视为一种特殊的拦截通知。Introduction需要一个IntroductionAdvisor和一个IntroductionInterceptor来实现以下接口:publicinterfaceIntroductionInterceptorextendsMethodInterceptor{booleanimplementsInterface(Classintf);}从AOPAlliance方法拦截器接口继承的invoke()方法必须实现introduction。也就是说,如果调用的方法在导入的接口上,则导入拦截器负责处理方法调用——它不能调用proceed()。介绍性建议不能与任何切入点一起使用,因为它只适用于类级别,而不适用于方法级别。只能在IntroductionAdvisor中使用introductionadvice,它有以下方法:publicinterfaceIntroductionAdvisorextendsAdvisor,IntroductionInfo{ClassFiltergetClassFilter();voidvalidateInterfaces()抛出IllegalArgumentException;}publicinterfaceIntroductionInfo{Class[]getInterfaces();没有MethodMatcher,因此没有与referrer关联的切入点。仅类过滤。getInterfaces()方法返回此Advisor引入的接口。内部使用validateInterfaces()方法查看引入的接口是否可以被配置的IntroductionInterceptor实现。下面直接给出一个例子。这个例子的作用是在某个类没有接口能力的时候,动态的赋予接口能力给它:classCustomIntroductionInterceptorimplementsIntroductionInterceptor,CountDAO{@Overridepublicvoidcount(){System.out.println("OrderStatistics...");}@OverridepublicbooleanimplementsInterface(Classintf){returnCountDAO.class.isAssignableFrom(intf);}@OverridepublicObjectinvoke(MethodInvocationinvocation)throwsThrowable{if(implementsInterface(invocation.getMethod().getDeclaringClass())){System.out.println("我是Introductionenhancement..."+"Class:"+invocation.getMethod().getDeclaringClass()+",方法:"+invocation.getMethod().getName());//实际调用的是当前Advice方法实现的CountDAO#count。返回invocation.getMethod().invoke(this,invocation.getArguments());}返回调用.proceed();}}创建一个代理处理器:@ComponentpublicclassOrderProxyCreatorextendsAbstractAutoProxyCreator{@OverrideprotectedObject[]getAdvicesAndAdvisorsForBean(ClassbeanClass,StringbeanName,TargetSourcecustomTargetSource)throwsBeansException{returnnewObject[]{newDefaultIntroductionAdvisor(newCustomIntroductionInterceptor()),CountDAO.class)};}//判断只要不是OrderDAO类型的就跳过(这里只代理OrderDAO类型的Bean)@OverrideprotectedbooleanshouldSkip(ClassbeanClass,StringbeanName){return!OrderDAO.class.isAssignableFrom(beanClass);}}OrderDAO实现,DAO不实现CountDAO:@ServicepublicclassOrderDAOImplimplementsOrderDAO{@Overridepublicvoidsave(){System.out.println("Saveorder...");}@Overridepublicvoidquery(){System.out.println("查询顺序...");}}测试:AnnotationConfigApplicationContextctx=newAnnotationConfigApplicationContext("com.pack.aop");ctx.registerShutdownHook();OrderDAOpersondao=ctx.getBean(OrderDAO.class);persondao.save();Objectobj=ctx.getBean("orderDAOImpl");if(objinstanceofCountDAO){CountDAOcdao=(CountDAO)obj;cdao.count();}运行结果:保存订单...我是IntroductionEnhancement...类:接口com.pack.aop.CountDAO,方法:countfromrunning原来OrderDAO有CountDAO接口的能力,而CountDAO的具体实现是在我们的介绍拦截器上实现的

最新推荐
猜你喜欢