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.
Related
I have a custom starter that should measure the execution time of the method, if there is an annotation #SomeCustomAnnotation I use Aspect in this starter
I added this starter as a dependency in another project and specified an annotation on the method #SomeCustomAnnotation, but nothing works.please tell me how to solve
#Aspect
#Slf4j
public class LoggableAspect {
#Pointcut("#annotation(com.app.annotations.LogTime)")
public void executeTiming() {
}
#Around("executeTiming()")
public Object logMethodTiming(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
Object returnValue = proceedingJoinPoint.proceed();
long totalTime = System.currentTimeMillis() - startTime;
log.info("Total time is >>>>>>>>", totalTime);
return returnValue;
}
}
#Configuration
#ConditionalOnClass(LoggableAspect.class)
#EnableConfigurationProperties(LoggingProperties.class)
#RequiredArgsConstructor
public class LoggingServiceAutoConfiguration {
private final LoggingProperties properties;
#Bean
#ConditionalOnMissingBean
public LoggableAspect loggableAspect(){
return new LoggableAspect();
}
}
I try to log with AOP for a CompletableFuture controller. #Before advice is working OK. But with #AfterReturning it is not working correctly in the exception case. I also tried with #AfterThrowing, which is not working either. When an exception occurs, my #AfterReturning advice also is not triggered and #AfterThrowing is never reached.
How can I use an AOP advice with exceptions in this case?
JController:
#RestController
public class JController extends BaseExceptionHandler {
#GetMapping(produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public CompletableFuture<BaseResponse> search() {
final CompletableFuture result = asyncProcessor.process(request);
return result;
}
}
BaseExceptionHandler:
public class BaseExceptionHandler {
#ExceptionHandler(Exception.class)
public ResponseEntity handleException(final Exception exception) {
return new ResponseEntity<>(new ErrorResponse(Message.INTERNAL_SERVER_ERROR, StatusCode.UNKNOWN_ERROR), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
AOP Class
#AfterReturning(value = "execution(* com.xxx.xxx.controller.*.*(..))", returning = "result")
public void outgoingSuccess(final JoinPoint joinPoint, final CompletableFuture result) {
LOGGER.debug("After Returning method: " + joinPoint.getTarget().getClass().getSimpleName());
}
#AfterThrowing("execution(* com.xxx.xxx.controller.*.*(..))")
public void outgoingError(final JoinPoint joinPoint) {
LOGGER.debug("After Throwing method: " + joinPoint.getTarget().getClass().getSimpleName());
}
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.
I'm trying to use AOP with annotation triggering as you can see in this pointcut
package mypackage.aop;
// ...
#Aspect
#Component
public class ErrorHandlerAspect {
private final static Logger LOGGER = LoggerFactory.getLogger(ErrorHandlerAspect.class);
#Pointcut("within(mypackage.config.steps..*) && #annotation(mypackage.aop.SaveAndErrors)")
private void pointcut(){ }
#Around("pointcut()")
private Object errorHandler(ProceedingJoinPoint pjp) throws Throwable{
try{
return pjp.proceed();
} catch (Throwable e){
LOGGER.error("Handling error");
throw e;
}
}
}
Here are declaration of annotation :
package mypackage.aop;
// ...
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface SaveAndErrors { }
... and usage in #Configuration class (for spring batch step configuration) :
package mypackage.config.steps;
// ...
#Configuration
public class StepConfiguration {
public final static String STEP_NAME = "xStep";
// ...
#SaveAndErrors
#Bean(name="xFileReader")
#StepScope
public ItemStreamReader<Object> xFileReader(#Value("#{stepExecutionContext['fileName']}") String resourceName // Inside a partitionner) throws xException {
try {
// ...
// return ...
} catch (yException e) {
throw new xException("new Exception :", e);
}
}
// ...
}
And my Application class :
#SpringBootApplication
public class Application { //...
}
Unfortunately, this is not working.
Removing && #annotation(mypackage.aop.SaveAndErrors) from pointcut, my aop proxy is working.
Where is the mistake?
I would like to create a Spring's bean producer method which is aware who invoked it, so I've started with the following code:
#Configuration
public class LoggerProvider {
#Bean
#Scope("prototype")
public Logger produceLogger() {
// get known WHAT bean/component invoked this producer
Class<?> clazz = ...
return LoggerFactory.getLogger(clazz);
}
}
How can I get the information who wants to get the bean injected?
I'm looking for some equivalent of CDI's InjectionPoint in Spring world.
Spring 4.3.0 enables InjectionPoint and DependencyDescriptor parameters for bean producing methods:
#Configuration
public class LoggerProvider {
#Bean
#Scope("prototype")
public Logger produceLogger(InjectionPoint injectionPoint) {
Class<?> clazz = injectionPoint.getMember().getDeclaringClass();
return LoggerFactory.getLogger(clazz);
}
}
By the way, the issue for this feature SPR-14033 links to a comment on a blog post which links to this question.
As far as I know, Spring does not have such a concept.
Then only thing that is aware of the point that is processed is a BeanPostProcessor.
Example:
#Target(PARAMETER)
#Retention(RUNTIME)
#Documented
public #interface Logger {}
public class LoggerInjectBeanPostProcessor implements BeanPostProcessor {
public Logger produceLogger() {
// get known WHAT bean/component invoked this producer
Class<?> clazz = ...
return LoggerFactory.getLogger(clazz);
}
#Override
public Object postProcessBeforeInitialization(final Object bean,
final String beanName) throws BeansException {
return bean;
}
#Override
public Object postProcessAfterInitialization(final Object bean,
final String beanName) throws BeansException {
ReflectionUtils.doWithFields(bean.getClass(),
new FieldCallback() {
#Override
public void doWith(final Field field) throws IllegalArgumentException, IllegalAccessException {
field.set(bean, produceLogger());
}
},
new ReflectionUtils.FieldFilter() {
#Override
public boolean matches(final Field field) {
return field.getAnnotation(Logger.class) != null;
}
});
return bean;
}
}