The idea is to create annotations hierarchy (similar to #Service, #Component etc) using #AliasFor annotation. This should give me the possibility to define aspect, that would execute on parent annotation, and every alias of it. But somehow it doesn't work for me.
#ComponentScan is fine, #EnableAspectJAutoProxy is set.
Example:
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.TYPE, ElementType.METHOD})
public #interface ParentAnnotation {
}
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
#ParentAnnotation
public #interface ChildAnnotation {
#AliasFor(annotation = ParentAnnotation.class)
String value() default "";
}
#Aspect
#Component
public class EventRecorderAspect {
#Around("#annotation(com.example.ParentAnnotation)")
public void exampleMethod(ProceedingJoinPoint joinPoint) throws Throwable {
// This should be executed for both #ParentAnnotation and #ChildAnnotation
}
}
#RestController
public class ExampleController {
#ChildAnnotation // This should result in executing aspect for every implementation
String controllerMethod();
}
UPDATE:
I've updated code, as #M.Deinum suggested in a comment below. But it still doesnt work.
AspectJ pointcut matching syntax, a subset of which is used by Spring AOP, is ignorant of meta annotations, even though withing the Spring framework as such there is support for it in other places. I think the closest you can get to specifying meta annotations in your pointcut is to do it explicitly up to the nesting level you require. See this answer for examples showing the syntax variants for both
class-level, e.g. within(#(#com.example.ParentAnnotation *) *),
method-level, e.g. execution(#(#com.example.ParentAnnotation *) * *(..))
annotations.
Update: #Ariel Grabijas asked in a follow-up comment:
So is there a way to somehow inherit annotations from interface method to class method?
Not in Java and also not by means of Spring AOP. But there is a workaround using native AspectJ inter-type definitions (ITD).
Related
I am wondering, why Spring's implementation of the #Component Annotation has RetentionPolicy.RUNTIME.
Here's the official implementation from github
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Indexed
public #interface Component {
String value() default "";
}
In my thought, the scanning for all Spring-Components annotated with #Component would be done at compile-time and all the results (annotated classes) would somehow be stored inside the ApplicationContext. I guess I am wrong, but why does this annotation need to be RetentionPolicy.Runtime?
The component scanning is done at the startup, not at the compilation.
Also, the aspects (AOP) are created at the runtime
I am trying to add some pre-processing logic using AspectJ in my Spring MVC project. I have a #Before method and the corresponding PointCut in place. However, this method is not getting invoked at all when I invoke any of the methods matching the PointCut.
Here are my classes:
#Aspect
#Configuration
public class ConcurrencyAspectConfig {
#Before("execution(public * com.test.wms.service.dto.PackingDtoApi.*(..))")
public void adviceMethod(JoinPoint joinPoint) {
System.out.println("**** ASPECT START");
}
}
#EnableAspectJAutoProxy
#Configuration
#ComponentScan({SpringConstants.PACKAGE_SPRING})
public class AppConfig {
}
When executing any of the public methods in PackingDtoApi, I am expecting to see the **** ASPECT START message on the console. However, it seems that the Advice method never gets invoked. What could be the reason?
There could be mainly two reasons for your implementation to not work:
Wrong Pointcut expression - Double check your pointcut expression
PackingDtoApi is not a spring bean - Annotate the class with a stereotype annotation like #Component
Note: If this doesn't solve your issue, please share the code for
PackingDtoApi class
I have some common interface for refactoring logic in my project. It looks about like this:
public interface RefactorAwareEntryPoint {
default boolean doRefactor() {
if (EventLogService.wasEvent(getEventType())) {
return true;
}
boolean result = doRefactorInternal();
if (result) {
EventLogService.registerEvent(eventType);
}
return result;
}
String getEventType();
boolean doRefactorInternal();
}
And than, when I need to write some refactoring - I implement this interface with methods, mark class like #Component, and Spring in loop evaluate each interface implementation and register it in database.
But we have a lot of refactors (every year - 200-300 new). It's hard to disable old implementations manualy, and we have a lot of beans in our spring-context.
Can we do something, for example, use some annotation - which will disable component creation by some condition?
For example:
#Component
#Enabled(YEAR.2020)
public class CustomRefactor implements RefactorAwareEntryPoint {
// Code implementation
}
And this annotation will work like this (a pseudocode):
if (YEAR.2020) {
create bean -> new CustomRefactor()
}
And when it will be YEAR.2021 - we will have no beans from YEAR.2020 in spring-context.
Use the annotation #Profile that makes application configuration and beans available in certain environments.
You can find more at Spring Boot 2.4.0 reference documentation: 3. Profiles
Spring Profiles provide a way to segregate parts of your application configuration and make it be available only in certain environments. Any #Component, #Configuration or #ConfigurationProperties can be marked with #Profile to limit when it is loaded
Consider each year as a separate environment.
#Component
#Profile("2020")
public class CustomRefactor2020 implements RefactorAwareEntryPoint {
// Code implementation
}
#Component
#Profile("2021")
public class CustomRefactor2021 implements RefactorAwareEntryPoint {
// Code implementation
}
In addition to the answers provided by our colleagues, consider the feature of spring called "Stereotype annotations". This is how well-known annotations like #Service are defined in spring.
In general, the fact that you mark your class with #Component annotation allows you to load the class as a spring bean because the annotated class becomes a subject to a process called "component scanning" - a process happens when you start the application context.
Since spring 4 there is a conditional interface that basically makes possible implementing a logic similar to what you refer to as #Enabled(YEAR.2020).
You might use a built-in "#ConditionalOnProperty" to map the 2020 year to property or even implement a custom conditional logic. I'll assume that you've implemented a custom conditional as #ConditionalOnYear
Now, what's interesting (and this is a "stereotype" feature that I've mentioned at the beginning of the post) is that you may create your own "component" annotation with a custom "conditional" logic and use it "as if" its a regular bean:
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#ConditionalOnYear(2020)
#Component
public #interface Year2020OnlyComponent {
#AliasFor(annotation = Component.class)
String value() default "";
}
#Year2020OnlyComponent
public class CustomRefactor implements RefactorAwareEntryPoint {
// Code implementation
}
You can also improve that by clever usage of #AliasFor annotation to be something like:
#SinceYearComponent(2020)
public class CustomRefactor implements RefactorAwareEntryPoint {
// Code implementation
}
But this is kind of out of scope for this question - so I just mention a direction here.
Of course, it's possible to merely use two annotations as you've suggested even without this "Stereotype" annotation feature:
#Component
#SinceYear(2020) // a custom conditional
public class CustomRefactor implements RefactorAwareEntryPoint {
// Code implementation
}
Check out the BeanFactoryPostprocessor interface. Probably you can remove a bean before it‘s creation.
Else you might implement your own BeanFactory and create the ApplicationContext with your implementation.
You can use excludeFilter annotations provided by spring boot .
As mentioned by others you can always use #Profile annotation to
enable/disable profiles.
Another option is excludeFilter
I'm practising with customs annotations and I want to create a custom annotation that set a Math.random() value to an #Entity field (I know that I can do this in the constructor but I want to do with an annotation)
My Annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.CONSTRUCTOR)
public #interface SetRandomPin {}
2.My Aspect
#Around("#annotation(com.testingAnnotations.annotattions.SetRandomPin)")
public void setUserPin(ProceedingJoinPoint joinPoint) throws NoSuchMethodException {....}
}
In my #Entity when I put #SetRandomPin in the constructor, the method setUserPin is not firing.
Only If I change to ElementType.METHOD and I move my annotation to the UserService.class the method is firing.
I'm stuck here and I can't understand why is working with an ElmentType but not with another one.
Default Spring AOP doesn't offer constructor interception or private/protected methods. You can do it using AspectJ.
From docs
If your interception needs include method calls or even constructors within the target class, consider the use of Spring-driven native AspectJ weaving instead of Spring’s proxy-based AOP framework.
I have a custom annotation:
#Target({ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface FeatureSwitch {
String featureName();
}
I intercept this with the below aspect and use it to check if a feature is on or off. If the feature is off, then I throw an exception.
Aspect:
#Aspect
public class FeatureSwitchAspect {
private final FeatureSwitchConfigurationApi featureSwitchConfigurationApi;
public FeatureSwitchAspect(final FeatureSwitchConfigurationApi featureSwitchConfigurationApi) {
this.featureSwitchConfigurationApi = featureSwitchConfigurationApi;
}
#Before("#annotation(featureSwitch)")
public void checkFeatureSwitch(final FeatureSwitch featureSwitch) {
final String featureName = featureSwitch.featureName();
Boolean featSwitch = featureSwitchConfigurationApi.isFeatureActive(featureName);
if (!featSwitch) {
throw new FeatureSwitchOffException();
}
}
}
The problem I am having is that the behaviour seems inconsistent. This seems to do as expected when I call a method from a different class, but if I make a call to an annotated private method, no interception occurs. Have I got it configured incorrectly? Any suggestions would be appreciated.
Method calls from within classes will not work with proxy-based AOP.
Since you are using the keyword this (which is a pointer to your original object and not the proxy objects that is wrapping it), you will be calling the wrapped method directly - thus bypassing the code added as a result of your AOP.
You have tagged your question by java and aop, not by spring or spring-aop. So I assume that you are not limited to proxy-based Spring AOP but can use a full-fledged AOP solution like AspectJ (possible even within Spring or application servers). If so, there is a solution:
Use a privileged aspect. Caveat: This is supported in native AspectJ syntax, but not in #AspectJ syntax.