Spring - Multiple Aspects Firing Out of Order - java

I am having trouble getting multiple aspects to fire in a specific order. I am using the RequestProcessor to do certain things on every incoming request on my controllers, which have a certain parameter
Then I have some specific annotations that I will be adding to only certain methods within my controllers.
FYI I am using Eclipse, Tomcat, Maven and spring with java/annotation based configs. I use Tomcat and the WebApplicationInitializer to load my context, dispatcher, listeners etc. I have no web.xml. I can post that or the pom.xml if needed too.
The problem I am getting is that a method that satisfies both the ProcessRequest pointcut and the someAnnotation pointcut is firing the someAnnotation method first even though the order is specified to fire ProcessRequest first. There is some properties set in the ProcessRequest that is needed in some other annotations.
Here is a simplified version of my code.
Spring Config Class
#Configuration // Enable Spring Annotation Configuration. Equivalent to <context:annotation-config/>
#EnableAspectJAutoProxy
#EnableCaching // Enable Spring caching
#EnableWebMvc // Enable Spring MVC Annotation. Equivalent to <mvc:annotation-driven />.
#ComponentScan(basePackages = {"xxx.yyy.zzz"}) // Scan for Spring Components. Equivalent to <context:component-scan>
public class WebAppConfig extends WebMvcConfigurerAdapter {
// Other Bean logic here
#Bean
public RequestProcessor requestProcessor() {
return new RequestProcessor();
}
#Bean
public AnnotationAspect annotationAspect() {
return new AnnotationAspect();
}
}
Aspect #1
#Aspect
#Order(0)
public class RequestProcessor {
#Pointcut("execution(* xxx.yyy.zzz.api..*.*(xxx.yyy.zzz.objects.api.Request,..)) && args(request)")
public void pointcut(Request<?> request) {}
#Before("pointcut(request)")
public void processRequest(Request<?> request) throws IOException, BadSignatureException {
// Some logic here that is independent of other and needs to run before other aspect which references annotation
}
}
Aspect #2
#Aspect
#Order(1)
public class AnnotationAspect {
#Before("#annotation(xxx.yyy.zzz.annotation.SomeAnnotation)")
public void someAnnotation() {
// Log for this annotation
}
// Some other annotation methods here
}
Also tried this format implements Ordered
#Aspect
public class RequestProcessor implements Ordered {
#Override
public int getOrder() {
return 0;
}
#Pointcut("execution(* xxx.yyy.zzz.api..*.*(xxx.yyy.zzz.objects.api.Request,..)) && args(request)")
public void pointcut(Request<?> request) {}
#Before("pointcut(request)")
public void processRequest(Request<?> request) throws IOException, BadSignatureException {
// Some logic here that is independent of other and needs to run before other aspect which references annotation
}
}
I read over this post and some others but couldn't find anything relevant that worked.
Ordering aspects with Spring AOP && MVC
****UPDATE****
So I have been reading through the AspectJ docs on declaring precedence so I thought I would give it a whirl. I created a simple aspect that only declares precedence and it works just fine.
Here is my Precedence Aspect:
public aspect AspectPrecedence {
declare precedence : RequestProcessor, SomeAspect;
}
I am not going to submit this as answer just yet because I would like to understand why the annotation or "implements Ordered" are not functioning properly in my project.
Any insight would be greatly appreciated. Thanks!
****UPDATE 2****
For reference this works locally in my eclipse environment and seemed to work when deployed to AWS via WAR file.
#Aspect
#DeclarePrecedence("RequestProcessor, SomeAspect")
public class RequestProcessor {
#Pointcut("execution(* xxx.yyy.zzz.api..*.*(xxx.yyy.zzz.objects.api.Request,..)) && args(request)")
public void pointcut(Request<?> request) {}
#Before("pointcut(request)")
public void processRequest(Request<?> request) throws IOException, BadSignatureException {
// Some logic here that is independent of other and needs to run before other aspect which references annotation
}
}

When using Eclipse with AspectJ support that automatically enables compile time weaving in your IDE. Which means the aspects get woven into your byte code, opposed to Spring which uses proxies to apply aspects.
Using an aspect to declare precedence or using #DeclarePrecedence will only work when using either compile or load time weaving (the latter can be enabled by specifying <context:load-time-weaver/> depending on your container might require some additional configuration). However both should work (you might need to specify the AspectJ compiler as a compiler for the #Aspect classes instead of the default java compiler).
#Order and Ordered only work for the proxy based solution and are ignored by AspectJ.

I think the problem could be that you're using LTW with AspectJ instead AOP with Spring, as #Order is defined for Spring, the container(AspectJ) is not able to determine the ordering of both the advices, so try one of these:
Try flipping the order of #Aspect and #Order annotations
You can try the annotation #DeclarePrecedence from AspectJ and then configuring your aspects into the aop.xml
<aspects>
<aspect name="your.package.RequestProcessor"/>
<aspect name="your.package.AnnotationAspect"/>
<concrete-aspect name="my_aspect_configuration_precedence"
precedence="*..*RequestProcessor, *"/>
</aspects>
I don't know if it's going to work but expect to give you a pointer on that

Related

How to enforce Aspect implementation is loaded when its corresponding annotation is used?

I have a created an annotation that verifies whether certain security aspects are correct.
For example, #RequireClientCertificate, with an Aspect implementation RequireClientCertificateAspect that verifies whether the correct HTTP header is indeed passed in to the Spring REST controller.
This works totally fine, IF the RequireClientCertificateAspect is actually loaded, i.e. if its package is mentioned somewhere in #ComponentScan().
However, if someone forgets to add this package to #ComponentScan, or the aspect is moved to another package, or someone (accidentally) removes the package from #ComponentScan, the aspect bean isn't loaded, and the aspect is completely not applied.
I have this annotation in a common library, shared by several microservices, so it's easy for one of the microservices to accidentally get it wrong. In that case, no checking of the client certificate would be performed.
Question: How can I enforce that, if the #RequireClientCertificate annotation is used, its corresponding Aspect implementation is also loaded?
Simplified usage example:
#Controller
#RequestMapping(value = "/v1.0", produces = MediaType.APPLICATION_JSON_VALUE)
#RequireClientCertificate
public class SomeApiController {
#ResponseBody
#PostMapping("/get-token/")
public ResponseEntity<Token> getToken() {
return ResponseEntity.ok(...get token...);
}
}
Simplified version of the aspect:
#Aspect
#Component
public class RequireClientCertificateAspect {
#Around("execution(* (#RequireClientCertificate *).*(..))")
public Object requireClientCertificateAspectImplementation(ProceedingJoinPoint joinPoint) throws Throwable {
... verify request header ...
try {
return joinPoint.proceed();
finally {
... some other things I need to check ...
}
}
}
Things I've tried/considered:
I can detect 'usage' of the annotation by adding a static field with an initializer to the interface. For example:
#Target({ElementType.TYPE, ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
public #interface RestFactoryGatewaySecurityContext {
static public final boolean dummy = SomeClass.checkAspectIsLoaded();
}
However, such initializers are called very early, and I don't think Spring DI is 'up and running' far enough at that stage that I could even reliably determine whether the aspect bean is loaded.
Another option is to use #Autowired to inject the RequireClientCertificateAspect bean on the main app class explicitly. If somehow the bean isn't on the component scan, this will prevent Spring from instantiating the app.
So that does work, but requires someone to explicitly add this 'dummy' autowire, which in itself is easy to forget, in addition to being a bit 'ugly'.
If you use spring boot you can create your own starter.
Create file META-INF/spring.factories:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.example.MyCustomConfiguration
Then just add any validation you want to your configuration
#Configuration
public class MyCustomConfiguration{
}
You can #Autowired your RequireClientCertificateAspect into it, which will cause error if it isn't defined.
You can create method with #PostConstruct and do any validation you want.
If you went so far as creating custom starter, you can just initialize your bewns there.
More about it you can read here

AspectJ, SpringAOP, Aspect runs twice

My aspect runs twice and I don't see a reason why. Maybe someone could point me on my mistake?
Here is a code:
Creating annotation for pointcut
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD})
public #interface CustodianMetrics {
String metricId();
}
Creating an aspect
#Aspect
#Component("custodianMetricsAspect")
public class CustodianMetricsAspect {
private final MonitoringService monitoringService;
#Autowired
public CustodianMetricsAspect(MonitoringService monitoringService) {
this.monitoringService = monitoringService;
}
#After("#annotation(custodianMetricsAnnotation)")
public void count(CustodianMetrics custodianMetricsAnnotation) {
Counter metric = monitoringService.metric(custodianMetricsAnnotation.metricId(), Counter.class);
metric.inc();
}
}
Configuring xml for spring
<aop:aspectj-autoproxy>
<aop:include name="path"/>
</aop:aspectj-autoproxy>
<aop:config>
<aop:aspect id="custodianMetricsAspect" ref="custodianMetricsAspect">
<aop:after method="count"
pointcut="#annotation(custodianMetricsAnnotation)"/>
</aop:aspect>
</aop:config>
I tried to change poitcut on this
#After("#annotation(custodianMetricsAnnotation) && execution(* *(..))")
But the same result - aspect runs twice. Any suggestions?
It happens because you have configured the aspect twice - both Spring XML configuration and the #Aspect annotation.
Read the note in section 8.1.2 Spring AOP capabilities and goals of Spring framework documentation, it states the following:
One such choice that is relevant to this chapter is that of which AOP framework (and which AOP style) to choose. You have the choice of AspectJ and/or Spring AOP, and you also have the choice of either the #AspectJ annotation-style approach or the Spring XML configuration-style approach.
In this case, based on my personal experience, I highly recommend you to stick with the annotations. However, it depends on your personal taste. You may find 8.4 Choosing which AOP declaration style to use relevant.
Edit:
If you choose the annotation-based configuration, don't forget to create a Java configuration class instead of deleted <aop:aspectj-autoproxy>... line.
#Configuration
#EnableAspectJAutoProxy
public class AspectJAutoProxyConfiguration { }

AOP can't pointcut class without #Service #Controller

AOP can pointcut like #Controller or #Service.
But it doesn't work well on Class without spring's annotation.
package com.erp.module;
#Slf4j
public class SalesOrderModule {
public void cancel(){
log.info("test");
}
public static SalesOrderModule init(int type) {
SalesOrderModule salesOrderModule = new SalesOrderModule(salesOrder);
*****
return salesOrderModule;
My Aspect.java like below
#Aspect
#Component
#Slf4j
public class WebLogAspect {
#Pointcut("execution(public * com.erp.controller.*.*(..)) || execution(public * com.erp.module.*.*(..))")
public void logPointCut() {
}
I invoke cancel like below:
SalesOrderModule so = SalesOrderModule.init(3);
so.cancel();
I know this problem is relative to proxy. But how can I
Spring Docs says:
Thus, for example, the Spring Framework’s AOP functionality is normally used in conjunction with the Spring IoC container. Aspects are configured using normal bean definition syntax (although this allows powerful "autoproxying" capabilities): this is a crucial difference from other AOP implementations. There are some things you cannot do easily or efficiently with Spring AOP, such as advise very fine-grained objects (such as domain objects typically): AspectJ is the best choice in such cases. However, our experience is that Spring AOP provides an excellent solution to most problems in enterprise Java applications that are amenable to AOP.
So if you need to intercept non-spring managed code, you need to use AspectJ instead of Spring AOP. TBH, I didn't need that so far.

Spring AOP: Advice on annotation used over another advice

I have created my custom annotation:
#Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface Condition {
String value();
}
I want to use this annotation to determine whether or not to run advice, my try:
#Condition("some.config")
#Around("execution(public * someMethod())")
Object doSomething(ProceedingJoinPoint joinPoint) throws Throwable {
// some logic here
}
#Around("#annotation(condition)")
Object checkCondition(ProceedingJoinPoint joinPoint, Condition condition) throws Throwable {
String property = (String) configuration.getProperty(condition.value());
if (Boolean.valueOf(property)){
return joinPoint.proceed();
} else {
return null;
}
}
It works when I use #Condition on some other methods, i.e. the checkCondition is applied and then the method is executed or not based on config value. For advice doSomething it doesn't get applied though.
You said your aspect works for other components, just not the aspect itself. From this statement I gather that
your aspect is wired correctly (e.g. annotated with #Component and detected by component scan or wired manually via XML config) and
you use proxy-based Spring AOP.
In (2) is the source of your problem. According to the Spring manual aspects themselves are exempt from being aspect targets themselves:
Advising aspects with other aspects?
In Spring AOP, it is not possible to have aspects themselves be the target of advice from other aspects. The #Aspect annotation on a class marks it as an aspect, and hence excludes it from auto-proxying.
So M. Prokhorov is somewhat wrong when saying that aspects are not (or cannot be) Spring components, but he is right insofar as by design you cannot self-advise an aspect or advise other aspects. His assumption that it may work with AspectJ is also correct. It does work with AspectJ, so if you need it to you can configure Spring to use AspectJ via LTW instead of Spring AOP for this case.

Spring AOP: Annotation pointcut not causing advice to execute

I am using Spring AOP (with AspectJ annotation style support) and want to execute code if a method is annotated with a specific annotation (WsTransaction).
Here is my aspect:
#Aspect
#Component
public class ExampleAspect {
#Pointcut("execution(* example.*.ws.*.*(..))")
public void isWebService() {}
#Pointcut("#annotation(example.common.ws.WsTransaction)")
public void isAnnotated() {}
#Before("isWebService() && isAnnotated()")
public void before() {
System.out.println("before called");
}
}
This is an example class where I expect it to run:
package example.common.ws;
#Endpoint
public class SomeEndpoint {
#WsTransaction() // I want advice to execute if this annotation present
#PayloadRoot(localPart = "SomeRequest", namespace = "http://example/common/ws/")
public SomeResponse methodToBeCalled(SomeRequest request) {
// Do stuff
return someResponse;
}
}
When I change #Before to only use isWebService() it is called but when I try it with isWebService() && isAnnotated() or just isAnnotated() nothing seems to happen.
I have <aop:aspectj-autoproxy/> in my Spring config.
The endpoint is created by Spring (using component-scan).
The annotation's retention policy is runtime.
Spring version is 3.0.3.RELEASE
I am not sure what is wrong or what I can try to debug.
Update: It seems Spring AOP doesn't pickup #Endpoint annotated classes
Update 2: AopUtils.isAopProxy(this) and AopUtils.isCglibProxy(this) are both false (Even when using <aop:aspectj-autoproxy proxy-target-class="true"/>)
Firstly I had to use <aop:aspectj-autoproxy proxy-target-class="true"/> to use class-based (CGLIB) proxies (instead of Java interface-based proxies).
Secondly (and this is where I got stuck) I had to specify the above in the contextConfigLocation of the servlet handling the SOAP requests (MessageDispatcherServlet) instead of the root application context.
I guess there may be some issue with the pointcut declaration.
#Pointcut("#annotation(example.common.ws.WsTransaction)")
See this link for possible solution

Categories