简体   繁体   English

AspectJ / AOP:如何通过类型“get-field”等限制编织?

[英]AspectJ / AOP : how to limit the weaving by type "get-field", etc.?

What condition should be fixed so that weaving occurs only for "method-execution"?应该固定什么条件以使编织仅发生在“方法执行”中? For constructors, fields, etc. ("field-set", "field-get"...) it is not necessary.对于构造函数、字段等(“field-set”、“field-get”...),这不是必需的。

@Pointcut("execution(* com.a.b.repository.c..*(..)) || 
@annotation(org.springframework.data.jpa.repository.Query) || 
target(org.springframework.data.jpa.repository.JpaRepository)")
public void executeARepository() { }

@Pointcut("call(* org.jooq.DSLContext.*(..))")
public void executeJOOQ() { }

@Pointcut("execution(* a.b.c.controller..*(..)) || execution(* a.b.c.d.controller..*(..))")
public void executeAControllers() { }

Before I answer the actual question, I want to comment on this recommendation:在回答实际问题之前,我想评论一下这个建议:

why not create an annotation for the aop, and add that to the method(s) you want it to apply on?为什么不为 aop 创建一个注释,并将其添加到您希望它应用的方法中? Stultuske斯图图斯克

I think this is a particularly bad idea in general, because it causes one of the very problems AOP was invented to avoid: scattering a cross-cutting concern across a vast number of classes in a code base.我认为这通常是一个特别糟糕的主意,因为它会导致 AOP 被发明要避免的问题之一:将横切关注点分散到代码库中的大量类中。 That this rather naive approach to AOP is so common, does not make it any better.这种相当幼稚的 AOP 方法如此普遍,并没有让它变得更好。 I like to think of it as "poor man's AOP".我喜欢将其视为“穷人的 AOP”。

If you annotate each method (or even just each class) you want to apply an aspect upon, it means that you are polluting your code base and breaking aspect encapsulation.如果您注释了要应用方面的每个方法(甚至只是每个类),这意味着您正在污染代码库并破坏方面封装。 Of course, that approach would work and still be better than scattering duplicate code implementing the cross-cutting concern, but still it is suboptimal and - most of all - a maintenance nightmare: You add a new class, forget the annotation, and the aspect will not be applied, which you might not even notice right away if it is just one of several dozen or hundred classes.当然,这种方法会起作用,并且仍然比分散实现横切关注点的重复代码更好,但它仍然不是最理想的,而且 - 最重要的是 - 维护噩梦:您添加一个新的 class,忘记注释和方面不会被应用,如果它只是几十个或几百个类中的一个,你甚至可能不会马上注意到。 It will be hidden in the noise.它会隐藏在噪音中。

What happens more often?更经常发生什么? That you add a new class or method (approximately every day) or that you need to adjust an aspect's pointcut, because you need to consider another base package or naming pattern or add an exceptional case (maybe twice a year)?您添加一个新的 class 或方法(大约每天),或者您需要调整方面的切入点,因为您需要考虑另一个基本 package 或命名模式或添加一个例外情况(可能一年两次)?

Compare the method-annotating approach to cleanly encapsulating the matching logic inside the aspect and maintaining the pointcut if you make a significant change to the code base.如果您对代码库进行重大更改,则比较方法注释方法以将匹配逻辑清晰地封装在方面内并维护切入点。 I would always opt for the latter, if possible.如果可能的话,我总是选择后者。 Scattering a multitude of annotations across the code base is just a last resort to me, if the pointcuts would be so convoluted and hard to maintain that the annotations would be the lesser evil.如果切入点如此复杂且难以维护,那么在代码库中散布大量注释对我来说只是最后的手段,以至于注释的危害较小。 Of course, you can target annotations if they are used for other purposes anyway, eg Spring's @Controller or whatever.当然,如果注释用于其他目的,您可以定位注释,例如 Spring 的@Controller或其他。 Then it would not mean to additionally pollute the code base.那么这并不意味着额外污染代码库。


Having gotten that topic off my chest, now let me help you answer the actual question: If you experience advice code firing for joinpoints other than method executions, probably (some part of) your pointcut is too broad.摆脱了这个话题,现在让我来帮助您回答实际问题:如果您遇到除方法执行之外的连接点的建议代码触发,可能(某些部分)您的切入点过于宽泛。 So let us check the pointcuts at hand:所以让我们检查一下手头的切入点:

execution(* a.b.c.controller..*(..)) ||
execution(* a.b.c.d.controller..*(..))

No problem here, both OR-connected pointcuts only target method executions.这里没问题,两个 OR 连接的切入点都只针对方法执行。

call(* org.jooq.DSLContext.*(..))

I am not sure why here you use call instead of execution - maybe because JOOQ is an external dependency and you want to avoid binary weaving for the third-party classes and rather weave each caller via call , as opposed to weaving the callee at the execution site, but having to configure binary weaving for that.我不知道为什么在这里你使用call而不是execution - 可能是因为 JOOQ 是一个外部依赖项,你想避免第三方类的二进制编织,而是通过call编织每个调用者,而不是在execution时编织被调用者站点,但必须为此配置二进制编织。 This is OK, no problem with weaving constructors or field read/write operations.这没关系,编织构造函数或字段读/写操作没有问题。

execution(* com.a.b.repository.c..*(..)) || 
@annotation(org.springframework.data.jpa.repository.Query) || 
target(org.springframework.data.jpa.repository.JpaRepository)

The execution pointcut is OK. execution切入点没问题。 The @annotation pointcut will probably fire twice per method, once for method call and once for method execution, unless of course the Spring classes are out of scope for compile-time weaving. @annotation切入点可能会为每个方法触发两次,一次用于方法调用,一次用于方法执行,当然,除非 Spring 类不在 scope 用于编译时编织。

Now the target pointcut is the culprit for native AspectJ (not talking about Spring AOP here), because if fires for all joinpoints in JpaRepository instances, including constructors and field accessors.现在target切入点是原生 AspectJ 的罪魁祸首(这里不讨论 Spring AOP),因为 if 会触发JpaRepository实例中的所有连接点,包括构造函数和字段访问器。 This is easy to fix by limiting matching for this sub-pointcut to method executions, eg like this:这很容易通过将此子切入点的匹配限制为方法执行来解决,例如:

execution(* com.a.b.repository.c..*(..)) || 
@annotation(org.springframework.data.jpa.repository.Query) || 
target(org.springframework.data.jpa.repository.JpaRepository) && execution(* *(..))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM