I'm trying to introduce a logger in my project using Spring AOP, but I'm new to AOP and to AspectJ syntax, so I'm having some troubles...
I've defined a basic aspect-class following some tutorial/docs:
#Aspect
public class Logger {
#Pointcut("execution(* exportdatamanager.export.ExportType.fetch(..))")
public void fetch() {
}
// ...
#AfterReturning("fetch()")
public void fetchingResult(JoinPoint joinPoint, Object result) {
System.out.println("TEST LOG " + result.toString());
}
}
But when I run my application I get this exception:
java.lang.IllegalArgumentException: error at ::0 formal unbound in pointcut
What I'm doing wrong, I suppose there's something wrong in my ApsectJ expression...
Can you also suggest me some quick reference to AspectJ syntax supported by Spring AOP?
NOTE
A snippet from my ExportType interface
public interface ExportType {
List<Object> fetch() throws FetchingStrategyException;
// ...
}
Ok, I just solved my issue this way:
#AfterReturning(pointcut = "fetch()", returning = "results")
public void fetchingResult(JoinPoint joinPoint, List<Object> results) {
System.out.println("TEST LOG " + results.toString());
}
Related
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();
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?
In my application, using #EnableBinding upon DemoController.class result in my Log Aspect fail,have log records no more.
And, if remove #EnableBinding, the log aspect work.
UspeController.class like this:
#RestController
#RequestMapping("/yeah/user")
#EnableBinding({OpenFileSystemOutput.class})
public class UspeController {
#Autowired
OpenFileSystemOutput openFileSystemOutput;
#PutMapping(value = "/applyAccount")
public Result<?> applyAccount() throws Exception {
UserMessage userMessage = new UserMessage(UserInfoContext.getUserId());
openFileSystemOutput.output().send(MessageBuilder.withPayload(userMessage).build());
return ResultUtils.success("");
}
}
LOG Aspect like this:
#Aspect
public class LogAspect {
#Pointcut("within(com.yeah..*) && #target(org.springframework.web.bind.annotation.RestController) ")
public void executionService() {
}
#Before(value = "executionService()")
public void doBefore(JoinPoint joinPoint) {
log.info("Api Interface: [{}], parameters: {}", request.getRequestURI(), JSON.toJSONString(getSerializableObject(joinPoint)));
}
}
Using EnableBinding is deprecated as of 3.1.x versions of Spring Cloud Stream. If you can, please upgrade your code to use the latest functional model. See the programming model section from the docs for more details.
I am trying to intercept all the returning List from my BaseRepostitary files. So that i can find the names inside that lists which has to be decrypted using this decrypt method. Following is my Aspect Class
#Aspect
#Service
public class DecryptionAspect {
#AfterReturning(value = "execution(java.util.List *(..)) "
+ "&& target(org.springframework.stereotype.Repository)) ", returning = "list")
public void decrypt(List list) throws Exception
{
//Do decryption here for the names inside the list
}
}
But the problem is this decrypt method is not triggering at the time of my Repository classes gets hit. So something is wrong in my expression. I know i can target the Repository class by the package name. But i have many Repository classes and i have given the #Repository annotation for that classes. So i want this AOP expression to identify which are all the classes have #Repository annotation present and intercept all the List items inside the Repository classes. So how to rewrite my expression. Thanks in advance!
Finally i got it!
#AfterReturning(value="execution(* *..*Repository.*(..))",returning="list")
public void decrypt(List list) throws Exception
{
//Do decryption here for the names inside the list
}
}
`#Aspect
#Service
public class DecryptionAspect {
#AfterReturning(value = "execution(java.util.List *(..)) "
+ "&& #target(org.springframework.stereotype.Repository)) ", returning = "list")
public void decrypt(List list) throws Exception
{
//Do decryption here for the names inside the list
}
}`
you should use #target to match the target class with a specific annotation.
Does the BaseRepostitary class implements some interface?
I Try this.Hope that works.
#Aspect
#Service
public class TestAop {
#AfterReturning(value = "execution( java.util.List *.queryList*(..)) "
+ " && #target(org.springframework.stereotype.Repository)", returning = "list")
public void decrypt(List list) throws Exception
{
System.out.println("test........."+ list.toString());
//Do decryption here for the names inside the list
}
}
ServiceImpl:
#Repository
public class ServiceImpl implements IService {
#Override
public <T> List<T> queryList(Map<String, Object> paramMap, Class<T> clazz) {
return null;
}
}
I am using slf4j with Spring AOP for logging and Exception purpose.there are some methods in some classes which formed a method chaining. I am able to log at first method's entry and exit point but when this method called another method then AOP is logging only first method's entry and exit point.I want to log every method's entry and and exit point using #Around annotation here is Pseudo code to explain what i want
package com.sample;
public class Test implements T{
#Override
public void show() {
System.out.println("Test.show()");
test();
}
void Test(){
//Want to log entry and exit point of this method whenever this method called by any other method
//The method may belongs to same class or different package's different class
}
spring.xml is something like this
<bean id="exceptionAspect" class="com.sample.ExceptionAspect"/>
<bean id="test" class="com.sample.Test"/>
My Advise class look like
#Aspect
public class LoggingAspect {
#Around(value="execution (* com.sample.*.*(..))||"+
"execution(* some other package.*.*(..))")
public void logAround(ProceedingJoinPoint joinPoint) throws Throwable {
final Logger logger = LoggerFactory.getLogger(joinPoint.getTarget().getClass());
logger.info("Execution of : " + joinPoint.getSignature() + " Started");
joinPoint.proceed();
logger.info("Execution of : " + joinPoint.getSignature() + " completed");
}
}
Client class
package com.test;
public class App {
public static void main(String[] args) throws Exception {
ApplicationContext appContext = new ClassPathXmlApplicationContext(
"classpath:/META-INF/spring.xml");
T test=(T) appContext.getBean("test");
test.show();
}
Any Help is greatly appreciated..
What you are trying to do is not possible with Spring AOP (at least not without some refactoring of the advised methods). The reason for that is that Spring AOP is proxy based, which means for each bean it creates a proxy class and injects it instead of your implementation. A proxy has all the methods of the bean with added aspect functionality. So When you call a method of a bean (actually the proxy of the bean), the aspect code is executed and your method is then called by delegation. So when your method calls other methods the call are performed using the real beans, not the proxies of those - if any are present - hence you don't get the output you are expecting.
You can think of a proxy it looks somehow like that:
class MyBeanProxy implements MyBean {
MyBeanImpl theBean;
public void foo() {
// aspect code
theBean.foo();
}
public void bar() {
// aspect code
theBean.bar();
}
}
Where your bean is something like
interface MyBean {
foo();
bar();
}
#Component("my_bean")
class MyBeanImpl implements MyBean {
public void foo() {
System.out.println("foo");
bar();
}
public void bar() {
System.out.println("bar");
}
}
In the example above, when you call foo() via the proxy then the aspect code is executed, and the delegation to MyBeanImpl#foo() happens, where bar() is being called. Now it becomes obvious that the aspect code for bar() will not be executed.
Now how can you make it work?
1 - Refactor your code in such a way that for methods you want to have the aspect code executed for them the calls happen on the proxy object not on the bean itself. For that you can get the actual proxy and use it to call your methods.
public void foo() {
System.out.println("foo");
MyBean myBeanProxy = (MyBean) AopContext.currentProxy();
myBeanProxy.bar();
}
Note that this method is more of a hack than a clean way to do the job. For example it is obvious that myBeanProxy has no clue of the state of your current object.
2 - Refactor you code in such a way that bar() is in another bean which you can retrieve using your appContext.
3- Use AspectJ: Aspect code is injected into the target classes themselves (The real thing!)
Here is small example using AspectJ
Aspect
package com.aj;
import java.util.Arrays;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
#Aspect
public class MyAspect {
#Around("execution( * com.app.services.*.* (..) )")
public Object callDurationAdvice(ProceedingJoinPoint pjp) throws Throwable {
Signature signature = pjp.getSignature();
Object[] args = pjp.getArgs();
String argList = Arrays.toString(args);
System.out.println(signature.getDeclaringTypeName() +
"." + signature.getName() + "(" + argList + ") started");
long s = System.nanoTime();
Object proceed = pjp.proceed(args);
long e = System.nanoTime();
System.out.println(signature.getDeclaringTypeName() +
"." + signature.getName() + "(" + argList + ") ended after " +
((double)(e-s)/1000000) + " ms");
return proceed;
}
}
One class in some package witch should be target for the aspect
package com.app.services;
public class ServicesVersionInfo {
public static String getVersion() {
return getVersionNumber() + " " + getVersionStage();
}
public static String getVersionNumber() {
return "1.0.0";
}
public static String getVersionStage() {
return "ALPHA";
}
}
The App
package com.app;
import com.app.services.ServicesVersionInfo;
public class App {
public static void main(String[] args) {
System.out.println("App services version: " +
ServicesVersionInfo.getVersion());
}
}
Ran, this should output something lie that
com.app.services.ServicesVersionInfo.getVersion([]) started
com.app.services.ServicesVersionInfo.getVersionNumber([]) started
com.app.services.ServicesVersionInfo.getVersionNumber([]) ended after 0.004862 ms
com.app.services.ServicesVersionInfo.getVersionStage([]) started
com.app.services.ServicesVersionInfo.getVersionStage([]) ended after 0.005673 ms
com.app.services.ServicesVersionInfo.getVersion([]) ended after 0.378877 ms
App services version: 1.0.0 ALPHA
Finally here are some similar questions and further readings:
Spring AOP not working for method call inside another method
Get AOP proxy from the object itself
Spring AOP top problem #1 - aspects are not applied