zoukankan      html  css  js  c++  java
  • @AspectJ 语法概要

    引言

    这是继上篇《AspectJ传统语法概要》之后,对@AspectJ语法的一个整理。

    传统的AspectJ固然强大,但需要特定的编译器ajc支持。为了避免这样的依赖,以“Keeping the code plain Java”的方式去实现AOP,AspectJ提供了一种略微区别于传统语法的形式:@AspectJ。@AspectJ使用了Annotation作为实现aspect各要素的基础,使实现aspect的代码可以被任意的Java编译器理解并编译运行。尽管@AspectJ相比传统的AspectJ语法更加冗长和累赘,但却更容易被普通的Java程序员理解和使用。流行的框架Spring,也使用了从@AspectJ衍生的AOP实现方式。

    需要注意的是,正是为了兼容普通的Java编译器,@AspectJ做出了相当大的妥协,只实现了AspectJ的一个子集:aspect、pointcut、advice、declaring parents与declaring errors and warnings,而introducing、exception handling与privileged aspect等内容没有得到支持。掌握了AspectJ的传统语法,理解@AspectJ就会变得非常简单了。


    @AspectJ下的aspect实现

    // @Aspect不再能修饰接口,而只能是类
    // 访问aspect实例时,不再能使用aspectOf()和hasAspect()
    // 而应以aspect的类作为参数,使用由org.aspectj.lang.Aspects提供的静态方法aspectOf()与hasAspect()
    @Aspect("perthis|pertarget|percflow|percflowbelow(Pointcut) | pertypewithin(TypePattern)")
    // 定义aspect的优先顺序,需要使用完全的限定名,这在@AspectJ中很普遍,也是由Java编译器决定的
    // AspectJ的未来版本可能提供string[]类型的参数支持
    @DeclarePrecedence("ajia.HomeSecurityAspect, ajia.SaveEnergyAspect")
    public abstract static class AspectName
            extends class_or_aspect_name
            implements interface_list
    {
        // 使用@Pointcut配合一个占位用的方法声明来定义一个pointcut
        // 抽象pointcut依旧只有名字、参数,没有实际的joinpoint定义
        @Pointcut
        public abstract void pointcut_name(Type args);
    
        // pointcut定义时仍要注意使用全限定名
        // 方法只是占位符,方法体除了采用类似条件编译时的if()切入方式外都置空
        // 若方法会抛出异常,则同样要在方法原型加上throws声明
        // 切记要开启编译器选项-g:vars,让编译器预留参数名(建设采用这种方式)
        @Pointcut("execution(public * ajia.banking.domain.Account.*(float)) && this(account) && args(amount)")
        public void accountOperation(Account account, float amount) {}
    
        // 或者利用Annotation的属性,建立参数名与pointcut之间的关联
        // 但这样得自己维护argNames与方法参数表的一致性,所以不推荐
        @Pointcut(value="execution(public * ajia.banking.domain.Account.*(float)) && this(account) && args(amount)",
                  argNames="account, amount")
        public void accountOperation(Account account, float amount) {}
    
        // advice的定义类似传统语法,
        // before-advice必须是public与void的
        // 方式一:匿名pointcut
        @Before("execution(* *(..)) && !within(ajia.monitoring.*)")
        public void beatHeart()
        {
            heartBeatListener.beat();
        }
    
        // 方式二:命名的pointcut
        @Pointcut("execution(* *.*(..)) && !within(ajia.monitoring.*)")
        public void aliveOperation() {}
        
        @Before("aliveOperation()")
        public void beatHeart()
        {
            heartBeatListener.beat();
        }
    
        // advice仍旧支持经由类JoinPoint的反射获取上下文
        // JoinPoint对象本身定义动态部分
        // JoinPoint.StaticPart定义静态部分
        // JoinPoint.EnclosingStaticpart定义包裹静态信息的部分
        // 同时,advice仍旧支持target/this/args
        @Pointcut("call(void Account.credit(float)) && target(account) && args(amount)")
        public void creditOperation(Account account, float amount) {}
    
        @Before("creditOperation(account, amount)" )
        public void beforeCreditOperation(JoinPoint.StaticPart jpsp, JoinPoint.EnclosingStaticPart jpesp,
                                          Account account, float amount)
        {
            System.out.println("Crediting " + amount + " to " + account);
        }
    
        // after-advice的实现同样直观
        @Pointcut("call(* java.sql.Connection.*(..)) && target(connection)")
        public void connectionOperation(Connection connection) {}
    
        @After("connectionOperation(connection)")
        public void monitorUse(Connection connection)
        {
            System.out.println("Just used " + connection);
        }
    
        @AfterReturning(value="connectionOperation(connection)", returning="ret")
        public void monitorSuccessfulUse(Connection connection, Object ret)
        {
            System.out.println("Just used " + connection + " successfully which returned " + ret);
        }
    
        @AfterThrowing(value="connectionOperation(connection)", throwing="ex")
        public void monitorFailedUse(Connection connection, Exception ex)
        {
            System.out.println("Just used " + connection + " but met with a failure of kind " + ex);
        }
    
        // around-advice的实现稍显复杂
        // 需要参考JoinPoint反射的方式,为around-advice的方法传入一个ProceedingJoinPoint参数
        // 该对象有方法proceed()及其重载版本proceed(Object[]),可以执行被切入的方法
        // 这个Object[]数组中,依次为this-target-args
        // 分别经由ProceedingJoinPoint对象的方法this()、target()与getArgs()获取
        @Around("pointcut_xxx()")
        public Object measureTime(ProceedingJoinPoint pjp)
        {
            Object[] context = formProceedArguments(pjp.this(), pjp.target(), pjp.getArgs());
            Object result = proceed(context);
            
            return result;
        }
    
        // 可以用下面这个方法获取该Object[]数组
        public static Object[] formProceedArguments(Object thiz, Object target, Object[] arguments)
        {
            int argumentsOffset = 0;
            if(thiz != null) { argumentsOffset++; }
            if(target != null) { argumentsOffset++; }
            
            Object[] jpContext = new Object[arguments.length + argumentsOffset];    
            int currentIndex = 0;
    
            if(thiz != null) { jpContext[currentIndex++] = thiz; }
            if(target != null) { jpContext[currentIndex++] = target; }
            System.arraycopy(arguments, 0,jpContext, argumentsOffset, arguments.length);
        
            return jpContext;
        }
    
        // 声明Error与Warning
        @DeclareError("callToUnsafeCode()")
        static final String unsafeCodeUsageError = 
            "This third-party code is known to result in a crash";
    
        @DeclareWarning("callToBlockingOperations()")
        static final String blockingCallFromAWTWarning = 
            "Please ensure you are not calling this from the AWT thread";
    
        // @AspectJ提供了@DeclareParents,但很少使用,而更多使用下面的@DeclareMixin作为替代
        // @AspectJ实现的Mix-in,本质是一个返回proxy对象的工厂方法,用于返回一个包裹了aspect的proxy
    
        // 下面的代码等价于:declare parents: ajia.banking.domain.* implements Serializable;
        // 由于返回null,因此只能作为对被切入类的都有Serializable的一个标记
        @DeclareMixin("ajia.banking.domain.*")
        public Serializable serializableMixin()
        {
            return null;
        }
    
        // @DeclareMixin支持一个参数,模仿依赖注入的方式,把被切入的对象传递给工厂
        // AuditorImp是接口Auditor的一个实现,即对Object的一个代理
        @DeclareMixin("ajia.banking.domain.*")
        public Auditor auditorMixin(Object mixedIn)
        {
            return new AuditorImpl(mixedIn);
        }
    
        // 要mix-in若干个接口,则需要在interfaces里依次放上要添附的接口
        @DeclareMixin(value="ajia.banking.domain.*", interfaces="{Auditor.class, MonitoringAgent.class}")
        public AuditorMonitoringAgent mixin()
        {
            return new AuditorMonitoringAgentImpl();
        }
    }
    转载请注明出处及作者,谢谢!
  • 相关阅读:
    java依赖注入
    Mac OS 中的 Python(和 NumPy)开发环境设置
    英语巴菲
    经济学原理
    You are beautiful
    大数据之tachyon(未完版)
    机器学习温和指南
    机器学习之回归
    Mac 命令
    批处理--批量打开程序&批量关闭程序
  • 原文地址:https://www.cnblogs.com/Abbey/p/2455855.html
Copyright © 2011-2022 走看看