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 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:ArrayList和LinkedList的底层原码分析
下一篇:项目性能遇到瓶颈,需要分库分表,主键id如何处理?

发表评论

最新留言

做的很好,不错不错
[***.243.131.199]2024年04月18日 15时06分04秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章