AOP and annotation 'inheritance' - java

let's consider the following situation.
#interface LoggedMethodInvocation{}
#LoggedMethodInvocation
#interface MonitoredMethodInvocation{}
I would like the #MonitoredMethodInvocation annotation implying the #LoggedMethodInvocation annotation.
class LoggingAOPConfig {
#Pointcut("#annotation(LoggedMethodInvocation)")
public void servicePointcut() {
}
#Around("servicePointcut()")
public Object logMethodInvocation(ProceedingJoinPoint pjp) throws Throwable {
// log the method invocation...
}
}
class MonitoringAOPConfig {
#Pointcut("#annotation(MonitoredMethodInvocation)")
public void servicePointcut() {
}
#Around("servicePointcut()")
public Object monitorResponseTime(ProceedingJoinPoint pjp) throws Throwable {
// add some meters to the method invocation
}
}
Now I would like to introduce some method, which shall be both monitored and logged. And I would like to annotate the method only with one annotation, namely #MonitoredMethodInvocation.
class SomeService {
#MonitoredMethodInvocation
Object someMethod(Object requestPayload) {
// ...
return responsePayload;
}
}
However it doesn't play, the logging aspect is not taken into the account.
There is spring's AnnotationUtils.findAnnotation which offers the needed functionality (of recognizing, whether the #LoggedMethodInvocation shall be considered). However, I don't know how to put this into the pointcut configuration.
How shall I modify the logging AOP config so it will recognize the logging annotation even if it is hidden behind the #MonitoredMethodInvocation?

Related

How to audit methods in Java Spring Boot

I am writing a Spring Boot Application. I want to audit methods with my annotation #AuditMetod: For example I have method foo() with the annotation:
#AuditMetod(name = "SomeValue")
foo() {...}
I want to handle and audit such methods like this (the simplest example):
auditMethod(Method method) {
if (method.hasAnnotation(AuditMethod.class)) {
System.out.println (method.getName() + " was called at " + new Date())
}
}
upd
Thanks to #Karthikeyan #Swapnil Khante and #misha2048 I understood, that I need to use AOP. But I have 2 problems:
The only method in Aspect class in not being called and I don't see the inscription "----------ASPECT METHOD IS CALLED-----------" in log
How can I check in aspect method what method it is intercepting. To get an instance of Method class.
Now I have the following code:
Controller:
#PostMapping
#LoggingRest(executor = "USER", method = "CREATE", model = "SUBSCRIPTION")
public ResponseEntity<?> create(#Valid #RequestBody SubscriptionRequestDto dto) {
...
}
Aspect:
`#Aspect
#Slf4j
#Component
public class AuditAspect {
#Pointcut(value = "#annotation(com.aspect.annotations.LoggingRest)")
public void auditMethod(ProceedingJoinPoint proceedingJoinPoint) {
log.info("----------ASPECT METHOD IS CALLED------------");
}`
And annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface LoggingRest {
String executor() default "SYSTEM";
String method() default "";
String model() default "";
}
Auditing is a cross-cutting concern and can be handled using AOP.
Another solution would be to use a low-level solution by writing a custom annotation and using a Spring interceptorto write your business logic.
To use the Spring interceptor you will need to implement the HandlerInterceptor interface
Example of the annotation
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface Audit {
boolean active() default true;
}
Interceptor example
#Component
public class AuditInterceptor implements HandlerInterceptor {
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Audit annotation = handlerMethod.getMethodAnnotation(Audit.class);
if (annotation != null && annotation.active()) {
// your business logic
}
}
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
check this interceptor example
I think one of the solutions here, as #Karthikeyan mentioned, is to use Spring AOP.
If you are not aware a brief introduction - spring-aop module implements the aspect oriented programming paradigm. We extract some common functionality, that we generally want to apply to some subset of functions/methods, to an entity called Aspect (see class annotated with #Aspect). This class will contain out cross-cutting functionality - such as auditing, for instance we want to audit the methods execution time, lets say. We just put the code to be executed, the condition, which tell the spring what exact beans methods should be affect by this aspect, see below.
For example, if I can audit the method execution duration with the following very simple example (in my case I said that any public method, returning void inside the Class com.example.stackoverflow.BusinessLogicClass must be inspected by this Aspect):
#SpringBootApplication
#EnableAspectJAutoProxy
public class StackoverflowApplication implements ApplicationRunner {
#Autowired
private BusinessLogicClass businessLogicClass;
public static void main(String[] args) {
SpringApplication.run(StackoverflowApplication.class, args);
}
#Override
public void run(ApplicationArguments args) throws Exception {
businessLogicClass.test();
}
}
#Aspect
#Component
class MyAspectLogicClass {
#Around("execution(public void com.example.stackoverflow.BusinessLogicClass.*(..))")
public Object hangAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
long before = System.currentTimeMillis();
Object returnedValue = proceedingJoinPoint.proceed();
long after = System.currentTimeMillis();
System.out.printf("Retruned in '%s' ms %n", (after - before));
return returnedValue;
}
}
#Component
class BusinessLogicClass {
public void test() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
In my case, I will get the time before method execution, then by the means of
proceedingJoinPoint.proceed() call I delegate the execution to the real method, and then, once I get the response back, I will get the current system time and calculate the execution time, fairly simple.
I hope I have at least directed you somewhere, if you are looking for documentation, this are the resources I suggest you should look for:
https://docs.spring.io/spring-framework/docs/2.5.x/reference/aop.html offical spring doc (stale a bit, but there are some valuable things to learn)
https://docs.spring.io/spring-framework/docs/4.3.15.RELEASE/spring-framework-reference/html/aop.html is more fresh doc
Hope it helped :)
The problem was in right annotation. In Aspect class I tried #Around and everything works as I need.
#Aspect
#Slf4j
#Component
public class AuditAspect {
#Around(value = "#annotation(com.aspect.annotations.LoggingRest)")
public void auditMethod(ProceedingJoinPoint proceedingJoinPoint) {
var method = ((MethodSignature) proceedingJoinPoint.getSignature()).getMethod();
log.info("----------ASPECT METHOD IS CALLED------------");
}
}
For getting a Method instance I use fallowing code
Method method = ((MethodSignature) proceedingJoinPoint.getSignature()).getMethod();

Java Spring AOP: Can I ignore Xlint:invalidAbsoluteTypeName error which caused by pointcuts to classes which I don't use?

I have an issue concerning a generic component and one (of a dozen) application(s). My component has point cuts to many annotations, which could be used within classes and methods in my apps. When all annotations are present on the classpath, everything works fine. But not in all my apps I have these dependencies. The quick fix is, of course, add them, but that gives my app a lot of code which I don't need in that app. I'm searching for a way to ignore the Xlint:invalidAbsoluteTypeName error as stated here: Xlint:invalidAbsoluteTypeName
So what I have:
I have many apps with Soap/JMS connections, and all are annotated with the #Annotation org.springframework.ws.server.endpoint.annotation.Endpoint.
I have my pointcut in my generic component (jar):
#Around("within(#org.springframework.ws.server.endpoint.annotation.Endpoint *)")
And the result is:
All apps having the Spring WS dependency along with my generic component have no issues
Apps which don't have the annotation, cannot start due to java.lang.IllegalArgumentException: warning no match for this type name: org.springframework.ws.server.endpoint.annotation.Endpoint [Xlint:invalidAbsoluteTypeName] (which is obvious, see the link)
So the problem looks like Xlint:invalidAbsoluteTypeName BUT I don't want to add Spring dependencies which I'm not using. I just want this AOP pointcut ignored. Other workarounds like splitting up the pointcuts to different jars imho give too much overhead. Is there any way to have Spring AOP just ignore this pointcut, or e.g. set the pointcut to st like if-exists(class)?
To show why I think separating is causing way too much overhead have a look at my aspect structure:
#Aspect
public class PerformanceLoggingAspect {
private LogWriter logWriter;
#Inject
public PerformanceLoggingAspect(LogWriter logWriter) {
this.logWriter = logWriter;
}
#Around("within(#org.springframework.web.bind.annotation.RestController *)")
public Object withinARestController(ProceedingJoinPoint pjp) throws Throwable {
return proceedWithLogging(pjp, MetingType.REST);
}
#Around("within(#org.springframework.ws.server.endpoint.annotation.Endpoint *)")
public Object withinAnEndpoint(ProceedingJoinPoint pjp) throws Throwable {
return proceedWithLogging(pjp, MetingType.BERICHT);
}
#Around("within(#javax.inject.Named *)")
public Object withinAService(ProceedingJoinPoint pjp) throws Throwable {
return proceedWithLogging(pjp, MetingType.SERVICE);
}
private Object proceedWithLogging(ProceedingJoinPoint pjp, String metingType) throws Throwable {
(... Working code (performance logging) if the annotation is on the classpath...)
}
}
Update: I tried creating a #NeedsClass("any.package.Class") which is a #Conditional annotation from spring-context. The condition class is a ClasspathCondition which checked if the classloader could load that given class. But the error occurs before the condition gets evaluated so I'm afraid this is a dead end. But if you're curious:
The #NeedsClass annotation I tried
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.TYPE, ElementType.METHOD})
#Documented
#Conditional(ClasspathCondition.class)
public #interface NeedsClass {
String[] value();
}
The Condition implementation. I had logging here, which never got written
public class ClasspathCondition implements Condition {
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
try {
String[] classes = (String[]) metadata.getAnnotationAttributes(NeedsClass.class.getName()).get("classes");
for (String clazz : classes) {
ClassUtils.resolveClassName(clazz, context.getClassLoader());
}
return true;
} catch (Throwable t) { /* noOp() */}
return false;
}
}
For now I have a workaround:
I created a superclass with the method:
protected Object proceedWithLogging(ProceedingJoinPoint pjp, String metingType) throws Throwable {
(... code which adds performance logging ...)
}
I created 4 subclasses with each the #Aspect annotation, and 1 method calling the super. For example this one targets JMS:
#Aspect
public class JmsPerformanceLogger extends PerformanceLoggingAspect {
#Inject
private LogWriter logWriter;
#Around("within(#org.springframework.ws.server.endpoint.annotation.Endpoint *)")
public Object withinAnEndpoint(ProceedingJoinPoint pjp) throws Throwable {
return proceedWithLogging(pjp, MetingType.BERICHT);
}
}
As a downside I have to configure all different beans which I need within my app, and I cannot add one simple configuration file as shown below, with all beans preconfigured:
#Configuration
public class PerformanceloggingConfig {
#Bean
public LogWriter performanceLogWriter(){
return new DefaultLogWriter();
}
#Bean
public JmsPerformanceLogger jmsPerformanceLogger(){
return new JmsPerformanceLogger();
}
#Bean
public RestPerformanceLogger restPerformanceLogger(){
return new RestPerformanceLogger();
}
#Bean
public ServicesPerformanceLogger servicesPerformanceLogger(){
return new ServicesPerformanceLogger();
}
#Bean
public DaoPerformanceLogger daoPerformanceLogger(){
return new DaoPerformanceLogger();
}
}
And therefore also not the handy annotation to autoconfig the class:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
#Import(PerformanceloggingConfig.class)
public #interface EnablePerformanceLogging {
}
But for now adding these 4 beans when I need them, makes it possible to differentiate per app. But of course this is still a workaround, as I want to use #EnablePerformanceLogging and be done with it. If anyone has a better answer, pls tell me

Java annotation to determine whether the annotated method executes

In my methods, all my code is in an if block, testing certain condition.
public void myMethod() {
if (/* some condition */) {
//do something
}
}
I would like to do this by annotation - meaning the annotation will execute some code that will "decide" whether or not the method should be invoked.
#AllowInvokeMethod(/* some parameters to decide */)
public void myMethod() {
//do something (if annotation allows invokation)
}
Is this possible?
You can use Spring AOP to create an ASpect to advise methods that are annotated your custom annotation
For example create an FilteredExecution annotation to be specified on your methods
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface FilteredExecution{
Class<? extends ExecutionFilter> value();
}
ExecutionFilter is an interface to decide whether execution should occur
public interface ExecutionFilter{
boolean sholudExecute();
}
Then the aspect
#Aspect
#Component
public class FilteredExceutionAspect{
#Around("#annotion(filterAnnotation)")
public void filter(ProceedingJoinPoint pjp , FilteredExecution filterAnnotation){
boolean shouldExecute = checkShouldExecute(filterAnnotation);
if(shouldExecute){
pjp.proceed();
}
}
private boolean checkShouldExecute(FilteredExecution filterAnnotation){
//use reflection to invoke the ExecutionFilter specified on filterAnnotatoon
}
You need to setup your context so that your beans with the custom annotation are auto proxied by using #EnableAspectjAutoProxy on your configuration class
you can try this, documentation above metoh.
this annotation is show when the method is invoke, and see the document of
meothd
/**
* descripcion of the method
* #param value , any value
*/
public void myMethod(String value) {
//do something (if annotation allows invokation)
}
if you put this structure you can't see the documentation when you
call some method,,
//descripcion of the method
public void myMethod(String value) {
//do something (if annotation allows invokation)
}
in my case, it works, i hope this works for you

Invoke other method instead

I have two methods and one of them with an annotation, let's say:
#ReplacingMethod(bar)
public void foo() { ... }
public void bar { ... }
Is it possible to invoke bar instead of foo whenever foo is called, without jumping into the body of foo? I did some research on this and were not able to set a return value via reflections. Any suggestions?
You can achieve this using Aspect Oriented Programming, e.g. with Spring AOP. I don't think you can change method implementation in pure Java without AOP.
Let me give you an example how to achieve what you asked for with Spring AOP. First, define your annotation:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface ReplacingMethod {
String value();
}
Then define an aspect that will do the actual replacing of method:
#Aspect // aspect is a module encapsulating your replacing functionality
public class ReplacingAspect {
// pointcut gives an expression selecting the "joint points" to be intercepted
#Pointcut("#annotation(example.annotation.ReplacingMethod)")
public void methodToBeReplaced() { }
// advice defining the code executed at joint points selected by given pointcut;
// in our case #Around is executed instead of the method call selected by pointcut methodToBeReplaced()
#Around("methodToBeReplaced()")
public void replaceMethodCall(ProceedingJoinPoint pjp) throws Throwable {
// get reference to the method to be replaced
MethodSignature signature = (MethodSignature) pjp.getSignature();
Method method = signature.getMethod();
// extract the name of the method to be called from ReplacingMethod annotation
ReplacingMethod replacingMethodAnnotation = method.getAnnotation(ReplacingMethod.class);
String methodToCallName = replacingMethodAnnotation.value();
// use reflection to call the method
Method methodToCall = pjp.getTarget().getClass().getMethod(methodToCallName);
methodToCall.invoke(pjp.getTarget());
}
}
Now, assuming you have class TestClass where you have applied your #ReplacingMethod annotation,
public class TestClass {
#ReplacingMethod("bar")
public void foo() { System.out.println("foo"); }
public void bar() { System.out.println("bar"); }
}
the last missing piece is to get create your instance of TestClass with AOP enabled and your ReplacingAspect applied:
public class Main {
public static void main(String... args) throws Exception {
ApplicationContext context = new AnnotationConfigApplicationContext(TestConfiguration.class); // create Spring context that enables AOP under the hood
TestClass testObject = context.getBean(TestClass.class); // we get reference to TestClass instance from context; calling on a plain new instance wouldn't work
testObject.foo(); // prints "bar" !
}
#EnableAspectJAutoProxy // enables AOP support
#Configuration
public static class TestConfiguration {
#Bean public TestClass testClass() { return new TestClass(); }
#Bean public ReplacingAspect aspect() { return new ReplacingAspect(); } // enables our ReplacingAspect
}
}
You can check out the whole working example at GitHub.
Reflection cannot change the schema of a class and not its behaviour. It can only call (possibly hidden) features.
If you want to replace a method call by another try out a byte code library as asm or javassist. These tools allow you to change class definitions and behaviour (even at runtime with some restrictions).
The approach with AOP is easier, but it is not as flexible and its classpath footprint is larger.

Configure interceptor (proxy) on intercepted method

As far as I know, in Spring AOP when we want some method call to be intercepted, we configure an Aspect having such a pointcut configuration that matches wanted method invocation. In other words, we configure interception on the Aspect side.
Is there a way to configure it entirely from the opposite side, that is, on method which invocation is to be intercepted? I expect that something like this is possible:
#Component
class MyClass {
#Intercept(interctptor="myInterceptor", method="invoke")
Object methodThatWillBeIntercepted(Object arg) {
// ..
}
}
#Component(value="myInterceptor")
class MyInterceptor {
Object invoke(MethodInvocation mi) {
// ...
if (someCondition) {
return mi.proceed();
} else {
return someOtherValue;
}
}
}
You can, at least if you're using it with AspectJ. You can use the syntax #annotation(com.mycompany.MyAnnotation) in your pointcut declaration to target elements that are annotated with your annotation. You can read more about it in section 9.2.3 of the Spring reference documentation
If you're not using AspectJ, but a generic proxy based interceptor, a "brute force" approact would be to proxy all the object you want to check and then check the method invocation argument to see if the method is annotated with your annotation, something like this:
class MyInterceptor {
public Object invoke(MethodInvocation mi) {
if(mi.getMethod().getAnnotation(MyAnnotationClass.class) != null) {
// Do the interception
}
else {
return mi.proceed();
}
}
}
Don't remember the exact API for MethodInvocation, but something like that.

Categories