I have been trying to get Spring AOP to work for the last day or two and have looked at many different resources but can't seem to get it working. I must be missing something obvious but I am spinning my wheels. Here is what I currently have.
My custom annotation:
CheckFeatureEnabled
:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckFeatureEnabled {
String propertyName() default "";
}
My Aspect:
CheckFeatureEnabledAspect
:
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
@Component
public class CheckFeatureEnabledAspect {
// I have tried both short and fully qualified, neither seem to work in my project
@Pointcut("@annotation(com.example.CheckFeatureEnabled)")
public void annotatedWithCheckFeatureEnabled() {}
@Before("annotatedWithCheckFeatureEnabled() && @annotation(checkFeatureEnabled)")
public void beforeMethodWithCheckFeatureEnabled(CheckFeatureEnabled checkFeatureEnabled) {
String propertyName = checkFeatureEnabled.propertyName();
System.out.println("AspectJ: Intercepted method with property: " + propertyName);
// Add your logic here
// Example: Check if the feature is enabled based on the propertyName
// boolean featureEnabled = checkFeature(propertyName);
// if (!featureEnabled) {
// throw new RuntimeException("Feature " + propertyName + " is not enabled!");
// }
}
My method that should be advised:
import com.example.InjectableProperties;
import com.google.inject.Inject;
import org.springframework.stereotype.Component;
@Component
public class AppHomeFeatureEnabler {
private final InjectableProperties properties;
@Inject
public AppHomeFeatureEnabler(InjectableProperties properties) {
this.properties = properties;
}
@CheckFeatureEnabled(propertyName = "test")
public boolean isEnabled() {
return this.properties.getBoolean("AppHome.enabled").orElse(false);
}
}
I have added @EnableAspectJAutoProxy
to my @SpringBootApplication
class and added the dependencies (both with AspectJ and not)
pom.xml
<!-- Spring AOP -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- AspectJ Runtime -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
</dependency>
<!-- AspectJ Weaver (for load-time weaving) -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
Any ideas what I might be missing here that would cause this not to work? If I swap out my custom annotation with something like @Deprecated
, it seems to work during boot. But I wasn't able to get any other annotation working after Spring fully starts up so it seems like something there but I can't figure it out.
For example, trying @Deprecated
in my App runtime:
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class CheckFeatureEnabledAspect {
@Pointcut("execution(* com.example..*(..)) && @annotation(java.lang.Deprecated)")
public void deprecatedMethodWithinApp() {}
// Advice to be executed before the annotated method is called
@Before("deprecatedMethodWithinApp()")
public void beforeDeprecatedMethod() {
System.out.println("Warning: A deprecated method is being executed within your application!");
}
}
Does not work, but if I do just @Pointcut("@annotation(java.lang.Deprecated)")
it works when starting the app (but does throw an error and kill the app ultimately)
Any suggestions as to what might be going wrong here?
So it looks like this is something about the way it is being called, I moved all the files over from a new project it's working in and it works here.
The way I am calling this is:
HomeOpenedHandler
public class HomeOpenedHandler
...
public List<LayoutBlock> buildBlocks() {
if (!new AppHomeFeatureEnabler(properties).isEnabled()) return List.of();
...
}
I also tried
@CheckFeatureEnabled(propertyName = "myFeature")
public List<LayoutBlock> buildBlocks() {
...
}
Neither worked? If it helps I am building a Slack app, so I wonder if something is getting called outside of the Spring context? And thats why its not working.
AppHomeFeatureEnabler
@Component
public class AppHomeFeatureEnabler {
@Autowired
InjectableProperties properties;
@Inject
public AppHomeFeatureEnabler(InjectableProperties properties) {
this.properties = properties;
}
@CheckFeatureEnabled(propertyName = "myFeature")
public boolean isEnabled() {
return this.properties.getBoolean("AppHome.enabled").orElse(false);
}
}
Why is isEnabled()
not working when calling it? But it works in a @RestController
? calls it via a @GetMapping
?
I your code, I just noticed this:
new AppHomeFeatureEnabler(properties).isEnabled()
I.e., you are creating an instance manually instead from a Spring context, e.g. via getBean(..)
. This way, Spring AOP cannot intercept any methods. Only native AspectJ could. Simply create instances via Spring, and you should be fine.