I have Aspect class with method clear().
#Aspect
public class Clear
{
#After("#annotation(org.springframework.transaction.annotation.Transactional)")
public void clear()
{
// do smth
}
}
now I want call this aspect after each execution of method with annotation #Transactional with readOnly = true like
#Transactional(readOnly = true)
public void someMethod()
{
//...
}
is there way to do it without custom annotations?
I think you are quite close.
In your clear method, you should take in a parameter of type JoinPoint. This parameter will be auto populated by Spring at runtime, and with it, you can get details of your specific joinpoint, including the java.lang.reflect.Method, which will contain the annotation you are after.
I am thinking something like this:
#Aspect
public class Clear
{
#After("#annotation(org.springframework.transaction.annotation.Transactional)")
public void clear(final JoinPoint joinPoint)
{
final Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
final Transactional txAnnotation = methood.getAnnotation(Transactional.class);
final boolean isReadOnly = txAnnotation.readOnly();
//do conditional work based on isReadOnly
}
}
Related
Is it possible to create a custom annotation that simply tracks invocations of some methods, without having to add a service method call in every method explicit?
#InvocationCounter(path = "/test1") //I'm looking for this
#GetMapping("/person/{id}")
public Person getPerson(Long id) {
//...
}
On every getPerson() call, I want an invocation counter to record the invocation, like:
#Service
public class InvocationCounterService {
Map<String, AtomicInteger> counter;
public void count(String path) {
if (counter.get(path) == null) counter.put(path, new AtomicInteger()));
counter.get(path).incrementAndGet();
}
#Scheduled(fixedRate = 60000)
public void persist() {
//optionally process or persist the invocations
}
}
Question: how could I possibly tell Spring to invoke the count() service method on each annotated controller method?
The annotation InvocationCounter:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface InvocationCounter {
String path();
}
The Aspect InvocationCounterAspect:
#Aspect
#Component
public class InvocationCounterAspect {
#Autowired
InvocationCounterService invocationCounterService;
#Around("#annotation(InvocationCounter)")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
InvocationCounter invocationCounter = signature.getMethod().getAnnotation(InvocationCounter.class);
final String path = invocationCounter.path(); //retrieve path from the annotation
invocationCounterService.count(path); //call the counter service
return joinPoint.proceed(); //proceed executing the annotated method
}
}
You should be looking at micrometer.io, Spring boot application natively supports metrics collection, including simple counter.
An annotation is a form of syntactic metadata that can be added to Java source code.
So you can add informations to your source with an annotation but you can't add code to be executed.
You can do with aspects as mentioned by pleft in the comments.
Or you can create a countExecutions function that takes your Function as a lambda expression to count the number of executions as follow:
public R countExecutions(Function<R, T> fun, T data) {
// invoke counter
return fun.apply(data);
}
and rewriting your code as
#GetMapping("/person/{id}")
public Person getPerson(Long id) {
return countExecutions( /* code previously in the getPerson as a Function */ , id);
}
I'm building a package that is trying to intercept a function's return value based on a flag. My design involves some AOP. The idea is that a class FirstIntercept intercepts a call firstCall and stores parameters in a Parameters object. Then later, a second class SecondIntercept intercepts another call secondCall and does some logic based on what is populated in Parameters:
// pseudoish code
public class FirstIntercept {
private Parameters param;
#AfterReturning(pointcut = "execution(* ...firstCall(..))", returning = "payload")
public void loadParam(Joinpoint joinPoint, Object payload) {
// logic handling payload returned from firstCall()
// logic provides a Boolean flag
this.param = new Parameters(flag);
}
}
public class Parameters {
#Getter
private Boolean flag;
public Parameters(Boolean flag) {
this.flag = flag;
}
}
public class SecondIntercept {
private static Parameters params;
#Around("execution(* ...secondCall(..))")
public void handleSecondCallIntercept(ProceedingJoinPoint joinPoint) {
// want to do logic here based on what params contains
}
}
What I want to achieve is that the Parameters object is loaded once and for all when FirstIntercept.loadParam is invoked through AOP. I'm not too sure how I can go about with this persistence. I looked online and Google guice seems to be promising. I believe a first step would to use dependency injection on the Parameters, but I'm really not sure. Can someone help point me in the right direction?
edit:
So I tried this setup:
public class FirstIntercept implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("invoked!");
return invocation.proceed();
}
#AfterReturning(pointcut = "execution(* ...firstCall(..))", returning = "payload")
public void loadParam(Joinpoint joinPoint, Object payload) {
// do stuff
}
public String firstCall() {
return "hello";
}
}
public class InterceptionModule extends AbstractModule {
protected void configure() {
FirstIntercept first = new FirstIntercept();
bindInterceptor(Matchers.any(), Matchers.annotatedWith(AfterReturning.class), first);
}
}
public class FirstIterceptTest {
#Test
public void dummy() {
Injector injector = Guice.createInjector(new InterceptionModule());
FirstIntercept intercept = injector.getInstance(FirstIntercept.class);
intercept.firstCall();
}
}
When I do .firstCall(), I can see the #AfterReturning running but the invoke is not being called.
If you expand upon the documentation for AOP https://github.com/google/guice/wiki/AOP you should get something close to:
public class FirstInterceptor implements MethodInterceptor {
#Inject Parameters parameters; // Injected with singleton Parameter
public Object invoke(MethodInvocation invocation) throws Throwable {
Object result = invocation.proceed();
// your logic based on result to set parameters.setFlag()
return result;
}
}
Then the second:
public class SecondInterceptor implements MethodInterceptor {
#Inject Parameters parameters; // Injected with singleton Parameter
public Object invoke(MethodInvocation invocation) throws Throwable {
boolean flag = parameters.getFlag();
// your logic here
return invocation.proceed(); // maybe maybe not?
}
}
Your parameters is the key, you'll need to ensure it's thread safe, which is another topic. But to inject these you need:
public class InterceptionModule extends AbstractModule {
protected void configure() {
// Ensure there is only ever one Parameter injected
bind(Parameter.class).in(Scopes.SINGLETON);
// Now inject and bind the first interceptor
FirstInterceptor firstInterceptor = new FirstInterceptor();
requestInjection(firstInterceptor );
bindInterceptor(Matchers.any(), Matchers.annotatedWith(AfterReturning.class),
firstInterceptor);
// Now inject and bind the second interceptor
SecondInterceptor SecondInterceptor = new SecondInterceptor ();
requestInjection(firstInterceptor);
bindInterceptor(Matchers.any(), Matchers.annotatedWith(AfterReturning.class),
SecondInterceptor);
}
}
Edit
Look at what you're doing.
You're telling Guice to wrap a method with #AfterReturn with the FirstInterceptor
Then you're calling interceptor.firstCall()
First call does not have #AfterReturn annotation, so why would it be matched against that configuration?
I'm guessing if you called:
intercept.loadParam();
you would see the invoke method. Also, this is great for a test, but in real life you want to have a Service level class have the #AfterReturn which is then Injected into another Api/Job/Etc that will call LoadParam.
edit
Oh no. Take a look at this line
bindInterceptor(Matchers.any(), // a class with this matcher
Matchers.annotatedWith(AfterReturning.class), // a method with this
firstInterceptor);
This means that the injector only fires on the loadParams. You need to annotate the method of the class youw ish to cause the interception with #AfterReturning. And you want the loadParams to be the invoke method.
I want to mark a field of a class with my custom annotation. And whenever any method is invoke I want to do some modification on that field.
public class Message{
public Integer id;
#FreeText // this is my custom annotation
public String htmlMsg;
public String textMsg ;
}
This annotation (#FreeText) can be used in any class.
In seasar framework, I can do this by create an interceptor and override invoke method. The I can get the object of this class and the find the field that marked with my annotation and modify it. However, i cannot find a way to do it in Spring.
In spring, I found some method like MethodInvocationInterceptor, but I don't know how to implement it. Can you suggest any way to do this in Spring?
Seasar2 and Spring are very close. I have not tested but you can do something like this.
First create FreeText custom annotation
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
#Documented
public #interface FreeText {}
Then create the following interceptor
public class EncryptSensitiveDataInterceptor extends MethodInterceptor {
#Override
public Object invoke(MethodInvocation in) throws Throwable {
Object[] params = in.getArguments();
Object param = params[0];
for (Field field : param.getClass().getDeclaredFields()) {
for (Annotation anno : field.getDeclaredAnnotations()) {
if (anno instanceof FreeText) {
field.set(param, [YOUR CUSTOM LOGIC METHOD]);
}
}
}
return in.proceed();
}
Hope this help.
I am trying to log the execution time for methods annotated with custom interface.
I am using Spring AOP.
But this does not seems to work for inner methods.
I think it is the limitation in Spring AOP
#Aspect
public class BusinessProfiler {
private static Log log = LogFactory.getLog(BusinessProfiler.class);
#Around("execution(* *(..)) && #annotation(TimeLog)")
public Object profile(ProceedingJoinPoint point) throws Throwable {
long start = System.currentTimeMillis();
Object result = point.proceed();
String format =
String.format("%s#%s: took [%s msec]", point.getTarget().getClass().getSimpleName(),
MethodSignature.class.cast(point.getSignature()).getMethod().getName(),
System.currentTimeMillis() - start);
log.info(format);
return result;
}
}
Are there any alternatives than Spring AOP
If you think about the way AOP annotations are dealt with by Spring this will be clear:
Spring takes your class and wraps it in a proxy with the extra code generated on the fly by the AOP annotation added. So only code called via the proxy (i.e from outside your class will be included).
Example
#Service
public class Foo {
public void doSomething() {
doSomethingInternal();
}
public void doSomethingInternal() {
}
}
If from another Spring bean I do this:
#Service
public class Bar {
#Autowired
private Foo foo;
public void execute() {
foo.doSomething();
}
}
Only doSomething will be called via the proxy which wraps your class, not doSomethingInternal, that will be called by your class.
I have this interface:
public interface FakeTemplate {
#CustomAnnotation
void foo() {
}
}
And this implementation of the interface:
#Component
public FakeImpl implements FakeTemplate {
#Override
public void foo() {
//Do Stuff
}
}
And this aspect:
#Aspect
#Component
public class CustomAspect {
#Before(value = "#annotation(com.fake.CustomAnnotation)")
public void doStuffBefore(JoinPoint joinPoint} {
}
}
I'm using spring with AspectJ enabled using: #EnableAspectJAutoProxy(proxyTargetClass = true)
My issue is that the aspect doStuffBefore method is not being called before the execution of FakeImpl's foo() method. It Does work when I put the #CustomAnnotation on FakeImpl instead of FakeTemplate, but I'd much prefer to put the annotation on FakeTemplate as it's in a separate API package and I've kind of delegated it as the place where I put all my annotations.
I'd also like to ensure that the CustomAnnotation is called on every class that implements FakeTemplate without remembering to put the annotation on all the implementation classes themselves.
Is there any way to get the advice to be called if the annotation is only on the interface class?
Annotation inheritance doesn't work for methods in java.
But you can use other pointcut expression, something like
execution(public * FakeTemplate+.foo(..))
There is sort of a workaround. In your example, you can add an extra 'default' method e.g. named fooFacade to your FakeTemplate interface, annotate that with your #CustomAnnotation, and then delegate to the 'real' foo method:
public interface FakeTemplate {
#CustomAnnotation
default void fooFacade() {
foo();
}
void foo();
}
Now whenever you call fooFacade(), execution of the pointcut on #CustomAnnotation will be triggered.