Spring AOP: Advice on annotation used over another advice - java

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.

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

SpringBoot application fails startup when I have Aspect defined on a Bean Method

Working with Springboot 2.7.0. I had a a working application and I made these changes on top of it
Aspect Configuration
#Configuration
#EnableAspectJAutoProxy
#ComponentScan
public class AspectConfig {}
Aspect Interface
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface Timed { }
Aspect Class to Measure method execution time
#Around("#annotation(Timed)")
public Object measureExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
Object proceed = joinPoint.proceed();
LOG.info("Time taken for {} is {} ms, joinPoint.getSignature(), System.currentTimeMillis() - start,);
return proceed;
}
Added the new #Timed annotation to an existing method in a bean (omitting non relevant code)
#Component
#ConditionalOnExpression("${oauth.enabled}")
public class JwtAuthFilter extends OncePerRequestFilter {
#Timed
public boolean verifySignatureAndExpiry(String bearerToken){
// method logic
}
}
This causes the Springboot application to fail startup.
I can get it to start if I add #Aspect to the JwtAuthFilter class.
but why would I need to do that? It makes the #Timed annotation limited use if I have to annotate every class that needs to use it with #Aspect. Not to mention, though there are no errors, the functionality won't work because an Aspect cannot work on another Aspect.
#Timed works on my controller method though.
#RestController
#RequestMapping(value = "/api/v1", produces = MediaType.APPLICATION_JSON_VALUE)
public class HealthController {
#GetMapping("/health")
#Timed
public Map<String, String> health(){
return Map.of("status", "up");
}
}
This causes the Spring Boot application to fail startup.
You should always post error messages and relevant stack traces, not just say "fails to start up". You are lucky that in this case, I remember the situation, so I can answer your question. Normally, I would be unable to do so without further information.
I can get it to start if I add #Aspect to the JwtAuthFilter class.
That does not make any sense. Why would you add #Aspect to something which is not an aspect? Of course, it makes the start-up error go away, but it also makes your real aspect not fire, because one Spring AOP aspect cannot advise another one, as you already mentioned. Therefore, this approach is - with all due respect - complete nonsense.
The reason for the exception is: You cannot advise your filter by Spring AOP, because it is derived from GenericFilterBean, which has some final methods. Final methods cannot be overriden, therefore not be proxied either. This has the effect of those methods being called upon the proxy instance directly instead of being delegated to the target object, i.e. if such a method accesses an instance field, it shall find it uninitialised, because the proxy's fields are not meant to be initialised, only the target object's ones. See also my answer here for more info.
In this case, final method org.springframework.web.filter.GenericFilterBean#init is trying to access this.logger, which leads to the NPE which makes Spring Boot's Tomcat fail to start up. This has been reported and briefly explained in this comment in Spring issue #27963, which has been closed as invalid.
#Timed works on my controller method though.
Yes, because your controller does not have a problem with accessing an instance field from a final method.
If you absolutely think that you need to measure your filter method's execution time from an aspect, you can switch from Spring AOP to native AspectJ, either for the whole project via load-time weaving or selectively for some target classes via compile-time weaving. I have tried locally, it works with the right pointcut. Then you can also advise your filter. FYI, the pointcut would be something like:
// Annotated class
#Around("execution(* *(..)) && !within(MyAspect) && #target(Timed)")
// Annotated method
#Around("execution(* *(..)) && !within(MyAspect) && #annotation(Timed)")
AspectJ is more powerful than Spring AOP, so you explicitly need to limit matching to method executions, otherwise other joinpoints such as method calls, constructor calls and others would be affected, too. You also need to make sure that the aspect does not advise itself or other aspects, which is perfectly possible in AspectJ.

Pointcut targeting a third party JAR class is not triggered

As a temporary fix of the bug https://github.com/spring-projects/spring-hateoas/issues/220, I would like modify the return value of org.springframework.hateoas.core.AnnotationMappingDiscoverer.getMapping methods so that I can resolve placeholders manually. Here is the aspect I tried:
<aop:aspectj-autoproxy />
#Component
#Aspect
public class AnnotationMappingDiscovererFix {
#Around("execution(* org.springframework.hateoas.core.AnnotationMappingDiscoverer.getMapping(..))")
public Object resolvePlaceholders(ProceedingJoinPoint joinPoint) throws Throwable {
Object mapping = joinPoint.proceed();
// resolve placeholders manually...
return mapping;
}
}
But this pointcut gets never triggered, any idea why?
With proxy-based Spring AOP you can only target Spring beans/components. I am not a Spring user, so I do not know for sure, but I do not think that you can actually intercept Spring framework classes via its own AOP framework due to "hen vs. egg" bootstrapping problems.
But if you use full AspectJ via load-time weaving (LTW), you should be able to achieve what you want because the AspectJ weaving agent (aspectjweaver.jar) is loaded before the Spring classes and thus can modify them during classloading phase. The Spring documentation explains how to use AspectJ in connection with Spring.

Spring - Multiple Aspects Firing Out of Order

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

Customised annotation in spring

I have seen few examples where customized annotations were used. example
#SimpleAnnotation
class SampleBean {
#SimpleAnnotation
public String getName() {
return "AAA";
}
public void setName(String name) {
}
public int getHeight() {
return 201;
}
}
#Target( { ElementType.METHOD, ElementType.TYPE })
#Retention(RetentionPolicy.RUNTIME)
#interface SimpleAnnotation {
}
Can anyone tell why we use this?
Spring supports for many Annotation the concept of "meta-annotation". (I am not sure if it is for all.)
This mean that you can build your own annotation and annotate the annotation with one of springs "core" annotations.
For example:
#Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE })
#Retention(RetentionPolicy.RUNTIME)
#Service
public #interface MyService {
}
Then you can use this annotation instead of #Service. (Btw: #Service, #Repository, #Controller use the same technique to "inherit" from #Component)
One example that make heavy use of this is "inherit" from #Qualifier.
For an example and some explanation have a look at Spring Reference Chapter: 3.9.3 Fine-tuning annotation-based autowiring with qualifiers (The Example with #Genre is at the end of the chapter.)
One very usefull construct that can be done with that technique is, that it enables you to combine several Annotations to a (in your use case) more meaning full. So instead of writing at every class of some type allways the same two annotations, for example: #Service and #Qualifiyer("someting") (the org.springframework.beans.factory.annotation.Qualifier). You can create your custom annotation that is annotated with this two annotations, and then use in your beans only this one custom annotation. (#See Avoid Spring Annotation Code Smell Use Spring 3 Custom Annotations)
If you want to see how powerfull this technique can be use, you can have a look at Context and Dependency Injection Framework.
Question from the comment:
The #interface also has some variables defined inside it, what does that signify?
The Annotations (defined by #Interface) work a bit like beans. This Fields are the properties that can/must be define if you use the annotations. The values can be later on be read via reflection API.
For example the #Controller Annotation in Spring:
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Component
public #interface Controller {
String value() default "";
}
The field with name value is that field that can be used without explicit name it: (#Controller("myUrl") is the same #Controller(value="myUrl"))
You can create your own meta-annotations that collect several other Spring annotations to reduce meta-boilerplate in your code:
#Service
#Scope(value = "prototype")
#Transactional(readOnly = true, rollbackFor = RuntimeException.class)
public #interface ReadOnlyService {}
And then you can simply write:
#ReadOnlyService
public class RoleService {
}
Spring will find the #ReadOnlyService and semantically replace it with:
#Service
#Scope(value = "prototype")
#Transactional(readOnly = true, rollbackFor = RuntimeException.class)
public class RoleService {
}
Of course having custom annotations pays of when you have tons of services annotated with the same set of Spring annotations that can be replaced with one, well named annotation.
Examples taken from: Avoid Spring Annotation Code Smell: Use Spring 3 Custom Annotations
Custom annotations do not do anything on their own. They are simple markers in code. Their real power comes from tools that look for specific annotations. Like some of the other answers mention, Spring has several uses for annotations and now mechanisms for defining your own component types. Pretty neat. Another example, a few weeks ago I used AOP and a few custom annotations to provide simple method level result caching. Now that I have the caching engine in place, and the appropriate AOP hooks defined, if I want to cache a method, I simply add that annotation. Some people simply use the annotations as fancy metadata to improve readability.
At the end of the day, they are a fairly simple tool that you can use for a great number of things.
The best part of using custom annotations is that you don't have to make any configuration, Spring will auto detect that these beans are service components and everything will work fine. Custom Annotations are a very small feature added in Spring but are very useful.For details take a look at this
http://java.dzone.com/articles/avoid-spring-annotation-code-smell-use-spring3-custom-annotations
Two options:
you need the #Component annotation on your custom annotation. That way you can use your custom annotation to mark classes as beans. In addition, you can add a default scope and other meta-information
qualifiers - you can use qualifier annotations (annotated with the #Qualifier meta-annotation) to distinguish between implementations of the same interface.
A common pattern is also to use annotations in AOP pointcuts. Not specifically Spring, but often employed when making use of Spring AOP.

Categories