Custom AOP Spring Boot annotation - spring-boot

I am trying to create a custom annotation. I need to understand what's missing.
This is a simple java application.
I have created my custom annotation and aspect. The point-cut, if I am not wrong, is #Before and I am using the annotation at method level.
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface Exampleannotate { }
#Aspect
public class ExampleAspect {
#Before("#annotation(ISTransaction)")
public Object logTransactionDetails(ProceedingJoinPoint joinPoint)
throws Throwable {
System.out.println("in aspect" + joinPoint.getArgs());
return joinPoint.proceed();
}
}
#Exampleannotat
public static void add(int a, int b) {
System.out.println(a + b);
}
I expect the program to print "in aspect" with joinPoint.getArgs() result.

Related

Annotation and Pointcut on same class method

I have two aspects but only TryCatchLog is working even when method annotated with CatchRedBanner.
One on all methods returning AssertionData in package pageActions
package com.abc.acceptance.b2b.aspects;
#Aspect
#Component
public class TryCatchLogAspect {
#Pointcut(
"execution(com.abc.acceptance.b2b.annotations.AssertionData com.abc.acceptance.b2b.pageActions..*(..))")
private void pageActionsTryCatchLog() {
}
#Around("pageActionsTryCatchLog()")
public Object tryCatchLog(ProceedingJoinPoint joinPoint) throws Throwable { ...
This one for methods with my annotation
import java.lang.annotation.*;
#Target({ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface CheckRedBanner {
}
-----
package com.abc.acceptance.b2b.annotations;
#Aspect
#Component
#Slf4j
public class CheckRedBannerAspect {
#Before("#annotation(CheckRedBanner)")
public void myAdviceForMethodAnnotation(JoinPoint joinPoint) {
handleBeforeExecution(joinPoint);
}
protected void handleBeforeExecution(JoinPoint joinPoint) {
System.out.println("Made iitttt !!! ");
}
}
My code is calling only TryCatchLog but not CheckRedBanner even when I annotate the method
package com.abc.acceptance.b2b.pageActions;
#Component
#Slf4j
#Scope(SCOPE_CUCUMBER_GLUE)
public class BroadbandPanelActions extends PageActions {
#CheckRedBanner
public AssertionData clickFindAddress() {
broadbandPanel.getFindAddressButton().click();
return new AssertionData();
}
...
From the reference docs regarding Advice Ordering
When two pieces of advice defined in different aspects both need to
run at the same join point, unless you specify otherwise, the order of
execution is undefined. You can control the order of execution by
specifying precedence.
For me the behaviour is reproducible when I order the Aspects as follows . I have ordered so that Around advice is executed before Before advice . Also note that I have commented the joinPoint.proceed(); call.
#Component
#Aspect
#Order(0)
public class TryCatchLogAspect {
#Pointcut("execution(sec2.aop.bean.AssertionData sec2.aop.bean..*(..))")
private void pageActionsTryCatchLog() {
}
#Around("pageActionsTryCatchLog()")
public Object tryCatchLog(ProceedingJoinPoint joinPoint) throws Throwable {
// Object result = joinPoint.proceed();
System.out.println("pageActionsTryCatchLog");
return new AssertionData();
}
}
and
#Component
#Aspect
#Order(1)
public class CheckRedBannerAspect {
#Before("#annotation(CheckRedBanner)")
public void myAdviceForMethodAnnotation(JoinPoint joinPoint) {
handleBeforeExecution(joinPoint);
}
protected void handleBeforeExecution(JoinPoint joinPoint) {
System.out.println("Made iitttt !!! ");
}
}
when I uncomment the proceed() call both the aspects work.
#Around("pageActionsTryCatchLog()")
public Object tryCatchLog(ProceedingJoinPoint joinPoint) throws Throwable {
Object result = joinPoint.proceed();
System.out.println("pageActionsTryCatchLog");
return new AssertionData();
}
When we are not explicitly calling joinPoint.proceed(); spring does that for us. But in the first case we are returning without calling the underlying method and the #Before advice fails to execute.

Spring Aspect on Converter

I created simple Aspect an annotation for measuring time of execution of annotated method.
When I annotate method of a simple Spring Bean, inject bean, and run it like bean.annotatedMethod(), everything works fine.
However, when I annotate convert() method on Spring Converter, annotation is ignored. I'm guessing the reason is that convert() is called internally by Spring's ConversionService, and somehow Aspects are not respected. Is there any way to get it to work?
Annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface LogExecTime {
}
Aspect, which I register in Spring:
#Aspect
#Component
public class LogTimeAspect {
#Around(value = "#annotation(annotation)")
public Object LogExecutionTime(final ProceedingJoinPoint joinPoint, final LogExecTime annotation) throws Throwable {
final long startMillis = System.currentTimeMillis();
try {
System.out.println("Starting timed operation");
final Object retVal = joinPoint.proceed();
return retVal;
} finally {
final long duration = System.currentTimeMillis() - startMillis;
System.out.println("Call to " + joinPoint.getSignature() + " took " + duration + " ms");
}
}
}
This works fine:
#Component
public class Operator {
#LogExecTime
public void operate() throws InterruptedException {
System.out.println("Performing operation");
Thread.sleep(1000);
}
}
#Bean
protected Void test(Operator o) {
o.operate();
return null;
}
But here, annotation is ignored:
public class SampleConverter implements Converter<SourceType, ResultType> {
#Override
#LogExecTime
public ImmutableData convert(#Nonnull ClassifiedEvent result) {
...
}
}
ConversionService conversionService;
...
conversionService.convert(source, ResultType.class));
Solved by comment of #EssexBoy, my converter was not a spring managed bean.

Run aspect on proxy object

I have following simple service:
#Service
public class TestServiceImpl implements TestService {
#Override
public void countExternal(Integer arg1) {
System.out.println("test - lock external");
count(arg1, new Integer(1));
}
public void count(Integer arg1, Integer arg2) {
System.out.println("test - lock internal");
}
}
that implements my simple interface:
public interface TestService {
void countExternal(Integer arg1);
}
Here's the aspect that I am using to do some validation etc. during count method:
#Aspect
#Component
public class TestAdvicer {
#Around("execution(* count(..))")
public Object advice(ProceedingJoinPoint joinPoint) throws Throwable {
// do som magic here
return joinPoint.proceed();
}
}
In my Spring configuration I have included autoproxying:
#EnableAspectJAutoProxy(proxyTargetClass = true)
Unfortunately, my TestAdvicer is never executed since count method is invoked from countExternal method. count method is executed on Proxy object and because of that advice didn't run.
Do you know how can I run my advice on Proxy object? What is the best way to solve this problem?

Spring: Injecting property as a value into an annotation

I am trying to inject a value into a Custom Annotation but Spring doesn't seem to be evaluating.
Here is my annotation:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface MyCustomAnno {
String var1();
String var2();
}
Here is my Bean (it is created in a Spring Configuration file) with Annotation:
public class MyClass {
#MyCustomAnno(var1 = "${some.property.one}",
var2 = "${some.property.two}")
public void someMethod() {
// do something here
}
}
Here is the Aspect where I am trying to use the values passed into the annotation:
#Aspect
public class MyAop {
#Around(value="#annotation(myCustomAnno)",argNames="myCustomAnno")
public Object aroundMethod(MyCustomAnno myCustomAnno) {
int intVar1 = Integer.parseInt(myCustomAnno.var1());
int intVar2 = Integer.parseInt(myCustomAnno.var2());
// ....
}
}
In the around method I am receiving a NumberFormatException: For input string: ${some.property.one}. This means that Spring didn't inject the value for some reason.
In case you are wondering, in the same class I can do the normal #Value annotation and the value gets injected properly:
#Value("${some.property.one}")
private propertyOne; // This works
Is it possible to do what I want to and if so, how?
AFAIK, placeholders are not resolved in custom annotations. However you could resolve them in the Aspect itself.
For example:
#Aspect
class MyAop implements EmbeddedValueResolverAware {
private StringValueResolver resolver;
#Around(value="#annotation(myCustomAnno)",argNames="myCustomAnno")
public void play(MyCustomAnno ann) {
System.out.println(resolver.resolveStringValue(ann.var1()));
}
#Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.resolver = resolver;
}
}

How to achieve a dynamic dependency injection in spring?

We have a scenario where the dependency should be determinded at runtime. An example shows the detail below:
public class OrderProcessor {
// The Validator should be determinded based on the version of the service.
private Validator orderProcessValidator;
public Confirmation process(Order order) {
if(orderProcessValidator.validate(order)) {
// Business logic
}
}
}
Is possible to inject Validator dynamically with Spring IOC, or can only be solved through the factory pattern?
It is still a bit unclear for me your scenario, but I am assuming you have two actual Order classes in your project. Maybe one is in com.foo.api1 package and the other is in com.foo.api2 package. Or one Order is called Order1 and the other is called Order2. The idea is that I'm assuming you have two different classes for the two 'api versions' of Order.
You can achieve what you need by using Spring AOP:
#Aspect
#Component
public class MyAspect {
#Autowired
private Validator1 validator1;
#Autowired
private Validator2 validator2;
#Pointcut("execution(* com.foo.bar.OrderProcessor.process(..))")
private void myPointcut() {}
#Around("myPointcut() && args(order)")
public Object myAroundAdvice(ProceedingJoinPoint pjp, Object order) throws Throwable {
if (order instanceof Order1) {
validator1.validate((Order1) order);
} else
if (order instanceof Order2) {
validator2.validate((Order2) order);
}
Object retVal = pjp.proceed();
return retVal;
}
}
#Component
public class OrderProcessor {
public void process(Object order) {
System.out.println("processing order");
}
}
#Component
public class Validator1 {
public void validate(Order1 order) {
System.out.println("validating inside validator 1");
}
}
#Component
public class Validator2 {
public void validate(Order2 order) {
System.out.println("validating inside validator 2");
}
}
So, basically, you are defining an aspect that should intercept calls to your OrderProcessor class and, depending on what parameter it receives, it calls one validator or the other.

Resources