Spring方法攔截器MethodInterceptor
前言
實(shí)現(xiàn)MethodInterceptor 接口,在調(diào)用目標(biāo)對(duì)象的方法時(shí),就可以實(shí)現(xiàn)在調(diào)用方法之前、調(diào)用方法過程中、調(diào)用方法之后對(duì)其進(jìn)行控制。
MethodInterceptor 接口可以實(shí)現(xiàn)MethodBeforeAdvice接口、AfterReturningAdvice接口、ThrowsAdvice接口這三個(gè)接口能夠所能夠?qū)崿F(xiàn)的功能,但是應(yīng)該謹(jǐn)慎使用MethodInterceptor 接口,很可能因?yàn)橐粫r(shí)的疏忽忘記最重要的MethodInvocation而造成對(duì)目標(biāo)對(duì)象方法調(diào)用失效,或者不能達(dá)到預(yù)期的設(shè)想。
示例代碼如下
public class TestMethodInterceptor { public static void main(String[] args) { ProxyFactory proxyFactory=new ProxyFactory(); proxyFactory.setTarget(new TestMethodInterceptor()); proxyFactory.addAdvice(new adviseMethodInterceptor()); Object proxy = proxyFactory.getProxy(); TestMethodInterceptor methodInterceptor = (TestMethodInterceptor) proxy; methodInterceptor.doSomeThing("通過代理工廠設(shè)置代理對(duì)象,攔截代理方法"); } public static class adviseMethodInterceptor implements MethodInterceptor{ @Override public Object invoke(MethodInvocation methodInvocation) throws Throwable { Object result=null; try{ System.out.println("方法執(zhí)行之前:"+methodInvocation.getMethod().toString()); result= methodInvocation.proceed(); System.out.println("方法執(zhí)行之后:"+methodInvocation.getMethod().toString()); System.out.println("方法正常運(yùn)行結(jié)果:"+result); return result; }catch (Exception e){ System.out.println("方法出現(xiàn)異常:"+e.toString()); System.out.println("方法運(yùn)行Exception結(jié)果:"+result); return result; } } } public String doSomeThing(String someThing){ //int i=5/0; return "執(zhí)行被攔截的方法:"+someThing; } }
正常運(yùn)行結(jié)果:
方法執(zhí)行之前:public java.lang.String com.blog.test.aop.TestMethodInterceptor.doSomeThing(java.lang.String)
方法執(zhí)行之后:public java.lang.String com.blog.test.aop.TestMethodInterceptor.doSomeThing(java.lang.String)
方法正常運(yùn)行結(jié)果:執(zhí)行被攔截的方法:通過代理工廠設(shè)置代理對(duì)象,攔截代理方法
異常運(yùn)行結(jié)果:
方法執(zhí)行之前:public java.lang.String com.blog.test.aop.TestMethodInterceptor.doSomeThing(java.lang.String)
方法出現(xiàn)異常:java.lang.ArithmeticException: / by zero
方法運(yùn)行Exception結(jié)果:null
Spring攔截器實(shí)現(xiàn)+后臺(tái)原理(MethodInterceptor)
MethodInterceptor
MethodInterceptor是AOP項(xiàng)目中的攔截器(注:不是動(dòng)態(tài)代理攔截器),區(qū)別與HandlerInterceptor攔截目標(biāo)時(shí)請(qǐng)求,它攔截的目標(biāo)是方法。
實(shí)現(xiàn)MethodInterceptor攔截器大致也分為兩種:
(1)MethodInterceptor接口;
(2)利用AspectJ的注解配置;
MethodInterceptor接口
import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; public class MethodInvokeInterceptor implements MethodInterceptor { @Override public Object invoke(MethodInvocation methodInvocation) throws Throwable { System.out.println("before method invoke...."); Object object = methodInvocation.proceed(); System.out.println("after method invoke....."); return object; } }
<!-- 攔截器 demo --> <bean id="methodInvokeInterceptor" class="com.paic.phssp.springtest.interceptor.method.MethodInvokeInterceptor"/> <aop:config> <!--切入點(diǎn),controlller --> <aop:pointcut id="pointcut_test" expression="execution(* com.paic.phssp.springtest.controller..*.*(..))" /> <!--在該切入點(diǎn)使用自定義攔截器 ,按照先后順序執(zhí)行 --> <aop:advisor pointcut-ref="pointcut_test" advice-ref="methodInvokeInterceptor" /> </aop:config> <!-- 自動(dòng)掃描使用了aspectj注解的類 --> <aop:aspectj-autoproxy/>
執(zhí)行:
AspectJ的注解
import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.stereotype.Component; @Aspect @Component public class AutoAspectJInterceptor { @Around("execution (* com.paic.phssp.springtest.controller..*.*(..))") public Object around(ProceedingJoinPoint point) throws Throwable{ System.out.println("AutoAspectJInterceptor begin around......"); Object object = point.proceed(); System.out.println("AutoAspectJInterceptor end around......"); return object; } }
運(yùn)行結(jié)果:
AutoAspectJInterceptor begin around......
>>>>:isAuthenticated=false
AutoAspectJInterceptor end around......
簡(jiǎn)單介紹下關(guān)鍵詞
-
AOP
=Aspect Oriented Program
面向切面(方面/剖面)編程 -
Advice
(通知):把各組件中公共業(yè)務(wù)邏輯抽離出來作為一個(gè)獨(dú)立 的組件 -
Weave
(織入):把抽離出來的組件(Advice),使用到需要使用該邏輯 地方的過程。 -
JoinPoint
(連接點(diǎn)): Advice 組件可以weave的特征點(diǎn)。 -
PointCut
(切入點(diǎn)):用來明確Advice需要織入的連接點(diǎn) -
Aspect
(切面):Aspect=Advice + PointCut
通知類型
-
@Before
在切點(diǎn)方法之前執(zhí)行 -
@After
在切點(diǎn)方法之后執(zhí)行 -
@AfterReturning
切點(diǎn)方法返回后執(zhí)行 -
@AfterThrowing
切點(diǎn)方法拋異常執(zhí)行 -
@Around
環(huán)繞通知
執(zhí)行順序:
-
@Around
環(huán)繞通知 -
@Before
通知執(zhí)行 -
@Before
通知執(zhí)行結(jié)束 -
@Around
環(huán)繞通知執(zhí)行結(jié)束 -
@After
后置通知執(zhí)行了! -
@AfterReturning
切面設(shè)置
可以使用&&、||、!、三種運(yùn)算符來組合切點(diǎn)表達(dá)式
execution表達(dá)式
"execution(public * com.xhx.springboot.controller.*.*(..))"
- *只能匹配一級(jí)路徑
- ..可以匹配多級(jí),可以是包路徑,也可以匹配多個(gè)參數(shù)
- + 只能放在類后面,表明本類及所有子類
within(類路徑) 配置指定類型的類實(shí)例,同樣可以使用匹配符
within(com.xhx.springboot..*)
@within(annotationType) 匹配帶有指定注解的類(注:與上不同)
"@within(org.springframework.stereotype.Component)"
@annotation(annotationType) 匹配帶有指定注解的方法
"@annotation(IDataSource)"
其中:IDataSource為自定義注解
import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface IDataSource { String value() default "dataSource"; }
下面分析下Spring @Aspect
1、注冊(cè)
org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator
看到實(shí)現(xiàn)接口BeanPostProcessor,必然在初始化Bean前后,執(zhí)行接口方法。
2、解析
AspectJAutoProxyBeanDefinitionParser.java#parse()方法
@Nullable public BeanDefinition parse(Element element, ParserContext parserContext) { AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element); this.extendBeanDefinition(element, parserContext); return null; }
public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(ParserContext parserContext, Element sourceElement) { BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext.getRegistry(), parserContext.extractSource(sourceElement)); useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement); registerComponentIfNecessary(beanDefinition, parserContext); }
@Nullable public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, @Nullable Object source) { return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source); }
3、具體實(shí)現(xiàn)
上面提到實(shí)現(xiàn)接口BeanPostProcessor,必然在初始化Bean前后,執(zhí)行接口方法。看下面時(shí)序圖:
AbstractAutoProxyCreator的postProcessAfterInitialization()方法
DefaultAopProxyFactory.createAopProxy()方法,具體創(chuàng)建代理類。兩種動(dòng)態(tài)代理:JDK動(dòng)態(tài)代理和CGLIB代理。
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { if (!config.isOptimize() && !config.isProxyTargetClass() && !this.hasNoUserSuppliedProxyInterfaces(config)) { return new JdkDynamicAopProxy(config); } else { Class<?> targetClass = config.getTargetClass(); if (targetClass == null) { throw new AopConfigException("TargetSource cannot determine target class: Either an interface or a target is required for proxy creation."); } else { return (AopProxy)(!targetClass.isInterface() && !Proxy.isProxyClass(targetClass) ? new ObjenesisCglibAopProxy(config) : new JdkDynamicAopProxy(config)); } } }
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持服務(wù)器之家。
原文鏈接:https://blog.csdn.net/u012834750/article/details/71773887