欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

使用Spring方法拦截器MethodInterceptor

程序员文章站 2022-06-26 11:18:52
目录spring方法拦截器methodinterceptor前言spring拦截器实现+后台原理(methodinterceptor)methodinterceptormethodintercepto...

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/>

执行:

使用Spring方法拦截器MethodInterceptor

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

使用Spring方法拦截器MethodInterceptor

看到实现接口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()方法

使用Spring方法拦截器MethodInterceptor

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

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