使用Spring方法拦截器MethodInterceptor

 更新时间:2021年10月26日 16:47:20   作者:淡淡的倔强  
这篇文章主要介绍了使用Spring方法拦截器MethodInterceptor,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

Spring方法拦截器MethodInterceptor

前言

实现MethodInterceptor 接口,在调用目标对象的方法时,就可以实现在调用方法之前、调用方法过程中、调用方法之后对其进行控制。

MethodInterceptor 接口可以实现MethodBeforeAdvice接口、AfterReturningAdvice接口、ThrowsAdvice接口这三个接口能够所能够实现的功能,但是应该谨慎使用MethodInterceptor 接口,很可能因为一时的疏忽忘记最重要的MethodInvocation而造成对目标对象方法调用失效,或者不能达到预期的设想。

示例代码如下

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("通过代理工厂设置代理对象,拦截代理方法");
    }
    public static class adviseMethodInterceptor implements MethodInterceptor{
        @Override
        public Object invoke(MethodInvocation methodInvocation) throws Throwable {
            Object result=null;
            try{
                System.out.println("方法执行之前:"+methodInvocation.getMethod().toString());
                result= methodInvocation.proceed();
                System.out.println("方法执行之后:"+methodInvocation.getMethod().toString());
                System.out.println("方法正常运行结果:"+result);
                return result;
            }catch (Exception e){
                System.out.println("方法出现异常:"+e.toString());
                System.out.println("方法运行Exception结果:"+result);
                return result;
            }
        }
    }
    public String doSomeThing(String someThing){
        //int i=5/0;
        return "执行被拦截的方法:"+someThing;
    }
}

正常运行结果:

方法执行之前:public java.lang.String com.blog.test.aop.TestMethodInterceptor.doSomeThing(java.lang.String)

方法执行之后:public java.lang.String com.blog.test.aop.TestMethodInterceptor.doSomeThing(java.lang.String)

方法正常运行结果:执行被拦截的方法:通过代理工厂设置代理对象,拦截代理方法

异常运行结果:

方法执行之前:public java.lang.String com.blog.test.aop.TestMethodInterceptor.doSomeThing(java.lang.String)

方法出现异常:java.lang.ArithmeticException: / by zero

方法运行Exception结果:null

Spring拦截器实现+后台原理(MethodInterceptor)

MethodInterceptor

MethodInterceptor是AOP项目中的拦截器(注:不是动态代理拦截器),区别与HandlerInterceptor拦截目标时请求,它拦截的目标是方法。

实现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>
        <!--切入点,controlller -->
        <aop:pointcut id="pointcut_test"   expression="execution(* com.paic.phssp.springtest.controller..*.*(..))" />
        <!--在该切入点使用自定义拦截器 ,按照先后顺序执行 -->
        <aop:advisor pointcut-ref="pointcut_test" advice-ref="methodInvokeInterceptor" />
    </aop:config>
    <!-- 自动扫描使用了aspectj注解的类 -->
    <aop:aspectj-autoproxy/>

执行:

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;
    }
}

运行结果:

AutoAspectJInterceptor begin around......

>>>>:isAuthenticated=false

AutoAspectJInterceptor end around......

简单介绍下关键词

  • AOP=Aspect Oriented Program面向切面(方面/剖面)编程
  • Advice(通知):把各组件中公共业务逻辑抽离出来作为一个独立 的组件
  • Weave(织入):把抽离出来的组件(Advice),使用到需要使用该逻辑 地方的过程。
  • JoinPoint (连接点): Advice 组件可以weave的特征点。
  • PointCut(切入点):用来明确Advice需要织入的连接点
  • Aspect(切面):Aspect=Advice + PointCut

通知类型

  • @Before 在切点方法之前执行
  • @After 在切点方法之后执行
  • @AfterReturning 切点方法返回后执行
  • @AfterThrowing 切点方法抛异常执行
  • @Around环绕通知

执行顺序:

  • @Around环绕通知
  • @Before通知执行
  • @Before通知执行结束
  • @Around环绕通知执行结束
  • @After后置通知执行了!
  • @AfterReturning

切面设置

可以使用&&、||、!、三种运算符来组合切点表达式

execution表达式

"execution(public * com.xhx.springboot.controller.*.*(..))"
  • *只能匹配一级路径
  • ..可以匹配多级,可以是包路径,也可以匹配多个参数
  • + 只能放在类后面,表明本类及所有子类

within(类路径) 配置指定类型的类实例,同样可以使用匹配符

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

看到实现接口BeanPostProcessor,必然在初始化Bean前后,执行接口方法。

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、具体实现

上面提到实现接口BeanPostProcessor,必然在初始化Bean前后,执行接口方法。看下面时序图:

AbstractAutoProxyCreator的postProcessAfterInitialization()方法

DefaultAopProxyFactory.createAopProxy()方法,具体创建代理类。两种动态代理:JDK动态代理和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));
            }
        }
    }

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • SpringBoot 如何添加容器启动的初始化逻辑的操作方法

    SpringBoot 如何添加容器启动的初始化逻辑的操作方法

    这篇文章主要介绍了SpringBoot 如何添加容器启动的初始化逻辑,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-09-09
  • Struts2学习手册之文件上传基础教程

    Struts2学习手册之文件上传基础教程

    Struts2提供的文件上传下载机制十分简便,使得我们写很少的代码,下面这篇文章主要给大家介绍了关于Struts2学习手册之文件上传的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考下
    2018-05-05
  • java 汉诺塔Hanoi递归、非递归(仿系统递归)和非递归规律 实现代码

    java 汉诺塔Hanoi递归、非递归(仿系统递归)和非递归规律 实现代码

    汉诺塔(Hanoi) 算法Java实现。通过三个函数,分别对Hanoi进行递归、非递归和非递归规律实现。
    2013-05-05
  • 全网最全最细的jmeter接口测试教程以及接口测试流程(入门教程)

    全网最全最细的jmeter接口测试教程以及接口测试流程(入门教程)

    本文主要介绍了全网最全最细的jmeter接口测试教程以及接口测试流程,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-11-11
  • Springboot如何配置yml文件与映射到java类

    Springboot如何配置yml文件与映射到java类

    这篇文章主要介绍了Springboot如何配置yml文件与映射到java类问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-09-09
  • 使用SpringBoot简单了解Druid的监控系统的配置方法

    使用SpringBoot简单了解Druid的监控系统的配置方法

    这篇文章主要介绍了使用SpringBoot简单了解Druid的监控系统的配置,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-06-06
  • 详解Java I/O流中的字符流有哪些

    详解Java I/O流中的字符流有哪些

    字节流的功能已经十分强大,几乎可以直接或间接地处理任何类型的输入/输出操作,但它却不能直接操作16位的Unicode字符,这就需要使用字符流,所以在今天的内容中,小编会给大家讲解IO流中的字符流,希望各位能够继续耐心学习
    2023-10-10
  • Hadoop源码分析三启动及脚本剖析

    Hadoop源码分析三启动及脚本剖析

    本篇是Hadoop源码分析系列文章第三篇,主要介绍Hadoop启动以及脚本的剖析,后续本系列文章会持续更新,有需要的朋友可以借鉴参考下
    2021-09-09
  • 二叉搜索树实例练习

    二叉搜索树实例练习

    一棵二叉查找树是按二叉树结构来组织的。这样的树可以用链表结构表示,其中每一个结点都是一个对象
    2012-11-11
  • SpringCloud Alibaba框架介绍

    SpringCloud Alibaba框架介绍

    spring cloud是一个基于springboot实现的微服务架构开发工具,目前主流的SpringCloud分为SpringCloud Netflix和阿里云开源的SpringCloud Alibaba两个系列,本文主要介绍SpringCloud Alibaba框架,感兴趣的朋友可以参考一下
    2023-04-04

最新评论