博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
《Spring》(十五)---@AspectJ
阅读量:5144 次
发布时间:2019-06-13

本文共 6183 字,大约阅读时间需要 20 分钟。

使用@AspectJ定义一个切面:

package com.ivy.annotation.aspectj;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;@Aspectpublic 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 yougreet to John...serve to John...

 

  2. 自动代理织入

  通过配置使用@AspectJ切面

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

 

  

  • @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 引介增强

  示例:

@Aspectpublic 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接口,则顺序不定。

 

转载于:https://www.cnblogs.com/IvySue/p/6500549.html

你可能感兴趣的文章
xml合并问题,多个xml拼接
查看>>
20169220 <网络攻防实践> 第四周学习总结
查看>>
windows 下版本控制系统 安装与 配置
查看>>
计算机数值表示
查看>>
Seafile搭建私有云盘
查看>>
WCF自定义异常
查看>>
软件工程——团队作业2
查看>>
ceph osd 自动挂载的N种情况
查看>>
@RequestParam @RequestBody @PathVariable 等参数绑定注解详解
查看>>
spring配置文件详解
查看>>
poj 2318 计算几何
查看>>
[Java]-集合框架
查看>>
累了。
查看>>
JS 拼凑字符串
查看>>
hack
查看>>
c++学习笔记_2
查看>>
自我鉴定,继续努力
查看>>
网络内核sk_buff结构体
查看>>
【python】SQLAlchemy
查看>>
3.Java网络编程之IP
查看>>