AspectJ切入点@Pointcut语法详解非常详细(附接口请求详情打印代码demo)
发布日期:2022-02-27 02:37:52
浏览次数:53
分类:技术文章
本文共 10692 字,大约阅读时间需要 35 分钟。
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; }
转载地址:https://blog.csdn.net/weixin_43391686/article/details/115867469 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!
发表评论
最新留言
做的很好,不错不错
[***.243.131.199]2024年04月18日 15时06分04秒
关于作者
喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
为新语言编写Visual Studio Code语法高亮插件
2019-04-26
手机编程环境初尝试-用AIDE开发Android应用
2019-04-26
程序员面试时用中文命名写白板代码的好处
2019-04-26
1992年日本对母语编程的可读性比较实验
2019-04-26
[转] 用python编写控制网络设备的自动化脚本3:启动
2019-04-26
扩展Python控制台实现中文反馈信息
2019-04-26
扩展Python控制台实现中文反馈信息之二-正则替换
2019-04-26
在PyPI测试平台发布Python包
2019-04-26
中文代码示例之Electron桌面应用开发初体验
2019-04-26
中文代码示例之NW.js桌面应用开发初体验
2019-04-26
为《 两周自制脚本语言 》添加中文测试代码
2019-04-26
将《 两周自制脚本语言 》测试中使用的接口中文化
2019-04-26
5分钟入门LingaScript-尝鲜中文版TypeScript
2019-04-26
重拾《 两周自制脚本语言 》- 支持中文标识符
2019-04-26
Java实现文本编辑时基于拼音输入的补全原型
2019-04-26
从立创EDA,Gratipay看中文编程开发环境和推广运营的一个趋势
2019-04-26
中文代码之Django官方入门:建立模型
2019-04-26