• 《Spring》(十五)---@AspectJ


    使用@AspectJ定义一个切面:

    package com.ivy.annotation.aspectj;
    
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    
    @Aspect
    public class PreGreetingAspect {
    
        @Before("execution(*greetTo(..))")
        public void beforeGreeting() {
            System.out.println("How are you");
        }
    }

    第三方处理程序通过类中是否拥有@Aspect注解判断其是否为一个切面。@Before注解表示该增强是前置增强,成员值是一个切点表达式,意思是在目标类的greetTo()方法上织入增强,greetTo()方法可以带任意的入参和任意的返回值。而beforeGreeting()方法就是增强所使用的横切逻辑,该横切逻辑在目标方法前调用。

    1. 编程方式织入

    ProxyFactory还有另一个兄弟,AspectJProxyFactory,通过AspectJProxyFactory可以实现Aspect定义到目标对象的织入。例如:为NaiveWaiter生成织入PreGreetingAspect切面的代理:

    package com.ivy.annotation.aspectj;
    
    import org.springframework.aop.aspectj.annotation.AspectJProxyFactory;
    
    import com.ivy.aop.advice.NaiveWaiter;
    import com.ivy.aop.advice.Waiter;
    
    public class AspectJProxyTest {
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
    
            Waiter target = new NaiveWaiter();
            AspectJProxyFactory factory = new AspectJProxyFactory();
            factory.setTarget(target);
            factory.addAspect(PreGreetingAspect.class);
            Waiter proxy = factory.getProxy();
            proxy.greetTo("John");
            proxy.serveTo("John");
        }
    
    }

    首先AspectProxyFactory设置了目标对象,之后添加一个切面类,该类必须是带@Aspect注解的类,然后就可以获取织入了切面的代理对象了。

    运行结果:

    log4j:WARN No appenders could be found for logger (org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory).
    log4j:WARN Please initialize the log4j system properly.
    How are you
    greet to John...
    serve to John...

      2. 自动代理织入

      通过配置使用@AspectJ切面

      一般情况都是通过Spring的配置完成切面织入的工作:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans 
                                  http://www.springframework.org/schema/beans/spring-beans.xsd
                                  http://www.springframework.org/schema/aop
                                  http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
      <aop:aspectj-autoproxy proxy-target-class="true">
     </aop:aspectj-autoproxy>
    <bean id="waiter" class="com.ivy.aop.advice.NaiveWaiter"/> <bean class="com.ivy.annotation.aspectj.PreGreetingAspect"/> </beans>

      

    • @AspectJ形式的Pointcut

      @AspectJ形式的Pointcut声明,依附在@Aspect所标注的Aspect定义类之内,通过Pointcut注解,指定AspectJ形式的Pointcut表达式之后,将这个指定了相应表达式的注解标注到Aspect定义类的某个方法上即可。

    Pointcut表述语言中有以下几种可用标志符:

      execution

    execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?), 其中方法的返回类型/方法名以及参数部分的匹配模式是必须指定的,其他部分的匹配模式可以省略。

    execution中使用两种通配符,即*和..。例如 execution(public void Foo.doSomething(String))

    1. * 可以用于任何部分的匹配模式中,可以匹配相邻的多个字符,即一个Word。所以可以简化为execution(* *(String))
    2. ..可以在两个位置使用,一个在declaring-type-pattern规定的位置,一个在方法参数匹配模式的位置。例如

          execution(void cn.spring21.*.doSomething(*)) // 只能指定到cn.spring21这一层下的所有类

          execution(void cn.spring21..*.doSomething(*)) // 可以匹配cn.spring21包下的所有类,以及cn.spring21下层包下声明的所有类型

        如果..用于方法参数列表匹配位置,则表示该方法可以有0到多个参数,参数类型不限。如下:

          execution(void *.doSomething(..))

      within

    within标志符只接受类型声明,它将会匹配指定类型下的所有Joinpoint。不过,因为SpringAoP只支持方法级别的Joinpoint,所以在我们为within指定某个类后,它将匹配指定类所声明的所有方法执行。

      

      切点表达式函数

      切点表达式由关键字和操作参数组成,Spring支持9个切点表达式函数,5个类型:

    1.  方法切点函数

        execution(): 方法匹配模式串,execution(* greetTo(..))表示所有目标类的greetTo()方法

        @annotation(): 方法注解类名,@annotation(com.ivy.anno.NeedTest)表示任何标注了@NeedTest注解的目标类

      2.    方法入参切点函数

        args(类名): args(com.ivy.advice.Waiter)表示所有有且仅有一个按类型匹配于Waiter入参的方法

        @args(类型注解类名): @args(com.ivy.Monitorable)表示任何这样的一个目标方法:它有一个入参且入参对象的类标注@Monitorable注解

      3.   目标类切点函数

        within(类名匹配串): within(com.ivy.service.*)表示com.ivy.service包中的所有连接点,即包中所有类的所有方法。而within(com.ivy.service.*Service)表示com.ivy.service包中所有以Service结尾的类的所有连接点。

        target(类名): ,target(com.ivy.Waiter)定义的切点,Waiter以及Waiter实现类NaiveWaiter中所有连接点都匹配该切点

        @within(类型注解类名): @within(com.ivy.Monitorable)定义的切点,假如Waiter类标注了@Monitorable注解,则Waiter以及NaiveWaiter类的所有连接点都匹配

      4.   目标类切点函数

        @target(类型注解类名): @target(com.ivy.Monitorable),假如NaiveWaiter标注了@Monitorable,则NaiveWaiter所有连接点匹配切点。

      5.   代理类切点函数

        this(类名): 代理类按类型匹配于指定类,则被代理的目标类所有连接点匹配切点。

     注意:

      所有以@开头的标志符,都只能指定注解类型参数,而且,注解只有Java 5及之后才有效。

      不同增强类型

      @Before 前置增强

      @AfterReturning 后置增强

      @Around 环绕增强

      @AfterThrowing 抛出增强

      @After Final增强,不管是抛出异常或是争抢退出,该增恰国内都会得到执行,该增强没有对应的增强接口,一般用于释放资源。

      @DeclareParents 引介增强

      示例:

    @Aspect
    public class MockAspect {
        @Pointcut("execution(* destroy(..))")
        public void destroy(){}
    
        @Before("execution(public void *.methodName(String))")
        public void setUpResourceFolder() {
            ...
        }
    
        @After("destroy()")
        public void cleanUpResourceIfNeccessary() {
            ...
        }
    }

      切点复合运算

    package com.ivy.annotation.aspectj;
    
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.Before;
    
    public class TestAspect {
    
        // 匹配com.ivy包中所有greetTo()方法的切点
        @After("within(com.ivy.*)&&execution(* greetTo(..))")
        public void greetToFun() {
            System.out.println("--greetToFun() executed!");
        }
        
        // 匹配所有serveTo()方法并且该方法不位于NaiveWaiter目标类的切点
        @Before("!target(com.ivy.NaiveWaiter)&&execution(* serveTo(..))")
        public void notServeInNaiveWaiter() {
            System.out.println("--notServeInNaiveWaiter() executed!");
        }
        
        // 匹配Waiter和Seller接口实现类所有连接点的切点
        @AfterReturning("target(com.ivy.Waiter)||target(com.ivy.Seller)")
        public void waiterOrSeller() {
            System.out.println("--waiterOrSeller() executed!");
        }
    }

      

      命名切点

      切点直接声明在增强方法处,这种切点声明方式称为匿名切点,匿名切点只能在声明处使用。如果希望在其他地方重用一个切点,可以通过@Pointcut注解以及切面类方法对切点进行命名。

    package com.ivy.annotation.aspectj;
    
    import org.aspectj.lang.annotation.Pointcut;
    
    public class TestNamePointcut {
    
        // 通过注解方法inPackage(), 该方法名就作为切点的名称,
        // 方法修饰符为private,表明该命名切点只能在本切面类中使用
        @Pointcut("within(com.ivy)")
        private void inPackage() {}
        
        // 通过注解方法inPackage(), 该方法名就作为切点的名称,
        // 方法修饰符为protected,表明该命名切点可以在当前包中的切面类,子切面类中使用
        @Pointcut("execution(* greetTo(..))")
        protected void greetTo() {}
        
        // 通过注解方法inPackage(), 该方法名就作为切点的名称,
        // 方法修饰符为public,表明该命名切点可以在任意类中使用
        @Pointcut("inPackage() and greetTo()")
        public void inPkgGreetTo() {}
    }

      inPkgGreetTo可以引用同类的greetTo()和inPackage(),而inPkgGreetTo()可以被任何类使用。

    package com.ivy.annotation.aspectj;
    
    import org.aspectj.lang.annotation.Before;
    
    public class TestNamePoint {
    
        @Before("NamePointcut.inPkgGreetTo()")
        public void pkgGreetTo() {
            System.out.println("--pgkGreetTo() executed!--");
        }
        
        @Before("!target(com.ivy.NaiveWaiter)&&NamePointcut.inPkgGreetTo()")
        public void pkgGreetToNotNaiveWaiter() {
            System.out.println("--pgkGreetTo() executed!--");
        }
    }

      增强织入顺序

    1.  如果增强在同一个切面类中声明,则依照增强在切面类中定义的顺序进行织入。
    2.  如果增强位于不同的切面类中,且这些切面类都实现了Ordered接口,则由接口方法的顺序号决定。
    3.  如果增强位于不同的切面类中,且这些切面没有实现Ordered接口,则顺序不定。
  • 相关阅读:
    swarm集群搭建 及集群维护操作
    zabbix 告警说明
    yum 下载安装包
    mongdb常见操作
    cloudera5.16.1 离线安装
    centos7 安装hadoop-3.2.1
    rpcbind 启动报错
    ingress-nginx 安装
    Dubbo学习
    mybatis防止SQL注入
  • 原文地址:https://www.cnblogs.com/IvySue/p/6500549.html
Copyright © 2020-2023  润新知