ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

AspectJ切入点@Pointcut语法详解非常详细(附接口请求详情打印代码demo)

2021-04-19 16:31:39  阅读:245  来源: 互联网

标签:匹配 demo void Object public Pointcut java 方法 AspectJ


AspectJ切入点@Pointcut语法详解非常详细

分类pointcuts 遵循特定的语法用于捕获每一个种类的可使用连接点。 主要的种类:
方法执行:execution(MethodSignature)
方法调用:call(MethodSignature)
构造器执行:execution(ConstructorSignature)
构造器调用:call(ConstructorSignature)
类初始化:staticinitialization(TypeSignature)
属性读操作:get(FieldSignature)
属性写操作:set(FieldSignature)
例外处理执行:handler(TypeSignature)
对象初始化:initialization(ConstructorSignature)
对象预先初始化:preinitialization(ConstructorSignature)

Advice执行:adviceexecution()
切入点指示符用来指示切入点表达式目的,,在Spring AOP中目前只有执行方法这一个连接点,Spring AOP支持的AspectJ切入点指示符如下:
execution:用于匹配方法执行的连接点;
within:用于匹配指定类型内的方法执行;
this:用于匹配当前AOP代理对象类型的执行方法;注意是AOP代理对象的类型匹配,这样就可能包括引入接口也类型匹配;
target:用于匹配当前目标对象类型的执行方法;注意是目标对象的类型匹配,这样就不包括引入接口也类型匹配;
args:用于匹配当前执行的方法传入的参数为指定类型的执行方法;
@within:用于匹配所以持有指定注解类型内的方法;
@target:用于匹配当前目标对象类型的执行方法,其中目标对象持有指定的注解;
@args:用于匹配当前执行的方法传入的参数持有指定注解的执行;
@annotation:用于匹配当前执行方法持有指定注解的方法;
bean:Spring AOP扩展的,AspectJ没有对于指示符,用于匹配特定名称的Bean对象的执行方法;
reference pointcut:表示引用其他命名切入点,只有@ApectJ风格支持,Schema风格不支持。

来了解下AspectJ类型匹配的通配符:
:匹配任何数量字符;
…:匹配任何数量字符的重复,如在类型模式中匹配任何数量子包;而在方法参数模式中匹配任何数量参数。
+:匹配指定类型的子类型;仅能作为后缀放在类型模式后边。
java.lang.String 匹配String类型;
java.
.String 匹配java包下的任何“一级子包”下的String类型;
如匹配java.lang.String,但不匹配java.lang.ss.String
java…* 匹配java包及任何子包下的任何类型;
如匹配java.lang.String、java.lang.annotation.Annotation
java.lang.*ing 匹配任何java.lang包下的以ing结尾的类型;
java.lang.Number+ 匹配java.lang包下的任何Number的自类型;
如匹配java.lang.Integer,也匹配java.math.BigInteger

举例说明:

任意公共方法的执行:
execution(public * (…))
任何一个以“set”开始的方法的执行:
execution(
set*(…))
AccountService 接口的任意方法的执行:
execution(* com.xyz.service.AccountService.(…))
定义在service包里的任意方法的执行:
execution(
com.xyz.service..(…))
定义在service包和所有子包里的任意类的任意方法的执行:
execution(* com.xyz.service….(…))
定义在pointcutexp包和所有子包里的JoinPointObjP2类的任意方法的执行:
execution(* com.test.spring.aop.pointcutexp…JoinPointObjP2.*(…))")
*> 最靠近(…)的为方法名,靠近.(…))的为类名或者接口名,如上例的JoinPointObjP2.(…))

pointcutexp包里的任意类.
within(com.test.spring.aop.pointcutexp.)
pointcutexp包和所有子包里的任意类.
within(com.test.spring.aop.pointcutexp…
)
实现了Intf接口的所有类,如果Intf不是接口,限定Intf单个类.
this(com.test.spring.aop.pointcutexp.Intf)
***> 当一个实现了接口的类被AOP的时候,用getBean方法必须cast为接口类型,不能为该类的类型.

带有@Transactional标注的所有类的任意方法.
@within(org.springframework.transaction.annotation.Transactional)
@target(org.springframework.transaction.annotation.Transactional)
带有@Transactional标注的任意方法.
@annotation(org.springframework.transaction.annotation.Transactional)
***> @within和@target针对类的注解,@annotation是针对方法的注解

参数带有@Transactional标注的方法.
@args(org.springframework.transaction.annotation.Transactional)
参数为String类型(运行是决定)的方法.

// @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();
}

}.

/**
* 定义一个切入点 只拦截controller.
* 解释下:
* ~ 第一个 * 代表任意修饰符及任意返回值.
* ~ 第二个 * 定义在web包或者子包
* ~ 第三个 * 任意方法
* ~ … 匹配任意数量的参数.
*/

    @Pointcut("execution(* com.precinct.data.controller..*.*(..))")
    public void logPointcut() {
    }
//around(和上面的方法名一样)
    @org.aspectj.lang.annotation.Around("logPointcut()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        LOG.info("=====================================Method  start====================================");
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        long start = System.currentTimeMillis();
        String requestMethod = request.getMethod();
        try {
            Object result = joinPoint.proceed();
            long end = System.currentTimeMillis();
            LOG.info("请求地址:" + request.getRequestURI());
            LOG.info("用户IP:" + request.getRemoteAddr());
            LOG.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
            if ("POST".equals(requestMethod)){
                LOG.info("POST请求参数: " + getRequest(request));
            }else if ("GET".equals(requestMethod)){
                LOG.info("GET请求参数: " + URLDecoder.decode(request.getQueryString(), "UTF-8" ));
            }
            LOG.info("执行时间: " + (end - start) + " ms!");
            LOG.info("=====================================Method  End====================================");
            return result;
        } catch (Throwable e) {
            long end = System.currentTimeMillis();
            LOG.info("URL:" + request.getRequestURI());
            LOG.info("IP:" + request.getRemoteAddr());
            LOG.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
            if ("POST".equals(requestMethod)){
                LOG.info("POST请求参数: " + getRequest(request));
            }else if ("GET".equals(requestMethod)){
                LOG.info("GET请求参数: " + URLDecoder.decode(request.getQueryString(), "UTF-8" ));
            }
            LOG.info("执行时间: " + (end - start) + " ms!");
            LOG.info("=====================================Method  End====================================");
            String requestParam = request.getQueryString();
            System.out.println("requestParam" + requestParam);
            System.out.println("requestParam" + requestParam);
            throw e;
        }
    }

    public static String getRequest(HttpServletRequest request){
        StringBuffer sb = new StringBuffer();
        BufferedReader reader = null;
        String inputParam = "";
        try {
            reader = request.getReader();
            String line = "";
            while((line = reader.readLine()) != null) {
                sb.append(line);
            }
            inputParam = URLEncoder.encode(sb.toString(), HTTP.UTF_8);
            inputParam = URLDecoder.decode(inputParam, HTTP.UTF_8);

        } catch (Exception e) {
            inputParam = "";
        }
        return inputParam;
    }

标签:匹配,demo,void,Object,public,Pointcut,java,方法,AspectJ
来源: https://blog.csdn.net/weixin_43391686/article/details/115867469

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有