使用Spring方法攔截器MethodInterceptor
Spring方法攔截器MethodInterceptor
前言
實(shí)現(xiàn)MethodInterceptor 接口,在調(diào)用目標(biāo)對象的方法時,就可以實(shí)現(xiàn)在調(diào)用方法之前、調(diào)用方法過程中、調(diào)用方法之后對其進(jìn)行控制。
MethodInterceptor 接口可以實(shí)現(xiàn)MethodBeforeAdvice接口、AfterReturningAdvice接口、ThrowsAdvice接口這三個接口能夠所能夠?qū)崿F(xiàn)的功能,但是應(yīng)該謹(jǐn)慎使用MethodInterceptor 接口,很可能因?yàn)橐粫r的疏忽忘記最重要的MethodInvocation而造成對目標(biāo)對象方法調(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è)置代理對象,攔截代理方法");
}
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è)置代理對象,攔截代理方法
異常運(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)+后臺原理(MethodInterceptor)
MethodInterceptor
MethodInterceptor是AOP項(xiàng)目中的攔截器(注:不是動態(tài)代理攔截器),區(qū)別與HandlerInterceptor攔截目標(biāo)時請求,它攔截的目標(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>
<!-- 自動掃描使用了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......
簡單介紹下關(guān)鍵詞
AOP=Aspect Oriented Program面向切面(方面/剖面)編程Advice(通知):把各組件中公共業(yè)務(wù)邏輯抽離出來作為一個獨(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.*.*(..))"
- *只能匹配一級路徑
- ..可以匹配多級,可以是包路徑,也可以匹配多個參數(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、注冊
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í)行接口方法??聪旅鏁r序圖:
AbstractAutoProxyCreator的postProcessAfterInitialization()方法

DefaultAopProxyFactory.createAopProxy()方法,具體創(chuàng)建代理類。兩種動態(tài)代理:JDK動態(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));
}
}
}
以上為個人經(jīng)驗(yàn),希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
基于springboot和redis實(shí)現(xiàn)單點(diǎn)登錄
這篇文章主要為大家詳細(xì)介紹了基于springboot和redis實(shí)現(xiàn)單點(diǎn)登錄,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-06-06
JFreeChart實(shí)現(xiàn)實(shí)時曲線圖
這篇文章主要為大家詳細(xì)介紹了JFreeChart實(shí)現(xiàn)實(shí)時曲線圖的方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-06-06
Java設(shè)計模式之共享模式/享元模式(Flyweight模式)介紹
這篇文章主要介紹了Java設(shè)計模式之共享模式/享元模式(Flyweight模式)介紹,本文講解了為什么使用共享模式/享元模式、如何使用共享模式/享元模式、Flyweight模式在XML等數(shù)據(jù)源中應(yīng)用等內(nèi)容,需要的朋友可以參考下2015-03-03
Java虛擬機(jī)JVM性能優(yōu)化(二):編譯器
這篇文章主要介紹了Java虛擬機(jī)JVM性能優(yōu)化(二):編譯器,本文先是講解了不同種類的編譯器,并對客戶端編譯,服務(wù)器端編譯器和多層編譯的運(yùn)行性能進(jìn)行了對比,然后給出了幾種常見的JVM優(yōu)化方法,需要的朋友可以參考下2014-09-09
spring mail借助qq郵箱服務(wù)器發(fā)送郵件
這篇文章主要介紹了spring mail借助qq郵箱服務(wù)器發(fā)送郵件的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-12-12
Spring中XML schema擴(kuò)展機(jī)制的深入講解
這篇文章主要給大家介紹了關(guān)于Spring中XML schema擴(kuò)展機(jī)制的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-09-09
springboot如何使用assembly打包項(xiàng)目和啟動腳本
這篇文章主要介紹了springboot如何使用assembly打包項(xiàng)目和啟動腳本問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-06-06
使用JAVA8 filter對List多條件篩選的實(shí)現(xiàn)
這篇文章主要介紹了使用JAVA8 filter對List多條件篩選的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03
Java中的CopyOnWriteArrayList原理詳解
這篇文章主要介紹了Java中的CopyOnWriteArrayList原理詳解,如源碼所示,CopyOnWriteArrayList和ArrayList一樣,都在內(nèi)部維護(hù)了一個數(shù)組,操作CopyOnWriteArrayList其實(shí)就是在操作內(nèi)部的數(shù)組,需要的朋友可以參考下2023-12-12

