在Spring Boot AOP中,在方法的”Around”阶段如何获取类的注解?

huangapple go评论71阅读模式
英文:

In Spring Boot AOP, how to get class annotation when in the method "Around" period?

问题

感谢这里的所有朋友们。我知道如何获取method上的注解集合的注解参数。

虽然我想在Class级别添加注解。如何在AOP环绕方法中从Class中检索属性呢?

顺便问一下,为什么我需要将响应值从void更改为Object并返回pjp.proceed()?如果方法没有响应,请求将会冻结。

对于带有HasRole注解的方法

@Around(" @annotation(com.example.demo.aspect.annotation.HasRole) && @annotation(annotation)")
public void aroundHasRole(ProceedingJoinPoint pjp, HasRole annotation) throws Throwable {

	log.info("value->>{}", annotation.value());
	pjp.proceed();

}

对于带有HasRole注解的类或方法

@Around("(@within(com.example.demo.aspect.annotation.IsAdmin)"
		+ "|| @annotation(com.example.demo.aspect.annotation.IsAdmin))")
public Object aroundHasRole(ProceedingJoinPoint pjp) throws Throwable {

	// <<如何在这里获取注解信息?>>
	return pjp.proceed();

}
英文:

Thanks for all the friends here. I know how to get the annotation parameter for the annotation set on method.

While I would like to add the annotation in the Class level. How to retrieve the attribute from Class in the AOP Around method?

By the way, I have another question, why I need to change the response value from void to Object and return pjp.proceed(). If the method do not have response, the request will freezing.

For the Method which has HasRole annotation

@Around(&quot;@annotation(com.example.demo.aspect.annotation.HasRole) &amp;&amp; @annotation(annotation)&quot;)
public void aroundHasRole(ProceedingJoinPoint pjp, HasRole annotation) throws Throwable {

	log.info(&quot;value-&gt;&gt;{}&quot;, annotation.value());
	pjp.proceed();

}

For Class or Method which has HasRole annotation

@Around(&quot;(@within(com.example.demo.aspect.annotation.IsAdmin)&quot;
		+ &quot;|| @annotation(com.example.demo.aspect.annotation.IsAdmin))&quot;)
public Object aroundHasRole(ProceedingJoinPoint pjp) throws Throwable {

	&lt;&lt;How to get annotation information here?&gt;&gt;
	return pjp.proceed();

}

答案1

得分: 2

正如您可能已经注意到的那样,您不能将来自不同||分支的信息绑定到advice方法的参数中,因为这会导致歧义。详情请参阅我的这里的回答(链接:https://stackoverflow.com/a/43344112/1082681)和这里的回答(链接:https://stackoverflow.com/a/26117370/1082681)。因此,如果您想避免丑陋(且缓慢)的反射操作,请按照我在其他回答中推荐的做法,编写两个不同的advices,并将共用的代码提取到一个辅助方法中,以避免代码重复。类似于这样的结构(未经测试,仅为了让您了解代码结构的想法):

@Around(" @within(annotation)")
public Object classIsAdmin(ProceedingJoinPoint pjp, IsAdmin annotation) throws Throwable {
  return commonIsAdmin(pjp, annotation);
}

@Around(" @annotation(annotation)")
public Object methodIsAdmin(ProceedingJoinPoint pjp, IsAdmin annotation) throws Throwable {
  return commonIsAdmin(pjp, annotation);
}

public Object commonIsAdmin(ProceedingJoinPoint pjp, IsAdmin annotation) throws Throwable {
  // 在这里放置所有共用的advice代码。
  // 非共用的代码可以保留在原始的advice方法中。
  log.info("value->>{}", annotation.value());
  return pjp.proceed();
}

为什么我需要将响应值从void更改为Object并返回pjp.proceed()?如果方法没有响应,请求将会冻结。

@Before@After advice相比,在@Around advice中,您可以通过完全跳过目标方法的执行(不调用proceed())或者丢弃/修改proceed()的结果来修改返回值。您在返回值方面完全自由,只需与目标方法的返回类型匹配即可。

话虽如此,应该清楚地表明,@Around advice方法的返回类型也必须与它拦截的目标方法的返回类型匹配。它可以是一个精确的类型,如MyType,一个超类型,或者如果您的advice目标涵盖了多种类型且没有共同的超类型,则可以是Object(所有类型的超类型)。如果所有目标方法都返回void,则advice也可以具有void的返回类型(前提是所有目标方法也返回void,否则即使切入点本身匹配,advice也不会匹配这些方法)。

因此,@Around advice是否匹配取决于切入点本身和返回类型的组合。您可以将其用作工具,通过定义特定的返回类型来限制切入点匹配(然后您需要将proceed()的返回值转换为该返回类型,因为proceed()始终返回Object)。

顺便提一下,如果目标方法返回原始类型(如intboolean等),那么advice将自动将结果包装成IntegerBoolean

您确实应该阅读Spring AOP和AspectJ的手册或教程,因为我在这里解释的内容可以在那里找到。


更新: OP要求有关参数绑定和确定名称的文档,以下是相关信息:

  • 您可以为所有advice类型(如@Before@After@Around)指定argNames参数。
  • 如果缺少该注释参数,Spring AOP将尝试通过类文件的调试信息来匹配advice方法的参数名,如果已经编译。否则,在这种情况下匹配将失败。
  • 当使用编译时织入的完整AspectJ而不是Spring AOP时,即使没有调试信息,也可以确定名称,因为AspectJ编译器可以在编译期间确定必要的信息。

所有这些都在Spring AOP手册中有描述。

英文:

As you probably have noticed, you cannot bind information from different || branches to an advice method parameter because it would be ambiguous, see also my answers here and here. So if you want to avoid ugly (and slow) reflection, do what I recommended in the other answers and write two distinct advices, factoring out the common code into a helper method if avoiding code duplication is your concern here. Something like this (untested, just to give you an idea about the code structure):

@Around(&quot;@within(annotation)&quot;)
public Object classIsAdmin(ProceedingJoinPoint pjp, IsAdmin annotation) throws Throwable {
  return commonIsAdmin(pjp, annotation);
}

@Around(&quot;@annotation(annotation)&quot;)
public Object methodIsAdmin(ProceedingJoinPoint pjp, IsAdmin annotation) throws Throwable {
  return commonIsAdmin(pjp, annotation);
}

public Object commonIsAdmin(ProceedingJoinPoint pjp, IsAdmin annotation) throws Throwable {
  // Here you would place all common advice code.
  // Non-common code could remain in the original advice methods.
  log.info(&quot;value-&gt;&gt;{}&quot;, annotation.value());
  return pjp.proceed();
}

> why I need to change the response value from void to Object and return pjp.proceed(). If the method do not have response, the request will freezing.

In contrast to a @Before or @After advice, in an @Around advice you can modify the return value by either skipping the target method execution completely by not calling proceed() or by discarding or modifying the result of proceed(). You are completely free in what you want to return, it just has to match the target method's return type.

Having said that, it should become clear that an around advice method also must have a return type matching that of the target method(s) it intercepts. It can be an exact type like MyType, a super type or simply Object (super type for all types) if your advice targets a multitude of types without a common super type. The advice can also have a return type of void if (and only if) all target methods also return void (otherwise the advice just would not match those methods, even if the pointcut as such would match).

So if an around advice matches is determined by a combination of the pointcut itself and the return type. You can use that as a tool to limit pointcut matching by defining a specific return type (to which you then would need to cast the return value of proceed() because proceed() always returns an Object).

BTW, if the target method returns a primitive type like int, boolean etc., then the advice would auto-wrap the result to be an Integer or Boolean.

You really ought to read Spring AOP and AspectJ manuals or tutorials because I am explaining things here which can be found there.


Update: The OP asked for documentation concerning parameter binding and a description how names are determined:

  • You can specify an argNames parameter for all advice types, e.g. @Before, @After, @Around.
  • If that annotation parameter is absent, Spring AOP will try to match advice method parameter names via class file debug info, if compiled in. Otherwise matching would fail in this case.
  • When using full AspectJ with compile-time weaving instead of Spring AOP, determining names also works without debug info because the AspectJ compiler can determine the necessary information during compilation.

All of this is described in the Spring AOP manual.

答案2

得分: 0

通过反射获取类,通过类获取固定方法信息,并获取方法上的注解。这样,您应该能够实现反射操作。

英文:

Obtain the class through reflection, obtain the fixed method information through the class, and get the annotation on the method. In this way, you should be able to reflect

huangapple
  • 本文由 发表于 2020年8月24日 00:40:35
  • 转载请务必保留本文链接:https://go.coder-hub.com/63549524.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定