I know that there are a lot of similar questions in Stackoverflow but none of them helped me.
I have a controller like this:
com.mypkg.controller;
#RestController
public class MyController {
#RequestMapping(method = RequestMethod.POST,
......
public ResponseEntity<?> MyEndpoint(myParams) {
return this.myMethod(myParams, "myString");
}
public ResponseEntity<?> myMethod(myParams, String myString){
//do something
return myReponseEntity
}
}
I defined my aspect in this way:
com.mypkg.controller;
#Aspect
#Component
#Slf4j
public class MyAspect {
#Around("execution(* com.mypkg.controller.MyController.MyEndpoint(..)) && args(..,aParam)")
public ResponseEntity<?> endpointAround(ProceedingJoinPoint joinPoint, String aParam) throws Throwable {
// I am working fine
// do something
return
}
#Around("execution(* com.mypkg.controller.MyController.myMethod(..)) && args(..,myString)")
public ResponseEntity<?> myMethodAround(ProceedingJoinPoint joinPoint, String myString) throws Throwable {
// **** I AM NOT CALLED****
// do something
// return ...
}
}
I configured the AutoProxy
#Configuration
#EnableAspectJAutoProxy(proxyTargetClass=true)
public class AopConfig {}
The function endpointAround is called every time that I call MyEndpoint (throw the REST api).
The problem is the second #Around. it is not called. I need to call a method everytime MyEndpoint is exectued and another one eveytime that MyEndpoint call myMethod.
The problem is that your method myMethod is call from within your other method directly, and not as a someSpringBean.myMethod.
The way spring works is by wrapping any of your beans, and then on the 'wrapping' it can execute all the aspect or other spring related stuff. When you call one method from another one inside the same class, you don't go through the wrapping, thus the aspect related stuff can't happen
You just missed some code. Let us use below code snippet it will work.
Use this com.mypkg.controller.MyController.myMethod instead of
com.mypkg.controller.myMethod it will work
Controller
com.mypkg.controller;
#RestController
public class MyController {
#RequestMapping(method = RequestMethod.POST,
......
public ResponseEntity<?> MyEndpoint(myParams) {
return this.myMethod(myParams, "myString");
}
public ResponseEntity<?> myMethod(myParams, String myString){
//do something
return myReponseEntity
}
}
and in Aspect
com.mypkg.controller;
#Aspect
#Component
#Slf4j
public class MyAspect {
#Around("execution(* com.mypkg.controller.MyController.MyEndpoint(..)) && args(..,aParam)")
public ResponseEntity<?> endpointAround(ProceedingJoinPoint joinPoint, String aParam) throws Throwable {
// I am working fine
// do something
return
}
#Around("execution(* com.mypkg.controller.MyController.myMethod(..)) && args(..,myString)")
public ResponseEntity<?> myMethodAround(ProceedingJoinPoint joinPoint, String myString) throws Throwable {
// **** I AM NOT CALLED****
// do something
// return ...
}
}
You just missed the package path. Your method path should be like this...springbean.method
Related
I have a controller class which further calls service class method. An AOP #Before aspect is applied on the service class method.
package com.example;
#RestController
public class BookController {
#Autowired
BookService bookService;
#RequestMapping(value = "/getBookDetails", method = RequestMethod.GET,
#RequestBody BookRequest bookRequest))
public String processBookDetails() {
System.out.println("Inside controller class");
String details = bookService.getBookDetailsInfo(bookRequest,bookName);
}
}
Service class
package com.example;
#Service
public class BookServiceImpl implements BookService {
#Override
public String getBookDetailsInfo(BookRequest bookRequest,String bookName) {
//some code
// call getBookDEtails
getBookDetails(bookInfoObj)
returns "Book details";
}
#CallBookInfo-- custom annotation for aspect which needs to be executed before getBookDetails is called
getBookDetails(BookInfoObj obj){
}
An aspect is written to be executed #Before the method getBookDetails() of BookServiceImpl
//Custom annotation
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface CallBookInfo{}
//Aspect
#Aspect
#Configuration
public class BookAspect {
#Before("#annotation(com.example.CallBookInfo)")
public Object beforeBookAdvice(JoinPoint joinpoint) {
System.out.println("Start aspect");
Object result= null;
try {
BookRequest obj= joinpoint.getArgs();
System.out.println("End aspect");
}
catch(Exception e) {}
return result;
}
}
Execution goes as below,
Controller calls BookServiceImpl.getDetailsInfo() method.
getBookDetails() is called inside this method after some conditions
#Before Aspect is called before the getBookDetails() due to the custom
annotation #CallBookInfo
How to get the BookRequest object which was passed from the Controller class to the service class and after some processing return it back to the service class from the aspect
Thank you in advance !!
I have a controller class which further calls service class method. An AOP #Around aspect is applied on the service class method.
package com.hetal.example;
#RestController
public class CustomerController {
#Autowired
CustomerService customerService;
#RequestMapping(value = "/getDetails", method = RequestMethod.GET)
public String getCustomerDetails() {
System.out.println("Inside controller class");
String details = customerService.getDetails(custName);
System.out.println("Customer details is = " + details); // prints null
}
}
package com.hetal.example;
#Service
public class CustomerServiceImpl implements CustomerService {
#Override
public String getDetails(String custName) {
//some code
returns "Customer details";
}
}
An aspect is written to be executed #Around the method getDetails() of CustomerServiceImpl
package com.hetal.config;
public class JoinPointConfig {
#Pointcut(value="execution(* com.hetal.example.CustomerService.getDetails(..) && args(custName)"))
public void handleCustomerDetails(String custName) {}
}
package com.hetal.config;
#Aspect
#Component
public class CustomerAspect {
#Around("com.hetal.config.JoinPointConfig.handleCustomerDetails(custName)")
public Object aroundCustomerAdvice(ProceedingJoinPoint joinpoint, String custName) {
System.out.println("Start aspect");
Object result= null;
try {
result = joinpoint.proceed();
System.out.println("End aspect");
}
catch(Exception e) {}
return result;
}
}
Execution goes as below,
Controller calls CustomerServiceImpl.getDetails method.
CustomerAspect is called, prints "Start aspect". //before advice
joinpoint.proceed() calls actual CustomerServiceImpl.getDetails method.
CustomerServiceImpl.getDetails returns a string "Customer details" and control comes back to the aspect, prints "End aspect" //after returning advice
Control goes back to controller class but the response received is null.
I want the response returned from the service class into the controller class after the completion of the aspect.
Thank you in advance !!
Yeah you some compilation issue in your applications make those changes and with the belwo return type issue in Aspect class,
but the main issue is with your Aspect class, its void return type hence that coming as null you should return the result as object , below is the code
package com.hetal.config;
#Aspect
#Component
public class CustomerAspect {
#Around("com.hetal.config.JoinPointConfig.handleCustomerDetails(custName)")
public Object aroundCustomerAdvice(ProceedingJoinPoint joinpoint, String custName) {
System.out.println("Start aspect");
Object result= null;
try {
result = joinpoint.proceed();
System.out.println("End aspect");
}
catch(Exception e) {}
return result;
}
}
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.
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?
Trying to map the index controller correctly.
#Controller
#RequestMapping("/")
public class ClientIndexController
{
#RequestMapping(method=RequestMethod.GET)
public ModelAndView index()
{
}
}
or
#Controller
public class ClientIndexController
{
#RequestMapping("/")
public ModelAndView index(HttpServletRequest request)
{
}
}
These both approaches could not distinguish two different requests.
http://domain.com/
http://domain.com/?test=1 - in this case 404 must be thrown.
How can I avoid such behavior?
You can have Map with all request parameters, and check if the map is empty. Then you can implement a lot of different ways in creating a 404 (the one in the example below in only one way (maybe not the best)).
#Controller
#RequestMapping("/")
public class ClientIndexController {
#RequestMapping(method=RequestMethod.GET)
public ModelAndView index(#RequestParam Map<String,String> allRequestParams) {
if(allRequestParams != null && !allRequestParams.isEmpty() {
throw new ResouceNotFoundException();
}
}
#ExceptionHandler(ResouceNotFoundException.class)
#ResponseStatus(404)
public void RprocessValidationError(ResouceNotFoundException ex) {
}
}
If you only want to check that a special parameter is not there then you could use
#RequestMapping(method = RequestMethod.GET, params="!test")
public ModelAndView index(){...}